public ProjectedPropertiesTestCase(ProjectedPropertiesTestCase other)
 {
     this.DebugDescription            = other.DebugDescription;
     this.TopLevelProjectedProperties = other.TopLevelProjectedProperties;
     this.NestedProjectedProperties   = other.NestedProjectedProperties;
     this.ExpectedProperties          = other.ExpectedProperties;
     this.ResponseOnly      = other.ResponseOnly;
     this.NestedPayload     = other.NestedPayload;
     this.IgnoreFormats     = other.IgnoreFormats;
     this.ExpectedException = other.ExpectedException;
 }
        private WriterTestExpectedResults CreateExpectedResults(WriterTestConfiguration testConfiguration, ProjectedPropertiesTestCase testCase, bool withModel)
        {
            if (testCase.ExpectedException != null)
            {
                ExpectedException expectedException = testCase.ExpectedException(withModel);
                if (expectedException != null)
                {
                    return(new WriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                    {
                        ExpectedException2 = expectedException
                    });
                }
            }

            if (testConfiguration.Format == ODataFormat.Atom)
            {
                #region Atom expected result
                var atomExpectedResults = new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                {
                    Xml = new XElement("properties", testCase.ExpectedProperties.OrderBy(p => p).Select(p => new XElement(p))).ToString(),
                    FragmentExtractor = (result) =>
                    {
                        // Navigation links
                        IEnumerable <string> actualProperties;
                        if (result == null)
                        {
                            actualProperties = new string[0];
                        }
                        else
                        {
                            actualProperties = result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                                               .Where(link => link.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value.StartsWith(TestAtomConstants.ODataNavigationPropertiesRelatedLinkRelationPrefix))
                                               .Select(link => link.Attribute(TestAtomConstants.AtomLinkTitleAttributeName).Value);
                            // Named stream links
                            actualProperties = actualProperties.Concat(result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                                                                       .Where(link => link.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value.StartsWith(TestAtomConstants.ODataStreamPropertyEditMediaRelatedLinkRelationPrefix))
                                                                       .Select(link => link.Attribute(TestAtomConstants.AtomLinkTitleAttributeName).Value));
                            // Association links
                            actualProperties = actualProperties.Concat(result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                                                                       .Where(link => link.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value.StartsWith(TestAtomConstants.ODataNavigationPropertiesAssociationLinkRelationPrefix))
                                                                       .Select(link => link.Attribute(TestAtomConstants.AtomLinkTitleAttributeName).Value));
                            // Properties
                            actualProperties = actualProperties.Concat(result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomContentElementName)
                                                                       .Elements(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.AtomPropertiesElementName)
                                                                       .Elements().Where(e => e.Name.Namespace == TestAtomConstants.ODataXNamespace)
                                                                       .Select(pe => pe.Name.LocalName));
                        }

                        return(new XElement("properties",
                                            actualProperties.OrderBy(p => p).Select(p => new XElement(p))));
                    }
                };

                if (testCase.NestedPayload)
                {
                    var originalFragmentExtractor = atomExpectedResults.FragmentExtractor;
                    atomExpectedResults.FragmentExtractor = (result) =>
                    {
                        // Verify that the Wrapping_ID property is not written
                        this.Assert.IsNull(result
                                           .Element(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomContentElementName)
                                           .Element(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.AtomPropertiesElementName),
                                           "There should be no other property but the nav link and thus no m:properties in the content.");
                        XElement expandedNavLinkElement = result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                                                          .Where(link => link.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value
                                                                 .StartsWith(TestAtomConstants.ODataNavigationPropertiesRelatedLinkRelationPrefix + "Wrapping_ExpandedEntry"))
                                                          .SingleOrDefault();
                        return(originalFragmentExtractor(
                                   expandedNavLinkElement == null
                                ? null
                                : expandedNavLinkElement
                                   .Element(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.ODataInlineElementName)
                                   .Element(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomEntryElementName)));
                    };
                }

                return(atomExpectedResults);

                #endregion Atom expected result
            }
            else if (testConfiguration.Format == ODataFormat.Json)
            {
                #region JSON Light expected result
                JsonArray expectedJson = new JsonArray();
                foreach (var p in testCase.ExpectedProperties.Distinct().OrderBy(p => p))
                {
                    expectedJson.Add(new JsonPrimitiveValue(p));
                }

                var jsonExpectedResults = new JsonWriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                {
                    Json = expectedJson.ToText(/*writingJsonLight*/ true, testConfiguration.MessageWriterSettings.Indent),
                    FragmentExtractor = (result) =>
                    {
                        // Everything except association links
                        IEnumerable <string> actualProperties;
                        if (result == null)
                        {
                            actualProperties = new string[0];
                        }
                        else
                        {
                            List <string> propertyNames = new List <string>();

                            foreach (JsonProperty jsonProperty in result.Object().Properties)
                            {
                                string propertyName = jsonProperty.Name;
                                int    atIndex      = propertyName.IndexOf('@');
                                int    dotIndex     = propertyName.IndexOf('.');

                                if (dotIndex < 0)
                                {
                                    propertyNames.Add(propertyName);
                                }
                                else if (atIndex >= 0)
                                {
                                    propertyNames.Add(propertyName.Substring(0, atIndex));
                                }
                            }

                            actualProperties = propertyNames.Distinct();
                        }

                        JsonArray r = new JsonArray();
                        foreach (var p in actualProperties.OrderBy(p => p))
                        {
                            r.Add(new JsonPrimitiveValue(p));
                        }

                        return(r);
                    }
                };

                if (testCase.NestedPayload)
                {
                    var originalFragmentExtractor = jsonExpectedResults.FragmentExtractor;
                    jsonExpectedResults.FragmentExtractor = (result) =>
                    {
                        // Verify that the Wrapping_ID property is not written
                        JsonObject resultObject = result.Object();
                        this.Assert.IsNull(resultObject.Property("Wrapping_ID"), "No other property but the nav. link should be written.");
                        return(originalFragmentExtractor(
                                   resultObject.Property("Wrapping_ExpandedEntry") == null
                                ? null
                                : resultObject.PropertyObject("Wrapping_ExpandedEntry")));
                    };
                }

                return(jsonExpectedResults);

                #endregion JSON Light expected result
            }
            else
            {
                throw new TaupoInvalidOperationException("The format " + testConfiguration.Format.GetType().FullName + " is not supported.");
            }
        }
        public void ProjectedPropertiesTest()
        {
            // Start with top-level payloads
            IEnumerable <ProjectedPropertiesTestCase> topLevelTestCases = new ProjectedPropertiesTestCase[]
            {
                // No point testing case where no annotation is specified, as all the other tests do that.
                // It's important to sort these in the payload order, so nav props go first, then named streams and then normal properties
                // Also note that assoc link and nav link might have the same name, in which case they will be twice in the list below
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[0]),
                    ExpectedProperties          = new string[0],
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "StringProperty" }),
                    ExpectedProperties          = new string[] { "StringProperty" },
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "StringProperty", "NumberProperty" }),
                    ExpectedProperties          = new string[] { "StringProperty", "NumberProperty" },
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "SimpleComplexProperty", "PrimitiveCollection" }),
                    ExpectedProperties          = new string[] { "SimpleComplexProperty", "PrimitiveCollection" },
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "NamedStream" }),
                    ExpectedProperties          = new string[] { "NamedStream" },
                    ResponseOnly = true,
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "AssociationLink" }),
                    ExpectedProperties          = new string[] { "AssociationLink" },
                    ResponseOnly  = true,
                    IgnoreFormats = new ODataFormat[] { ODataFormat.Json },
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "NamedStream", "StringProperty", "NumberProperty", "SimpleComplexProperty", "DeepComplexProperty", "PrimitiveCollection", "ComplexCollection" }),
                    ExpectedProperties          = new string[] { "NamedStream", "StringProperty", "NumberProperty", "SimpleComplexProperty", "DeepComplexProperty", "PrimitiveCollection", "ComplexCollection" },
                    ResponseOnly = true,
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "DeferredNavigation" }),
                    ExpectedProperties          = new string[] { "DeferredNavigation", "DeferredNavigation" },
                    ResponseOnly = true,
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "ExpandedEntry" }),
                    ExpectedProperties          = new string[] { "ExpandedEntry" },
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "ExpandedFeed" }),
                    ExpectedProperties          = new string[] { "ExpandedFeed" },
                },
            };

            // Then create wrapped test cases
            var nestedTestCases = topLevelTestCases.Select(t =>
                                                           new ProjectedPropertiesTestCase(t)
            {
                NestedPayload               = true,
                NestedProjectedProperties   = t.TopLevelProjectedProperties,
                TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "Wrapping_ExpandedEntry" }),
            });

            // Create more interesting test cases with multiple annotations
            var manualTestCases = new ProjectedPropertiesTestCase[]
            {
                new ProjectedPropertiesTestCase {
                    DebugDescription            = "Non-existant name in top-level annotation is ignored.",
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "NumberProperty", "NamedStream", "NamedStream", "DeferredNavigation", "ExpandedEntry", "ExpandedFeed", "NonExistant" }),
                    ExpectedProperties          = new string[] { "DeferredNavigation", "ExpandedEntry", "ExpandedFeed", "NamedStream", "DeferredNavigation", "NumberProperty" },
                    ResponseOnly = true,
                },
                new ProjectedPropertiesTestCase {
                    DebugDescription            = "Make sure a top-level annotation with invalid property names or paths projects nothing.",
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { string.Empty, null, "Invalid", "SimpleComplexProperty/Name" }),
                    ExpectedProperties          = new string[] { },
                },
                new ProjectedPropertiesTestCase {
                    DebugDescription            = "Make sure a nested annotation works with a top-level annotation.",
                    NestedPayload               = true,
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "Wrapping_ExpandedEntry" }),
                    NestedProjectedProperties   = new ProjectedPropertiesAnnotation(new string[] { "StringProperty", "NumberProperty" }),
                    ExpectedProperties          = new string[] { "StringProperty", "NumberProperty" },
                },
                new ProjectedPropertiesTestCase {
                    DebugDescription            = "Make sure a nested annotation is ignored if a top-level annotation does not include the navigation property.",
                    NestedPayload               = true,
                    TopLevelProjectedProperties = new  ProjectedPropertiesAnnotation(new string[] { "Invalid" }),
                    NestedProjectedProperties   = new ProjectedPropertiesAnnotation(new string[] { "StringProperty", "NumberProperty" }),
                    ExpectedProperties          = new string[0],
                },
            };

            var testCases = topLevelTestCases.Concat(nestedTestCases).Concat(manualTestCases);

            IEdmModel edmModel = this.CreateEdmModel();

            // First create test descriptors that use the model
            var testDescriptors = this.CreateEdmTestDescriptors(testCases, edmModel);

            // Then append the test descriptors without model
            testDescriptors = testDescriptors.Concat(this.CreateEdmTestDescriptors(testCases, /*model*/ null));

            //ToDo: Fix places where we've lost JsonVerbose coverage to add JsonLight
            this.CombinatorialEngineProvider.RunCombinations(
                testDescriptors.PayloadCases(WriterPayloads.TopLevelValuePayload),
                this.WriterTestConfigurationProvider.ExplicitFormatConfigurations.Where(tc => tc.Format == ODataFormat.Atom),
                (testDescriptor, testConfiguration) =>
            {
                testConfiguration = testConfiguration.Clone();
                testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                TestWriterUtils.WriteAndVerifyODataEdmPayload(testDescriptor.DeferredLinksToEntityReferenceLinksInRequest(testConfiguration), testConfiguration, this.Assert, this.Logger);
            });
        }
        private IEnumerable <ODataItem> CreatePayload(ProjectedPropertiesTestCase testCase)
        {
            // First create the entry itself (it might get wrapped later)
            ODataEntry entry = new ODataEntry()
            {
                TypeName   = "TestModel.EntityType",
                Properties = new List <ODataProperty>()
                {
                    new ODataProperty {
                        Name = "StringProperty", Value = "foo"
                    },
                    new ODataProperty {
                        Name = "NumberProperty", Value = 42
                    },
                    new ODataProperty {
                        Name = "SimpleComplexProperty", Value = new ODataComplexValue
                        {
                            TypeName   = "TestModel.SimplexComplexType",
                            Properties = new ODataProperty[] {
                                new ODataProperty {
                                    Name = "Name", Value = "Bart"
                                }
                            }
                        }
                    },
                    new ODataProperty {
                        Name = "DeepComplexProperty", Value = new ODataComplexValue
                        {
                            TypeName   = "TestModel.NestedComplexType",
                            Properties = new ODataProperty[] {
                                new ODataProperty {
                                    Name = "InnerComplexProperty", Value = new ODataComplexValue
                                    {
                                        TypeName   = "TestModel.SimplexComplexType2",
                                        Properties = new ODataProperty[] {
                                            new ODataProperty {
                                                Name = "Value", Value = 43
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },

                    new ODataProperty {
                        Name = "PrimitiveCollection", Value = new ODataCollectionValue
                        {
                            TypeName = "Collection(Edm.String)",
                            Items    = new object[] { "Simpson" }
                        }
                    },
                    new ODataProperty {
                        Name = "ComplexCollection", Value = new ODataCollectionValue
                        {
                            TypeName = "Collection(TestModel.RatingComplexType)",
                            Items    = new object[] {
                                new ODataComplexValue
                                {
                                    TypeName   = "TestModel.RatingComplexType",
                                    Properties = new ODataProperty[] { new ODataProperty {
                                                                           Name = "Rating", Value = -3
                                                                       } }
                                }
                            }
                        }
                    }
                },
                SerializationInfo = MySerializationInfo
            };

            if (testCase.ResponseOnly)
            {
                // Add a stream property for responses
                ((List <ODataProperty>)entry.Properties).Add(new ODataProperty {
                    Name = "NamedStream", Value = new ODataStreamReferenceValue {
                        EditLink = new Uri("http://odata.org/namedstream")
                    }
                });
            }

            ODataItem[] entryItems = new ODataItem[]
            {
                entry,
                new ODataNavigationLink {
                    Name = "DeferredNavigation", IsCollection = false, Url = new Uri("http://odata.org/deferred"), AssociationLinkUrl = testCase.ResponseOnly ? new Uri("http://odata.org/associationlink2") : null
                },
                null, // End deferred link

                new ODataNavigationLink {
                    Name = "ExpandedEntry", IsCollection = false, Url = new Uri("http://odata.org/entry")
                },
                new ODataEntry()
                {
                    TypeName   = "TestModel.ExpandedEntryType",
                    Properties = new ODataProperty[] {
                        new ODataProperty {
                            Name = "ExpandedEntryName", Value = "bar"
                        }
                    },
                    SerializationInfo = MySerializationInfo
                },
                new ODataNavigationLink {
                    Name = "ExpandedEntry_DeferredNavigation", IsCollection = false, Url = new Uri("http://odata.org/deferred")
                },
                null,         // End deffered link
                new ODataNavigationLink {
                    Name = "ExpandedEntry_ExpandedFeed", IsCollection = true, Url = new Uri("http://odata.org/feed")
                },
                new ODataFeed {
                    Id = new Uri("http://test/feedid1"), SerializationInfo = MySerializationInfo
                },
                null, // End feed
                null, // End exanded expanded feed link
                null, // End expanded entry
                null, // End expanded entry nav link

                new ODataNavigationLink {
                    Name = "ExpandedFeed", IsCollection = true, Url = new Uri("http://odata.org/feed")
                },
                new ODataFeed {
                    Id = new Uri("http://test/feedid2")
                },
                new ODataEntry {
                    TypeName = "TestModel.EntityType"
                },
                null,         // End entry
                new ODataEntry {
                    TypeName = "TestModel.EntityType", SerializationInfo = MySerializationInfo
                },
                null, // End entry
                null, // End expanded feed
                null, // End expanded feed nav link

                null, // End the top-level entry
            };

            ProjectedPropertiesAnnotation projectedProperties = testCase.TopLevelProjectedProperties;

            if (!testCase.NestedPayload)
            {
                this.Assert.IsNull(testCase.NestedProjectedProperties, "For a non-nested payload, no nested annotation must be specified.");
                entry.SetAnnotation(projectedProperties);
                return(entryItems);
            }

            // If we are processing a test case for a nested payload, wrap the entry items into a wrapping entry with an expanded navigation link.
            ODataEntry wrappingEntry = new ODataEntry()
            {
                TypeName   = "TestModel.WrappingEntityType",
                Properties = new[] { new ODataProperty {
                                         Name = "Wrapping_ID", Value = 1
                                     } },
                SerializationInfo = MySerializationInfo
            };
            IEnumerable <ODataItem> wrappedItems =
                new ODataItem[] { wrappingEntry, new ODataNavigationLink {
                                      Name = "Wrapping_ExpandedEntry", IsCollection = false, Url = new Uri("http://odata.org/wrapping")
                                  } }
            .Concat(entryItems)
            .Concat(new ODataItem[] { null, null });

            ProjectedPropertiesAnnotation nestedProjectedProperties = testCase.NestedProjectedProperties;

            entry.SetAnnotation(nestedProjectedProperties);
            wrappingEntry.SetAnnotation(projectedProperties);

            return(wrappedItems);
        }
 public ProjectedPropertiesTestCase(ProjectedPropertiesTestCase other)
 {
     this.DebugDescription = other.DebugDescription;
     this.TopLevelProjectedProperties = other.TopLevelProjectedProperties;
     this.NestedProjectedProperties = other.NestedProjectedProperties;
     this.ExpectedProperties = other.ExpectedProperties;
     this.ResponseOnly = other.ResponseOnly;
     this.NestedPayload = other.NestedPayload;
     this.IgnoreFormats = other.IgnoreFormats;
     this.ExpectedException = other.ExpectedException;
 }
        public void ProjectedPropertiesTest()
        {
            // Start with top-level payloads
            IEnumerable<ProjectedPropertiesTestCase> topLevelTestCases = new ProjectedPropertiesTestCase[]
            {
                // No point testing case where no annotation is specified, as all the other tests do that.
                // It's important to sort these in the payload order, so nav props go first, then named streams and then normal properties
                // Also note that assoc link and nav link might have the same name, in which case they will be twice in the list below
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[0]),
                    ExpectedProperties = new string[0],
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "StringProperty" }),
                    ExpectedProperties = new string[] { "StringProperty" },
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "StringProperty", "NumberProperty" }),
                    ExpectedProperties = new string[] { "StringProperty", "NumberProperty" },
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "SimpleComplexProperty", "PrimitiveCollection" }),
                    ExpectedProperties = new string[] { "SimpleComplexProperty", "PrimitiveCollection" },
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "NamedStream" }),
                    ExpectedProperties = new string[] { "NamedStream" },
                    ResponseOnly = true,
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "AssociationLink" }),
                    ExpectedProperties = new string[] { "AssociationLink" },
                    ResponseOnly = true,
                    IgnoreFormats = new ODataFormat[] { ODataFormat.Json },
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "NamedStream", "StringProperty", "NumberProperty", "SimpleComplexProperty", "DeepComplexProperty", "PrimitiveCollection", "ComplexCollection" }),
                    ExpectedProperties = new string[] { "NamedStream", "StringProperty", "NumberProperty", "SimpleComplexProperty", "DeepComplexProperty", "PrimitiveCollection", "ComplexCollection" },
                    ResponseOnly = true,
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "DeferredNavigation" }),
                    ExpectedProperties = new string[] { "DeferredNavigation", "DeferredNavigation" },
                    ResponseOnly = true,
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "ExpandedEntry" }),
                    ExpectedProperties = new string[] { "ExpandedEntry" },
                },
                new ProjectedPropertiesTestCase {
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "ExpandedFeed" }),
                    ExpectedProperties = new string[] { "ExpandedFeed" },
                },
            };

            // Then create wrapped test cases 
            var nestedTestCases = topLevelTestCases.Select(t => 
                new ProjectedPropertiesTestCase(t)
                {
                    NestedPayload = true,
                    NestedProjectedProperties = t.TopLevelProjectedProperties,
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "Wrapping_ExpandedEntry" }),
                });

            // Create more interesting test cases with multiple annotations
            var manualTestCases = new ProjectedPropertiesTestCase[]
            {
                new ProjectedPropertiesTestCase {
                    DebugDescription = "Non-existant name in top-level annotation is ignored.",
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "NumberProperty", "NamedStream", "NamedStream", "DeferredNavigation", "ExpandedEntry", "ExpandedFeed", "NonExistant" }),
                    ExpectedProperties = new string[] { "DeferredNavigation", "ExpandedEntry", "ExpandedFeed", "NamedStream", "DeferredNavigation", "NumberProperty" },
                    ResponseOnly = true,
                },
                new ProjectedPropertiesTestCase {
                    DebugDescription = "Make sure a top-level annotation with invalid property names or paths projects nothing.",
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { string.Empty, null, "Invalid", "SimpleComplexProperty/Name" }),
                    ExpectedProperties = new string[] { },
                },
                new ProjectedPropertiesTestCase {
                    DebugDescription = "Make sure a nested annotation works with a top-level annotation.",
                    NestedPayload = true,
                    TopLevelProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "Wrapping_ExpandedEntry" }),
                    NestedProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "StringProperty", "NumberProperty" }),
                    ExpectedProperties = new string[] { "StringProperty", "NumberProperty" },
                },
                new ProjectedPropertiesTestCase {
                    DebugDescription = "Make sure a nested annotation is ignored if a top-level annotation does not include the navigation property.",
                    NestedPayload = true,
                    TopLevelProjectedProperties = new  ProjectedPropertiesAnnotation(new string[] { "Invalid" }),
                    NestedProjectedProperties = new ProjectedPropertiesAnnotation(new string[] { "StringProperty", "NumberProperty" }),
                    ExpectedProperties = new string[0],
                },
            };

            var testCases = topLevelTestCases.Concat(nestedTestCases).Concat(manualTestCases);

            IEdmModel edmModel = this.CreateEdmModel();

            // First create test descriptors that use the model
            var testDescriptors = this.CreateEdmTestDescriptors(testCases, edmModel);

            // Then append the test descriptors without model
            testDescriptors = testDescriptors.Concat(this.CreateEdmTestDescriptors(testCases, /*model*/null));

            //ToDo: Fix places where we've lost JsonVerbose coverage to add JsonLight
            this.CombinatorialEngineProvider.RunCombinations(
                testDescriptors.PayloadCases(WriterPayloads.TopLevelValuePayload),
                this.WriterTestConfigurationProvider.ExplicitFormatConfigurations.Where(tc => tc.Format == ODataFormat.Atom),
                (testDescriptor, testConfiguration) =>
                {
                    testConfiguration = testConfiguration.Clone();
                    testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                    TestWriterUtils.WriteAndVerifyODataEdmPayload(testDescriptor.DeferredLinksToEntityReferenceLinksInRequest(testConfiguration), testConfiguration, this.Assert, this.Logger);
                });
        }
        private WriterTestExpectedResults CreateExpectedResults(WriterTestConfiguration testConfiguration, ProjectedPropertiesTestCase testCase, bool withModel)
        {
            if (testCase.ExpectedException != null)
            {
                ExpectedException expectedException = testCase.ExpectedException(withModel);
                if (expectedException != null)
                {
                    return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                    {
                        ExpectedException2 = expectedException
                    };
                }
            }

            if (testConfiguration.Format == ODataFormat.Atom)
            {
                #region Atom expected result
                var atomExpectedResults = new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                {
                    Xml = new XElement("properties", testCase.ExpectedProperties.OrderBy(p => p).Select(p => new XElement(p))).ToString(),
                    FragmentExtractor = (result) =>
                    {
                        // Navigation links
                        IEnumerable<string> actualProperties;
                        if (result == null)
                        {
                            actualProperties = new string[0];
                        }
                        else
                        {
                            actualProperties = result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                            .Where(link => link.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value.StartsWith(TestAtomConstants.ODataNavigationPropertiesRelatedLinkRelationPrefix))
                            .Select(link => link.Attribute(TestAtomConstants.AtomLinkTitleAttributeName).Value);
                            // Named stream links
                            actualProperties = actualProperties.Concat(result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                                .Where(link => link.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value.StartsWith(TestAtomConstants.ODataStreamPropertyEditMediaRelatedLinkRelationPrefix))
                                .Select(link => link.Attribute(TestAtomConstants.AtomLinkTitleAttributeName).Value));
                            // Association links
                            actualProperties = actualProperties.Concat(result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                                .Where(link => link.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value.StartsWith(TestAtomConstants.ODataNavigationPropertiesAssociationLinkRelationPrefix))
                                .Select(link => link.Attribute(TestAtomConstants.AtomLinkTitleAttributeName).Value));
                            // Properties
                            actualProperties = actualProperties.Concat(result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomContentElementName)
                                .Elements(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.AtomPropertiesElementName)
                                .Elements().Where(e => e.Name.Namespace == TestAtomConstants.ODataXNamespace)
                                .Select(pe => pe.Name.LocalName));
                        }

                        return new XElement("properties",
                            actualProperties.OrderBy(p => p).Select(p => new XElement(p)));
                    }
                };

                if (testCase.NestedPayload)
                {
                    var originalFragmentExtractor = atomExpectedResults.FragmentExtractor;
                    atomExpectedResults.FragmentExtractor = (result) =>
                    {
                        // Verify that the Wrapping_ID property is not written
                        this.Assert.IsNull(result
                            .Element(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomContentElementName)
                            .Element(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.AtomPropertiesElementName),
                            "There should be no other property but the nav link and thus no m:properties in the content.");
                        XElement expandedNavLinkElement = result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                                .Where(link => link.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value
                                    .StartsWith(TestAtomConstants.ODataNavigationPropertiesRelatedLinkRelationPrefix + "Wrapping_ExpandedEntry"))
                                .SingleOrDefault();
                        return originalFragmentExtractor(
                                expandedNavLinkElement == null
                                ? null
                                : expandedNavLinkElement
                                    .Element(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.ODataInlineElementName)
                                    .Element(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomEntryElementName));
                    };
                }

                return atomExpectedResults;
                #endregion Atom expected result
            }
            else if (testConfiguration.Format == ODataFormat.Json)
            {
                #region JSON Light expected result
                JsonArray expectedJson = new JsonArray();
                foreach (var p in testCase.ExpectedProperties.Distinct().OrderBy(p => p))
                {
                    expectedJson.Add(new JsonPrimitiveValue(p));
                }

                var jsonExpectedResults = new JsonWriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                {
                    Json = expectedJson.ToText(/*writingJsonLight*/ true, testConfiguration.MessageWriterSettings.Indent),
                    FragmentExtractor = (result) =>
                    {
                        // Everything except association links
                        IEnumerable<string> actualProperties;
                        if (result == null)
                        {
                            actualProperties = new string[0];
                        }
                        else
                        {
                            List<string> propertyNames = new List<string>();

                            foreach (JsonProperty jsonProperty in result.Object().Properties)
                            {
                                string propertyName = jsonProperty.Name;
                                int atIndex = propertyName.IndexOf('@');
                                int dotIndex = propertyName.IndexOf('.');

                                if (dotIndex < 0)
                                {
                                    propertyNames.Add(propertyName);
                                }
                                else if (atIndex >= 0)
                                {
                                    propertyNames.Add(propertyName.Substring(0, atIndex));
                                }
                            }

                            actualProperties = propertyNames.Distinct();
                        }

                        JsonArray r = new JsonArray();
                        foreach (var p in actualProperties.OrderBy(p => p))
                        {
                            r.Add(new JsonPrimitiveValue(p));
                        }

                        return r;
                    }
                };

                if (testCase.NestedPayload)
                {
                    var originalFragmentExtractor = jsonExpectedResults.FragmentExtractor;
                    jsonExpectedResults.FragmentExtractor = (result) =>
                    {
                        // Verify that the Wrapping_ID property is not written
                        JsonObject resultObject = result.Object();
                        this.Assert.IsNull(resultObject.Property("Wrapping_ID"), "No other property but the nav. link should be written.");
                        return originalFragmentExtractor(
                            resultObject.Property("Wrapping_ExpandedEntry") == null
                                ? null
                                : resultObject.PropertyObject("Wrapping_ExpandedEntry"));
                    };
                }

                return jsonExpectedResults;
                #endregion JSON Light expected result
            }
            else
            {
                throw new TaupoInvalidOperationException("The format " + testConfiguration.Format.GetType().FullName + " is not supported.");
            }
        }
        private IEnumerable<ODataItem> CreatePayload(ProjectedPropertiesTestCase testCase)
        {
            // First create the entry itself (it might get wrapped later)
            ODataEntry entry = new ODataEntry()
            {
                TypeName = "TestModel.EntityType",
                Properties = new List<ODataProperty>() 
                    {
                        new ODataProperty { Name = "StringProperty", Value = "foo" },
                        new ODataProperty { Name = "NumberProperty", Value = 42 },
                        new ODataProperty { Name = "SimpleComplexProperty", Value = new ODataComplexValue 
                        { 
                            TypeName = "TestModel.SimplexComplexType", 
                            Properties = new ODataProperty[] {
                                new ODataProperty { Name = "Name", Value = "Bart" }
                        } } },
                        new ODataProperty { Name = "DeepComplexProperty", Value = new ODataComplexValue 
                        { 
                            TypeName = "TestModel.NestedComplexType",
                            Properties = new ODataProperty[] {
                                new ODataProperty { Name = "InnerComplexProperty", Value = new ODataComplexValue 
                                { 
                                    TypeName = "TestModel.SimplexComplexType2",
                                    Properties = new ODataProperty[] {
                                        new ODataProperty { Name = "Value", Value = 43 }
                                } } }
                        } } },
                        
                        new ODataProperty { Name = "PrimitiveCollection", Value = new ODataCollectionValue 
                        { 
                            TypeName = "Collection(Edm.String)",
                            Items = new object[] { "Simpson" }
                        } },
                        new ODataProperty { Name = "ComplexCollection", Value = new ODataCollectionValue 
                        { 
                            TypeName = "Collection(TestModel.RatingComplexType)",
                            Items = new object[] {
                                new ODataComplexValue 
                                { 
                                    TypeName = "TestModel.RatingComplexType",
                                    Properties = new ODataProperty[] { new ODataProperty { Name = "Rating", Value = -3 } } 
                                }
                        } } }
                    },
                SerializationInfo = MySerializationInfo
            };

            if (testCase.ResponseOnly)
            {
                // Add a stream property for responses
                ((List<ODataProperty>)entry.Properties).Add(new ODataProperty { Name = "NamedStream", Value = new ODataStreamReferenceValue { EditLink = new Uri("http://odata.org/namedstream") } });
            }

            ODataItem[] entryItems = new ODataItem[]
            {
                entry,
                new ODataNavigationLink { Name = "DeferredNavigation", IsCollection = false, Url = new Uri("http://odata.org/deferred"), AssociationLinkUrl = testCase.ResponseOnly ? new Uri("http://odata.org/associationlink2") : null },
                null, // End deferred link

                new ODataNavigationLink { Name = "ExpandedEntry", IsCollection = false, Url = new Uri("http://odata.org/entry") },
                    new ODataEntry() 
                    {
                        TypeName = "TestModel.ExpandedEntryType",
                        Properties = new ODataProperty[] {
                            new ODataProperty { Name = "ExpandedEntryName", Value = "bar" }
                        },
                        SerializationInfo = MySerializationInfo
                    },
                        new ODataNavigationLink { Name = "ExpandedEntry_DeferredNavigation", IsCollection = false, Url = new Uri("http://odata.org/deferred") },
                        null, // End deffered link
                        new ODataNavigationLink { Name = "ExpandedEntry_ExpandedFeed", IsCollection = true, Url = new Uri("http://odata.org/feed") },
                            new ODataFeed { Id = new Uri("http://test/feedid1"), SerializationInfo = MySerializationInfo },
                            null, // End feed
                        null, // End exanded expanded feed link
                    null, // End expanded entry
                null, // End expanded entry nav link

                new ODataNavigationLink { Name = "ExpandedFeed", IsCollection = true, Url = new Uri("http://odata.org/feed") },
                    new ODataFeed { Id = new Uri("http://test/feedid2") },
                        new ODataEntry { TypeName = "TestModel.EntityType" },
                        null, // End entry
                        new ODataEntry { TypeName = "TestModel.EntityType", SerializationInfo = MySerializationInfo },
                        null, // End entry
                    null, // End expanded feed
                null, // End expanded feed nav link

                null, // End the top-level entry
            };

            ProjectedPropertiesAnnotation projectedProperties = testCase.TopLevelProjectedProperties;

            if (!testCase.NestedPayload)
            {
                this.Assert.IsNull(testCase.NestedProjectedProperties, "For a non-nested payload, no nested annotation must be specified.");
                entry.SetAnnotation(projectedProperties);
                return entryItems;
            }

            // If we are processing a test case for a nested payload, wrap the entry items into a wrapping entry with an expanded navigation link.
            ODataEntry wrappingEntry = new ODataEntry() 
            {
                TypeName = "TestModel.WrappingEntityType",
                Properties = new[] { new ODataProperty { Name = "Wrapping_ID", Value = 1 } },
                SerializationInfo = MySerializationInfo
            };
            IEnumerable<ODataItem> wrappedItems = 
                new ODataItem[] { wrappingEntry, new ODataNavigationLink { Name = "Wrapping_ExpandedEntry", IsCollection = false, Url = new Uri("http://odata.org/wrapping") }}
                .Concat(entryItems)
                .Concat(new ODataItem[] { null, null });

            ProjectedPropertiesAnnotation nestedProjectedProperties = testCase.NestedProjectedProperties;
            entry.SetAnnotation(nestedProjectedProperties);
            wrappingEntry.SetAnnotation(projectedProperties);
            
            return wrappedItems;
        }