From 1118315a9dac7e86d8b22ea6e192dd1c920df86a Mon Sep 17 00:00:00 2001 From: Anthony Sneed Date: Mon, 26 Jan 2015 18:23:00 +0100 Subject: [PATCH] [Resolves #1887] XmlDataContractSerializer Input and Output Formatter Tests Cleanup. --- Mvc.NoFun.sln | 15 + Mvc.sln | 15 + .../Properties/AssemblyInfo.cs | 1 + .../FlushReportingStream.cs | 21 + .../Microsoft.AspNet.Mvc.Xml.Test.kproj | 23 + ...ataContractSerializerInputFormatterTest.cs | 38 +- ...taContractSerializerOutputFormatterTest.cs | 556 ++++++++++++++++++ .../XmlSerializerInputFormatterTest.cs} | 28 +- .../XmlSerializerOutputFormatterTest.cs} | 4 +- .../project.json | 18 + ...taContractSerializerInputFormatterTests.cs | 499 ++++++++++++++++ ...aContractSerializerOutputFormatterTests.cs | 38 +- 12 files changed, 1201 insertions(+), 55 deletions(-) create mode 100644 test/Microsoft.AspNet.Mvc.Xml.Test/FlushReportingStream.cs create mode 100644 test/Microsoft.AspNet.Mvc.Xml.Test/Microsoft.AspNet.Mvc.Xml.Test.kproj rename test/{Microsoft.AspNet.Mvc.Core.Test/Formatters => Microsoft.AspNet.Mvc.Xml.Test}/XmlDataContractSerializerInputFormatterTest.cs (93%) create mode 100644 test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerOutputFormatterTest.cs rename test/{Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerInputFormatterTests.cs => Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs} (94%) rename test/{Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerOutputFormatterTests.cs => Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerOutputFormatterTest.cs} (99%) create mode 100644 test/Microsoft.AspNet.Mvc.Xml.Test/project.json create mode 100644 test/XmlDataContractSerializerInputFormatterTests.cs rename test/{Microsoft.AspNet.Mvc.Core.Test/Formatters => }/XmlDataContractSerializerOutputFormatterTests.cs (95%) diff --git a/Mvc.NoFun.sln b/Mvc.NoFun.sln index fd070b14b0..1aac1cf088 100644 --- a/Mvc.NoFun.sln +++ b/Mvc.NoFun.sln @@ -52,6 +52,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.TagHel EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Xml", "src\Microsoft.AspNet.Mvc.Xml\Microsoft.AspNet.Mvc.Xml.kproj", "{9C632DF0-DC06-410B-95AE-B5423702E84F}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Xml.Test", "test\Microsoft.AspNet.Mvc.Xml.Test\Microsoft.AspNet.Mvc.Xml.Test.kproj", "{22019146-BDFA-442E-8C8E-345FB9644578}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -262,6 +264,18 @@ Global {9C632DF0-DC06-410B-95AE-B5423702E84F}.Release|Mixed Platforms.Build.0 = Release|Any CPU {9C632DF0-DC06-410B-95AE-B5423702E84F}.Release|x86.ActiveCfg = Release|Any CPU {9C632DF0-DC06-410B-95AE-B5423702E84F}.Release|x86.Build.0 = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|x86.ActiveCfg = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|x86.Build.0 = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|Any CPU.Build.0 = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|x86.ActiveCfg = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -286,5 +300,6 @@ Global {2223120F-D675-40DA-8CD8-11DC14A0B2C7} = {DAAE4C74-D06F-4874-A166-33305D2643CE} {860119ED-3DB1-424D-8D0A-30132A8A7D96} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} {9C632DF0-DC06-410B-95AE-B5423702E84F} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E} + {22019146-BDFA-442E-8C8E-345FB9644578} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} EndGlobalSection EndGlobal diff --git a/Mvc.sln b/Mvc.sln index 3db8c17b26..b2d7a62568 100644 --- a/Mvc.sln +++ b/Mvc.sln @@ -128,6 +128,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "XmlFormattersWebSite", "tes EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ValidationWebSite", "test\WebSites\ValidationWebSite\ValidationWebSite.kproj", "{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Xml.Test", "test\Microsoft.AspNet.Mvc.Xml.Test\Microsoft.AspNet.Mvc.Xml.Test.kproj", "{22019146-BDFA-442E-8C8E-345FB9644578}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -726,6 +728,18 @@ Global {C3123A70-41C4-4122-AD1C-D35DF8958DD7}.Release|Mixed Platforms.Build.0 = Release|Any CPU {C3123A70-41C4-4122-AD1C-D35DF8958DD7}.Release|x86.ActiveCfg = Release|Any CPU {C3123A70-41C4-4122-AD1C-D35DF8958DD7}.Release|x86.Build.0 = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|x86.ActiveCfg = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Debug|x86.Build.0 = Debug|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|Any CPU.Build.0 = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|x86.ActiveCfg = Release|Any CPU + {22019146-BDFA-442E-8C8E-345FB9644578}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -788,5 +802,6 @@ Global {0449D6D2-BE1B-4E29-8E1B-444420802C03} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} {C3123A70-41C4-4122-AD1C-D35DF8958DD7} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} {87AB84B2-22C1-43C6-BB8A-1D327B446FB0} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} + {22019146-BDFA-442E-8C8E-345FB9644578} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.Mvc.Core/Properties/AssemblyInfo.cs b/src/Microsoft.AspNet.Mvc.Core/Properties/AssemblyInfo.cs index 5d995b03fd..0eb5f89547 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Properties/AssemblyInfo.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Properties/AssemblyInfo.cs @@ -6,4 +6,5 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.AspNet.Mvc.Core.Test")] [assembly: InternalsVisibleTo("Microsoft.AspNet.Mvc.Razor.Test")] [assembly: InternalsVisibleTo("Microsoft.AspNet.Mvc.TagHelpers.Test")] +[assembly: InternalsVisibleTo("Microsoft.AspNet.Mvc.Xml.Test")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/FlushReportingStream.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/FlushReportingStream.cs new file mode 100644 index 0000000000..5bb32e1933 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Xml.Test/FlushReportingStream.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Threading; +using Moq; + +namespace Microsoft.AspNet.Mvc +{ + public static class FlushReportingStream + { + public static Stream GetThrowingStream() + { + var mock = new Mock(); + mock.Verify(m => m.Flush(), Times.Never()); + mock.Verify(m => m.FlushAsync(It.IsAny()), Times.Never()); + + return mock.Object; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/Microsoft.AspNet.Mvc.Xml.Test.kproj b/test/Microsoft.AspNet.Mvc.Xml.Test/Microsoft.AspNet.Mvc.Xml.Test.kproj new file mode 100644 index 0000000000..5e40fb5442 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Xml.Test/Microsoft.AspNet.Mvc.Xml.Test.kproj @@ -0,0 +1,23 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 22019146-bdfa-442e-8c8e-345fb9644578 + Microsoft.AspNet.Mvc.Xml.Test + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs similarity index 93% rename from test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerInputFormatterTest.cs rename to test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs index 95dd8baf21..52b2d20ef7 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerInputFormatterTest.cs +++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if ASPNET50 using System; using System.IO; using System.Linq; @@ -81,7 +80,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public void XmlDataContractSerializerFormatterHasProperSuppportedMediaTypes() + public void HasProperSuppportedMediaTypes() { // Arrange & Act var formatter = new XmlDataContractSerializerInputFormatter(); @@ -96,7 +95,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public void XmlDataContractSerializerFormatterHasProperSuppportedEncodings() + public void HasProperSuppportedEncodings() { // Arrange & Act var formatter = new XmlDataContractSerializerInputFormatter(); @@ -107,7 +106,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterReadsSimpleTypes() + public async Task ReadAsync_ReadsSimpleTypes() { // Arrange var expectedInt = 10; @@ -134,7 +133,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterReadsComplexTypes() + public async Task ReadAsync_ReadsComplexTypes() { // Arrange var expectedInt = 10; @@ -164,7 +163,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterReadsWhenMaxDepthIsModified() + public async Task ReadAsync_ReadsWhenMaxDepthIsModified() { // Arrange var expectedInt = 10; @@ -188,7 +187,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterThrowsOnExceededMaxDepth() + public async Task ReadAsync_ThrowsOnExceededMaxDepth() { if (TestPlatformHelper.IsMono) { @@ -211,7 +210,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterThrowsWhenReaderQuotasAreChanged() + public async Task ReadAsync_ThrowsWhenReaderQuotasAreChanged() { if (TestPlatformHelper.IsMono) { @@ -234,7 +233,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public void XmlDataContractSerializerFormatterThrowsWhenMaxDepthIsBelowOne() + public void SetMaxDepth_ThrowsWhenMaxDepthIsBelowOne() { // Arrange var formatter = new XmlDataContractSerializerInputFormatter(); @@ -244,7 +243,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task VerifyStreamIsOpenAfterRead() + public async Task ReadAsync_VerifyStreamIsOpenAfterRead() { // Arrange var input = "" + @@ -262,7 +261,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterThrowsOnInvalidCharacters() + public async Task ReadAsync_ThrowsOnInvalidCharacters() { // Arrange var expectedException = TestPlatformHelper.IsMono ? typeof(SerializationException) : @@ -289,7 +288,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterIgnoresBOMCharacters() + public async Task ReadAsync_IgnoresBOMCharacters() { // Arrange var sampleString = "Test"; @@ -320,7 +319,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerAcceptsUTF16Characters() + public async Task ReadAsync_AcceptsUTF16Characters() { // Arrange var expectedInt = 10; @@ -347,7 +346,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task ReadsSerializableErrorXml() + public async Task ReadAsync_ReadsSerializableErrorXml() { // Arrange var serializableErrorXml = "" + @@ -367,7 +366,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterThrowsWhenNotConfiguredWithRootName() + public async Task ReadAsync_ThrowsWhenNotConfiguredWithRootName() { // Arrange var SubstituteRootName = "SomeOtherClass"; @@ -386,7 +385,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterReadsWhenConfiguredWithRootName() + public async Task ReadAsync_ReadsWhenConfiguredWithRootName() { // Arrange var expectedInt = 10; @@ -422,7 +421,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterThrowsWhenNotConfiguredWithKnownTypes() + public async Task ReadAsync_ThrowsWhenNotConfiguredWithKnownTypes() { // Arrange var KnownTypeName = "SomeDummyClass"; @@ -442,7 +441,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlDataContractSerializerFormatterReadsWhenConfiguredWithKnownTypes() + public async Task ReadAsync_ReadsWhenConfiguredWithKnownTypes() { // Arrange var expectedInt = 10; @@ -507,5 +506,4 @@ namespace Microsoft.AspNet.Mvc return httpContext.Object; } } -} -#endif +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerOutputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerOutputFormatterTest.cs new file mode 100644 index 0000000000..9a526b6ea6 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerOutputFormatterTest.cs @@ -0,0 +1,556 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Core.Collections; +using Microsoft.AspNet.Mvc.Xml; +using Microsoft.Net.Http.Headers; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc +{ + public class XmlDataContractSerializerOutputFormatterTest + { + [DataContract(Name = "DummyClass", Namespace = "")] + public class DummyClass + { + [DataMember] + public int SampleInt { get; set; } + } + + [DataContract(Name = "SomeDummyClass", Namespace = "")] + public class SomeDummyClass : DummyClass + { + [DataMember] + public string SampleString { get; set; } + } + + [DataContract(Name = "TestLevelOne", Namespace = "")] + public class TestLevelOne + { + [DataMember] + public int SampleInt { get; set; } + [DataMember] + public string sampleString; + } + + [DataContract(Name = "TestLevelTwo", Namespace = "")] + public class TestLevelTwo + { + [DataMember] + public string SampleString { get; set; } + [DataMember] + public TestLevelOne TestOne { get; set; } + } + + [DataContract(Name = "Child", Namespace = "")] + public class Child + { + [DataMember] + public int Id { get; set; } + [DataMember] + public Parent Parent { get; set; } + } + + [DataContract(Name = "Parent", Namespace = "")] + public class Parent + { + [DataMember] + public string Name { get; set; } + [DataMember] + public List Children { get; set; } + } + + public static IEnumerable BasicTypeValues + { + get + { + yield return new object[] { "sampleString", + "sampleString" }; + yield return new object[] { 5, + "5" }; + yield return new object[] { 5.43, + "5.43" }; + yield return new object[] { 'a', + "97" }; + yield return new object[] { new DummyClass { SampleInt = 10 }, + "" + + "10" }; + yield return new object[] { new Dictionary() { { "Hello", "World" } }, + "" + + "HelloWorld" }; + } + } + + [Theory] + [MemberData(nameof(BasicTypeValues))] + public async Task WriteAsync_CanWriteBasicTypes(object input, string expectedOutput) + { + // Arrange + var formatter = new XmlDataContractSerializerOutputFormatter(); + var outputFormatterContext = GetOutputFormatterContext(input, typeof(object)); + + // Act + await formatter.WriteAsync(outputFormatterContext); + + // Assert + Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); + outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0; + Assert.Equal(expectedOutput, + new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8) + .ReadToEnd()); + Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead); + } + + [Fact] + public void DefaultConstructor_ExpectedWriterSettings_Created() + { + // Arrange and Act + var formatter = new XmlDataContractSerializerOutputFormatter(); + + // Assert + var writerSettings = formatter.WriterSettings; + Assert.NotNull(writerSettings); + Assert.True(writerSettings.OmitXmlDeclaration); + Assert.False(writerSettings.CloseOutput); + Assert.False(writerSettings.CheckCharacters); + } + + [Fact] + public async Task SuppliedWriterSettings_TakeAffect() + { + // Arrange + var writerSettings = FormattingUtilities.GetDefaultXmlWriterSettings(); + writerSettings.OmitXmlDeclaration = false; + var sampleInput = new DummyClass { SampleInt = 10 }; + var formatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); + var formatter = new XmlDataContractSerializerOutputFormatter(writerSettings); + var expectedOutput = ""+ + "" + + "10"; + + // Act + await formatter.WriteAsync(formatterContext); + + // Assert + Assert.Same(writerSettings, formatter.WriterSettings); + var responseStream = formatterContext.ActionContext.HttpContext.Response.Body; + Assert.NotNull(responseStream); + responseStream.Position = 0; + var actualOutput = new StreamReader(responseStream, Encoding.UTF8).ReadToEnd(); + Assert.Equal(expectedOutput, actualOutput); + } + + [Fact] + public async Task WriteAsync_WritesSimpleTypes() + { + // Arrange + var sampleInput = new DummyClass { SampleInt = 10 }; + var formatter = new XmlDataContractSerializerOutputFormatter(); + var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); + + // Act + await formatter.WriteAsync(outputFormatterContext); + + // Assert + Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); + outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0; + Assert.Equal("" + + "10", + new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8) + .ReadToEnd()); + } + + [Fact] + public async Task WriteAsync_WritesComplexTypes() + { + // Arrange + var sampleInput = new TestLevelTwo + { + SampleString = "TestString", + TestOne = new TestLevelOne + { + SampleInt = 10, + sampleString = "TestLevelOne string" + } + }; + var formatter = new XmlDataContractSerializerOutputFormatter(); + var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); + + // Act + await formatter.WriteAsync(outputFormatterContext); + + // Assert + Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); + outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0; + Assert.Equal("" + + "TestString" + + "10TestLevelOne string" + + "", + new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8) + .ReadToEnd()); + } + + [Fact] + public async Task WriteAsync_WritesOnModifiedWriterSettings() + { + // Arrange + var sampleInput = new DummyClass { SampleInt = 10 }; + var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); + var formatter = new XmlDataContractSerializerOutputFormatter( + new System.Xml.XmlWriterSettings + { + OmitXmlDeclaration = false, + CloseOutput = false + }); + + // Act + await formatter.WriteAsync(outputFormatterContext); + + // Assert + Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); + outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0; + Assert.Equal("" + + "" + + "10", + new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body, + Encoding.UTF8).ReadToEnd()); + } + + [Fact] + public async Task WriteAsync_WritesUTF16Output() + { + // Arrange + var sampleInput = new DummyClass { SampleInt = 10 }; + var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType(), + "application/xml; charset=utf-16"); + var formatter = new XmlDataContractSerializerOutputFormatter(); + formatter.WriterSettings.OmitXmlDeclaration = false; + + // Act + await formatter.WriteAsync(outputFormatterContext); + + // Assert + Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); + outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0; + Assert.Equal("" + + "" + + "10", + new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body, + Encodings.UTF16EncodingLittleEndian).ReadToEnd()); + } + + [Fact] + public async Task WriteAsync_WritesIndentedOutput() + { + // Arrange + var sampleInput = new DummyClass { SampleInt = 10 }; + var formatter = new XmlDataContractSerializerOutputFormatter(); + formatter.WriterSettings.Indent = true; + var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); + + // Act + await formatter.WriteAsync(outputFormatterContext); + + // Assert + Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); + outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0; + var outputString = new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body, + Encoding.UTF8).ReadToEnd(); + Assert.Equal("" + + "\r\n 10\r\n", + outputString); + } + + [Fact] + public async Task WriteAsync_VerifyBodyIsNotClosedAfterOutputIsWritten() + { + // Arrange + var sampleInput = new DummyClass { SampleInt = 10 }; + var formatter = new XmlDataContractSerializerOutputFormatter(); + var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); + + // Act + await formatter.WriteAsync(outputFormatterContext); + + // Assert + Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); + Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead); + } + + [Fact] + public async Task WriteAsync_DoesntFlushOutputStream() + { + // Arrange + var sampleInput = new DummyClass { SampleInt = 10 }; + var formatter = new XmlDataContractSerializerOutputFormatter(); + var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); + + var response = outputFormatterContext.ActionContext.HttpContext.Response; + response.Body = FlushReportingStream.GetThrowingStream(); + + // Act & Assert + await formatter.WriteAsync(outputFormatterContext); + } + + public static IEnumerable TypesForCanWriteResult + { + get + { + yield return new object[] { null, typeof(string), true }; + yield return new object[] { null, null, false }; + yield return new object[] { new DummyClass { SampleInt = 5 }, null, true }; + yield return new object[] { new DummyClass { SampleInt = 5 }, typeof(object), true }; + yield return new object[] { null, typeof(object), true }; + yield return new object[] { + new Dictionary { { "Hello", "world" } }, typeof(object), true }; + yield return new object[] { + new Dictionary { { "Hello", "world" } }, typeof(Dictionary), true }; + } + } + + [Theory] + [MemberData(nameof(TypesForCanWriteResult))] + public void CanWriteResult_ReturnsExpectedOutput(object input, Type declaredType, bool expectedOutput) + { + // Arrange + var formatter = new XmlDataContractSerializerOutputFormatter(); + var outputFormatterContext = GetOutputFormatterContext(input, declaredType); + + // Act + var result = formatter.CanWriteResult(outputFormatterContext, MediaTypeHeaderValue.Parse("application/xml")); + + // Assert + Assert.Equal(expectedOutput, result); + } + + public static IEnumerable TypesForGetSupportedContentTypes + { + get + { + yield return new object[] { typeof(DummyClass), typeof(DummyClass), "application/xml" }; + yield return new object[] { typeof(DummyClass), typeof(object), "application/xml" }; + yield return new object[] { null, typeof(DummyClass), "application/xml" }; + yield return new object[] { typeof(DummyClass), null, "application/xml" }; + yield return new object[] { typeof(object), null, "application/xml" }; + yield return new object[] { null, null, null }; + } + } + + [Theory] + [MemberData(nameof(TypesForGetSupportedContentTypes))] + public void GetSupportedContentTypes_ReturnsSupportedTypes(Type declaredType, + Type runtimeType, object expectedOutput) + { + // Arrange + var formatter = new XmlDataContractSerializerOutputFormatter(); + + // Act + var result = formatter.GetSupportedContentTypes( + declaredType, runtimeType, MediaTypeHeaderValue.Parse("application/xml")); + + // Assert + if (expectedOutput != null) + { + Assert.Equal(expectedOutput, Assert.Single(result).ToString()); + } + else + { + Assert.Equal(expectedOutput, result); + } + } + + [Fact] + public async Task WriteAsync_ThrowsWhenNotConfiguredWithKnownTypes() + { + // Arrange + var sampleInput = new SomeDummyClass { SampleInt = 1, SampleString = "TestString" }; + var formatter = new XmlDataContractSerializerOutputFormatter(); + var outputFormatterContext = GetOutputFormatterContext(sampleInput, typeof(DummyClass)); + + // Act & Assert + await Assert.ThrowsAsync(typeof(SerializationException), + async () => await formatter.WriteAsync(outputFormatterContext)); + } + + [Fact] + public async Task WriteAsync_ThrowsWhenNotConfiguredWithPreserveReferences() + { + // Arrange + var child = new Child { Id = 1 }; + var parent = new Parent { Name = "Parent", Children = new List { child } }; + child.Parent = parent; + + var formatter = new XmlDataContractSerializerOutputFormatter(); + var outputFormatterContext = GetOutputFormatterContext(parent, parent.GetType()); + + // Act & Assert + await Assert.ThrowsAsync(typeof(SerializationException), + async () => await formatter.WriteAsync(outputFormatterContext)); + } + + [Fact] + public async Task WriteAsync_WritesWhenConfiguredWithRootName() + { + // Arrange + var sampleInt = 10; + var SubstituteRootName = "SomeOtherClass"; + var SubstituteRootNamespace = "http://tempuri.org"; + var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + + var expectedOutput = string.Format( + "<{0} xmlns:i=\"{2}\" xmlns=\"{1}\">{3}", + SubstituteRootName, + SubstituteRootNamespace, + InstanceNamespace, + sampleInt); + + var sampleInput = new DummyClass { SampleInt = sampleInt }; + + var dictionary = new XmlDictionary(); + var settings = new DataContractSerializerSettings + { + RootName = dictionary.Add(SubstituteRootName), + RootNamespace = dictionary.Add(SubstituteRootNamespace) + }; + var formatter = new XmlDataContractSerializerOutputFormatter + { + SerializerSettings = settings + }; + var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); + + // Act + await formatter.WriteAsync(outputFormatterContext); + + // Assert + Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); + outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0; + var actualOutput = new StreamReader( + outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8).ReadToEnd(); + Assert.Equal(expectedOutput, actualOutput); + } + + [Fact] + public async Task WriteAsync_WritesWhenConfiguredWithKnownTypes() + { + // Arrange + var sampleInt = 10; + var sampleString = "TestString"; + var KnownTypeName = "SomeDummyClass"; + var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + + var expectedOutput = string.Format( + "{2}" + + "{3}", + KnownTypeName, + InstanceNamespace, + sampleInt, + sampleString); + + var sampleInput = new SomeDummyClass + { + SampleInt = sampleInt, + SampleString = sampleString + }; + + var settings = new DataContractSerializerSettings + { + KnownTypes = new[] { typeof(SomeDummyClass) } + }; + var formatter = new XmlDataContractSerializerOutputFormatter + { + SerializerSettings = settings + }; + var outputFormatterContext = GetOutputFormatterContext(sampleInput, typeof(DummyClass)); + + // Act + await formatter.WriteAsync(outputFormatterContext); + + // Assert + Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); + outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0; + var actualOutput = new StreamReader( + outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8).ReadToEnd(); + Assert.Equal(expectedOutput, actualOutput); + } + + [Fact] + public async Task WriteAsync_WritesWhenConfiguredWithPreserveReferences() + { + // Arrange + var sampleId = 1; + var sampleName = "Parent"; + var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + var SerializationNamespace = "http://schemas.microsoft.com/2003/10/Serialization/"; + + var expectedOutput = string.Format( + "" + + "" + + "{2}" + + "{3}", + InstanceNamespace, + SerializationNamespace, + sampleId, + sampleName); + + var child = new Child { Id = sampleId }; + var parent = new Parent { Name = sampleName, Children = new List { child } }; + child.Parent = parent; + + var settings = new DataContractSerializerSettings + { + PreserveObjectReferences = true + }; + var formatter = new XmlDataContractSerializerOutputFormatter + { + SerializerSettings = settings + }; + var outputFormatterContext = GetOutputFormatterContext(parent, parent.GetType()); + + // Act + await formatter.WriteAsync(outputFormatterContext); + + // Assert + Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); + outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0; + var actualOutput = new StreamReader( + outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8).ReadToEnd(); + Assert.Equal(expectedOutput, actualOutput); + } + + private OutputFormatterContext GetOutputFormatterContext(object outputValue, Type outputType, + string contentType = "application/xml; charset=utf-8") + { + return new OutputFormatterContext + { + Object = outputValue, + DeclaredType = outputType, + ActionContext = GetActionContext(contentType) + }; + } + + private static ActionContext GetActionContext(string contentType) + { + var request = new Mock(); + var headers = new HeaderDictionary(new Dictionary(StringComparer.OrdinalIgnoreCase)); + headers["Accept-Charset"] = MediaTypeHeaderValue.Parse(contentType).Charset; + request.Setup(r => r.ContentType).Returns(contentType); + request.SetupGet(r => r.Headers).Returns(headers); + var response = new Mock(); + response.SetupGet(f => f.Body).Returns(new MemoryStream()); + var httpContext = new Mock(); + httpContext.SetupGet(c => c.Request).Returns(request.Object); + httpContext.SetupGet(c => c.Response).Returns(response.Object); + return new ActionContext(httpContext.Object, routeData: null, actionDescriptor: null); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerInputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs similarity index 94% rename from test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerInputFormatterTests.cs rename to test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs index e6f3189277..cfadaba9a0 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerInputFormatterTests.cs +++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs @@ -17,7 +17,7 @@ using Xunit; namespace Microsoft.AspNet.Mvc { - public class XmlSerializerInputFormatterTests + public class XmlSerializerInputFormatterTest { public class DummyClass { @@ -65,7 +65,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public void XmlSerializerFormatterHasProperSuppportedMediaTypes() + public void HasProperSuppportedMediaTypes() { // Arrange & Act var formatter = new XmlSerializerInputFormatter(); @@ -80,7 +80,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public void XmlSerializerFormatterHasProperSuppportedEncodings() + public void HasProperSuppportedEncodings() { // Arrange & Act var formatter = new XmlSerializerInputFormatter(); @@ -91,7 +91,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlSerializerFormatterReadsSimpleTypes() + public async Task ReadAsync_ReadsSimpleTypes() { // Arrange var expectedInt = 10; @@ -122,7 +122,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlSerializerFormatterReadsComplexTypes() + public async Task ReadAsync_ReadsComplexTypes() { // Arrange var expectedInt = 10; @@ -156,7 +156,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlSerializerFormatterReadsWhenMaxDepthIsModified() + public async Task ReadAsync_ReadsWhenMaxDepthIsModified() { // Arrange var expectedInt = 10; @@ -180,7 +180,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlSerializerFormatterThrowsOnExceededMaxDepth() + public async Task ReadAsync_ThrowsOnExceededMaxDepth() { if (TestPlatformHelper.IsMono) { @@ -205,7 +205,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlSerializerFormatterThrowsWhenReaderQuotasAreChanged() + public async Task ReadAsync_ThrowsWhenReaderQuotasAreChanged() { if (TestPlatformHelper.IsMono) { @@ -230,7 +230,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public void XmlSerializerSerializerThrowsWhenMaxDepthIsBelowOne() + public void SetMaxDepth_ThrowsWhenMaxDepthIsBelowOne() { // Arrange var formatter = new XmlSerializerInputFormatter(); @@ -240,7 +240,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task VerifyStreamIsOpenAfterRead() + public async Task ReadAsync_VerifyStreamIsOpenAfterRead() { // Arrange var input = "" + @@ -258,7 +258,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlSerializerFormatterThrowsOnInvalidCharacters() + public async Task ReadAsync_ThrowsOnInvalidCharacters() { // Arrange var expectedException = TestPlatformHelper.IsMono ? typeof(InvalidOperationException) : @@ -286,7 +286,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlSerializerFormatterIgnoresBOMCharacters() + public async Task ReadAsync_IgnoresBOMCharacters() { // Arrange var sampleString = "Test"; @@ -317,7 +317,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task XmlSerializerFormatterAcceptsUTF16Characters() + public async Task ReadAsync_AcceptsUTF16Characters() { // Arrange var expectedInt = 10; @@ -347,7 +347,7 @@ namespace Microsoft.AspNet.Mvc } [Fact] - public async Task ReadsSerializableErrorXml() + public async Task ReadAsync_ReadsSerializableErrorXml() { // Arrange var serializableErrorXml = "" + diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerOutputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerOutputFormatterTest.cs similarity index 99% rename from test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerOutputFormatterTests.cs rename to test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerOutputFormatterTest.cs index 633c3c8bda..cf54c35344 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerOutputFormatterTests.cs +++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerOutputFormatterTest.cs @@ -13,9 +13,9 @@ using Microsoft.AspNet.Mvc.Xml; using Moq; using Xunit; -namespace Microsoft.AspNet.Mvc.Core +namespace Microsoft.AspNet.Mvc { - public class XmlSerializerOutputFormatterTests + public class XmlSerializerOutputFormatterTest { public class DummyClass { diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/project.json b/test/Microsoft.AspNet.Mvc.Xml.Test/project.json new file mode 100644 index 0000000000..7c927e5db4 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Xml.Test/project.json @@ -0,0 +1,18 @@ +{ + "compilationOptions": { + "warningsAsErrors": "true" + }, + "dependencies": { + "Microsoft.AspNet.Mvc" : "6.0.0-*", + "Microsoft.AspNet.Mvc.Xml" : "6.0.0-*", + "Microsoft.AspNet.Testing": "1.0.0-*", + "Moq": "4.2.1312.1622", + "xunit.runner.kre": "1.0.0-*" + }, + "commands": { + "test": "xunit.runner.kre" + }, + "frameworks": { + "aspnet50": { } + } +} diff --git a/test/XmlDataContractSerializerInputFormatterTests.cs b/test/XmlDataContractSerializerInputFormatterTests.cs new file mode 100644 index 0000000000..339c83e0ae --- /dev/null +++ b/test/XmlDataContractSerializerInputFormatterTests.cs @@ -0,0 +1,499 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if ASPNET50 +using System; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Testing; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc +{ + public class XmlDataContractSerializerInputFormatterTest + { + [DataContract(Name = "DummyClass", Namespace = "")] + public class DummyClass + { + [DataMember] + public int SampleInt { get; set; } + } + + [DataContract(Name = "SomeDummyClass", Namespace = "")] + public class SomeDummyClass : DummyClass + { + [DataMember] + public string SampleString { get; set; } + } + + [DataContract(Name = "TestLevelOne", Namespace = "")] + public class TestLevelOne + { + [DataMember] + public int SampleInt { get; set; } + [DataMember] + public string sampleString; + public DateTime SampleDate { get; set; } + } + + [DataContract(Name = "TestLevelTwo", Namespace = "")] + public class TestLevelTwo + { + [DataMember] + public string SampleString { get; set; } + [DataMember] + public TestLevelOne TestOne { get; set; } + } + + [Theory] + [InlineData("application/xml", true)] + [InlineData("application/*", true)] + [InlineData("*/*", true)] + [InlineData("text/xml", true)] + [InlineData("text/*", true)] + [InlineData("text/json", false)] + [InlineData("application/json", false)] + [InlineData("", false)] + [InlineData(null, false)] + [InlineData("invalid", false)] + public void CanRead_ReturnsTrueForAnySupportedContentType(string requestContentType, bool expectedCanRead) + { + // Arrange + var formatter = new XmlDataContractSerializerInputFormatter(); + var contentBytes = Encoding.UTF8.GetBytes("content"); + + var actionContext = GetActionContext(contentBytes, contentType: requestContentType); + var formatterContext = new InputFormatterContext(actionContext, typeof(string)); + + // Act + var result = formatter.CanRead(formatterContext); + + // Assert + Assert.Equal(expectedCanRead, result); + } + + [Fact] + public void XmlDataContractSerializerFormatterHasProperSuppportedMediaTypes() + { + // Arrange & Act + var formatter = new XmlDataContractSerializerInputFormatter(); + + // Assert + Assert.True(formatter.SupportedMediaTypes + .Select(content => content.ToString()) + .Contains("application/xml")); + Assert.True(formatter.SupportedMediaTypes + .Select(content => content.ToString()) + .Contains("text/xml")); + } + + [Fact] + public void XmlDataContractSerializerFormatterHasProperSuppportedEncodings() + { + // Arrange & Act + var formatter = new XmlDataContractSerializerInputFormatter(); + + // Assert + Assert.True(formatter.SupportedEncodings.Any(i => i.WebName == "utf-8")); + Assert.True(formatter.SupportedEncodings.Any(i => i.WebName == "utf-16")); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterReadsSimpleTypes() + { + // Arrange + var expectedInt = 10; + var expectedString = "TestString"; + + var input = "" + + "" + expectedInt + "" + + "" + expectedString + ""; + + var formatter = new XmlDataContractSerializerInputFormatter(); + var contentBytes = Encoding.UTF8.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne)); + + // Act + var model = await formatter.ReadAsync(context); + + // Assert + Assert.NotNull(model); + Assert.IsType(model); + + var levelOneModel = model as TestLevelOne; + Assert.Equal(expectedInt, levelOneModel.SampleInt); + Assert.Equal(expectedString, levelOneModel.sampleString); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterReadsComplexTypes() + { + // Arrange + var expectedInt = 10; + var expectedString = "TestString"; + var expectedLevelTwoString = "102"; + + var input = "" + + "" + expectedLevelTwoString + "" + + "" + expectedInt + "" + + "" + expectedString + ""; + + var formatter = new XmlDataContractSerializerInputFormatter(); + var contentBytes = Encoding.UTF8.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo)); + + // Act + var model = await formatter.ReadAsync(context); + + // Assert + Assert.NotNull(model); + Assert.IsType(model); + + var levelTwoModel = model as TestLevelTwo; + Assert.Equal(expectedLevelTwoString, levelTwoModel.SampleString); + Assert.Equal(expectedInt, levelTwoModel.TestOne.SampleInt); + Assert.Equal(expectedString, levelTwoModel.TestOne.sampleString); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterReadsWhenMaxDepthIsModified() + { + // Arrange + var expectedInt = 10; + + var input = "" + + "" + expectedInt + ""; + var formatter = new XmlDataContractSerializerInputFormatter(); + formatter.MaxDepth = 10; + var contentBytes = Encoding.UTF8.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(DummyClass)); + + + // Act + var model = await formatter.ReadAsync(context); + + // Assert + Assert.NotNull(model); + Assert.IsType(model); + var dummyModel = model as DummyClass; + Assert.Equal(expectedInt, dummyModel.SampleInt); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterThrowsOnExceededMaxDepth() + { + if (TestPlatformHelper.IsMono) + { + // ReaderQuotas are not honored on Mono + return; + } + + // Arrange + var input = "" + + "test" + + "10" + + "test"; + var formatter = new XmlDataContractSerializerInputFormatter(); + formatter.MaxDepth = 1; + var contentBytes = Encoding.UTF8.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo)); + + // Act & Assert + await Assert.ThrowsAsync(typeof(SerializationException), async () => await formatter.ReadAsync(context)); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterThrowsWhenReaderQuotasAreChanged() + { + if (TestPlatformHelper.IsMono) + { + // ReaderQuotas are not honored on Mono + return; + } + + // Arrange + var input = "" + + "test" + + "10" + + "test"; + var formatter = new XmlDataContractSerializerInputFormatter(); + formatter.XmlDictionaryReaderQuotas.MaxStringContentLength = 2; + var contentBytes = Encoding.UTF8.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo)); + + // Act & Assert + await Assert.ThrowsAsync(typeof(SerializationException), async () => await formatter.ReadAsync(context)); + } + + [Fact] + public void XmlDataContractSerializerFormatterThrowsWhenMaxDepthIsBelowOne() + { + // Arrange + var formatter = new XmlDataContractSerializerInputFormatter(); + + // Act & Assert + Assert.Throws(typeof(ArgumentException), () => formatter.MaxDepth = 0); + } + + [Fact] + public async Task VerifyStreamIsOpenAfterRead() + { + // Arrange + var input = "" + + "10"; + var formatter = new XmlDataContractSerializerInputFormatter(); + var contentBytes = Encoding.UTF8.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(DummyClass)); + + // Act + var model = await formatter.ReadAsync(context); + + // Assert + Assert.NotNull(model); + Assert.True(context.ActionContext.HttpContext.Request.Body.CanRead); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterThrowsOnInvalidCharacters() + { + // Arrange + var expectedException = TestPlatformHelper.IsMono ? typeof(SerializationException) : + typeof(XmlException); + var expectedMessage = TestPlatformHelper.IsMono ? + "Expected element 'TestLevelTwo' in namespace '', but found Element node 'DummyClass' in namespace ''" : + "The encoding in the declaration 'UTF-8' does not match the encoding of the document 'utf-16LE'."; + var inpStart = Encodings.UTF16EncodingLittleEndian.GetBytes("" + + ""); + byte[] inp = { 192, 193 }; + var inpEnd = Encodings.UTF16EncodingLittleEndian.GetBytes(""); + + var contentBytes = new byte[inpStart.Length + inp.Length + inpEnd.Length]; + Buffer.BlockCopy(inpStart, 0, contentBytes, 0, inpStart.Length); + Buffer.BlockCopy(inp, 0, contentBytes, inpStart.Length, inp.Length); + Buffer.BlockCopy(inpEnd, 0, contentBytes, inpStart.Length + inp.Length, inpEnd.Length); + + var formatter = new XmlDataContractSerializerInputFormatter(); + var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo)); + + // Act + var ex = await Assert.ThrowsAsync(expectedException, () => formatter.ReadAsync(context)); + Assert.Equal(expectedMessage, ex.Message); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterIgnoresBOMCharacters() + { + // Arrange + var sampleString = "Test"; + var sampleStringBytes = Encoding.UTF8.GetBytes(sampleString); + var inputStart = Encoding.UTF8.GetBytes("" + Environment.NewLine + + "" + sampleString); + byte[] bom = { 0xef, 0xbb, 0xbf }; + var inputEnd = Encoding.UTF8.GetBytes(""); + var expectedBytes = new byte[sampleString.Length + bom.Length]; + + var contentBytes = new byte[inputStart.Length + bom.Length + inputEnd.Length]; + Buffer.BlockCopy(inputStart, 0, contentBytes, 0, inputStart.Length); + Buffer.BlockCopy(bom, 0, contentBytes, inputStart.Length, bom.Length); + Buffer.BlockCopy(inputEnd, 0, contentBytes, inputStart.Length + bom.Length, inputEnd.Length); + + var formatter = new XmlDataContractSerializerInputFormatter(); + var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo)); + + // Act + var model = await formatter.ReadAsync(context); + + // Assert + Assert.NotNull(model); + var levelTwoModel = model as TestLevelTwo; + Buffer.BlockCopy(sampleStringBytes, 0, expectedBytes, 0, sampleStringBytes.Length); + Buffer.BlockCopy(bom, 0, expectedBytes, sampleStringBytes.Length, bom.Length); + Assert.Equal(expectedBytes, Encoding.UTF8.GetBytes(levelTwoModel.SampleString)); + } + + [Fact] + public async Task XmlDataContractSerializerAcceptsUTF16Characters() + { + // Arrange + var expectedInt = 10; + var expectedString = "TestString"; + + var input = "" + + "" + expectedInt + "" + + "" + expectedString + ""; + + var formatter = new XmlDataContractSerializerInputFormatter(); + var contentBytes = Encodings.UTF16EncodingLittleEndian.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne)); + + // Act + var model = await formatter.ReadAsync(context); + + // Assert + Assert.NotNull(model); + Assert.IsType(model); + + var levelOneModel = model as TestLevelOne; + Assert.Equal(expectedInt, levelOneModel.SampleInt); + Assert.Equal(expectedString, levelOneModel.sampleString); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterThrowsWhenNotConfiguredWithRootName() + { + // TODO: Test on Mono platform + + // Arrange + const string SubstituteRootName = "SomeOtherClass"; + const string SubstituteRootNamespace = "http://tempuri.org"; + + var input = string.Format( + "<{0} xmlns=\"{1}\">1", + SubstituteRootName, + SubstituteRootNamespace); + var formatter = new XmlDataContractSerializerInputFormatter(); + var contentBytes = Encoding.UTF8.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(DummyClass)); + + // Act & Assert + await Assert.ThrowsAsync(typeof(SerializationException), async () => await formatter.ReadAsync(context)); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterReadsWhenConfiguredWithRootName() + { + // Arrange + var expectedInt = 10; + const string SubstituteRootName = "SomeOtherClass"; + const string SubstituteRootNamespace = "http://tempuri.org"; + + var input = string.Format( + "<{0} xmlns=\"{1}\">{2}", + SubstituteRootName, + SubstituteRootNamespace, + expectedInt); + + var dictionary = new XmlDictionary(); + var settings = new DataContractSerializerSettings + { + RootName = dictionary.Add(SubstituteRootName), + RootNamespace = dictionary.Add(SubstituteRootNamespace) + }; + var formatter = new XmlDataContractSerializerInputFormatter + { + SerializerSettings = settings + }; + var contentBytes = Encoding.UTF8.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(DummyClass)); + + // Act + var model = await formatter.ReadAsync(context); + + // Assert + Assert.NotNull(model); + Assert.IsType(model); + + var dummyModel = model as DummyClass; + Assert.Equal(expectedInt, dummyModel.SampleInt); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterThrowsWhenNotConfiguredWithKnownTypes() + { + // TODO: Test on Mono platform + + // Arrange + const string KnownTypeName = "SomeDummyClass"; + const string InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + + var input = string.Format( + "1" + + "TestString", + KnownTypeName, + InstanceNamespace); + + var formatter = new XmlDataContractSerializerInputFormatter(); + var contentBytes = Encoding.UTF8.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(DummyClass)); + + // Act & Assert + await Assert.ThrowsAsync(typeof(SerializationException), async () => await formatter.ReadAsync(context)); + } + + [Fact] + public async Task XmlDataContractSerializerFormatterReadsWhenConfiguredWithKnownTypes() + { + // Arrange + var expectedInt = 10; + var expectedString = "TestString"; + const string KnownTypeName = "SomeDummyClass"; + const string InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + + var input = string.Format( + "{2}" + + "{3}", + KnownTypeName, + InstanceNamespace, + expectedInt, + expectedString); + var settings = new DataContractSerializerSettings + { + KnownTypes = new[] { typeof(SomeDummyClass) } + }; + var formatter = new XmlDataContractSerializerInputFormatter + { + SerializerSettings = settings + }; + var contentBytes = Encoding.UTF8.GetBytes(input); + var context = GetInputFormatterContext(contentBytes, typeof(DummyClass)); + + // Act + var model = await formatter.ReadAsync(context); + + // Assert + Assert.NotNull(model); + Assert.IsType(model); + + var dummyModel = model as SomeDummyClass; + Assert.Equal(expectedInt, dummyModel.SampleInt); + Assert.Equal(expectedString, dummyModel.SampleString); + } + + private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType) + { + var actionContext = GetActionContext(contentBytes); + var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, modelType); + return new InputFormatterContext(actionContext, metadata.ModelType); + } + + private static ActionContext GetActionContext(byte[] contentBytes, + string contentType = "application/xml") + { + return new ActionContext(GetHttpContext(contentBytes, contentType), + new AspNet.Routing.RouteData(), + new ActionDescriptor()); + } + private static HttpContext GetHttpContext(byte[] contentBytes, + string contentType = "application/xml") + { + var request = new Mock(); + var headers = new Mock(); + request.SetupGet(r => r.Headers).Returns(headers.Object); + request.SetupGet(f => f.Body).Returns(new MemoryStream(contentBytes)); + request.SetupGet(f => f.ContentType).Returns(contentType); + + var httpContext = new Mock(); + httpContext.SetupGet(c => c.Request).Returns(request.Object); + httpContext.SetupGet(c => c.Request).Returns(request.Object); + return httpContext.Object; + } + } +} +#endif diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerOutputFormatterTests.cs b/test/XmlDataContractSerializerOutputFormatterTests.cs similarity index 95% rename from test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerOutputFormatterTests.cs rename to test/XmlDataContractSerializerOutputFormatterTests.cs index cc3d1e9e8e..e5c95f1047 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerOutputFormatterTests.cs +++ b/test/XmlDataContractSerializerOutputFormatterTests.cs @@ -7,13 +7,12 @@ using System.IO; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; -using System.Xml; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Core.Collections; -using Microsoft.AspNet.Mvc.Xml; using Microsoft.Net.Http.Headers; using Moq; using Xunit; +using System.Xml; namespace Microsoft.AspNet.Mvc.Core { @@ -220,10 +219,9 @@ namespace Microsoft.AspNet.Mvc.Core Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body); outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0; Assert.Equal("" + - "" + - "10", - new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body, - Encoding.UTF8).ReadToEnd()); + "" + + "10", + new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8).ReadToEnd()); } [Fact] @@ -301,6 +299,7 @@ namespace Microsoft.AspNet.Mvc.Core // Act & Assert await formatter.WriteAsync(outputFormatterContext); } + public static IEnumerable TypesForCanWriteResult { get @@ -347,8 +346,7 @@ namespace Microsoft.AspNet.Mvc.Core [Theory] [MemberData(nameof(TypesForGetSupportedContentTypes))] - public void XmlDataContractSerializer_GetSupportedContentTypes_Returns_SupportedTypes(Type declaredType, - Type runtimeType, object expectedOutput) + public void XmlDataContractSerializer_GetSupportedContentTypes_Returns_SupportedTypes(Type declaredType, Type runtimeType, object expectedOutput) { // Arrange var formatter = new XmlDataContractSerializerOutputFormatter(); @@ -371,19 +369,22 @@ namespace Microsoft.AspNet.Mvc.Core [Fact] public async Task XmlDataContractSerializerOutputFormatterThrowsWhenNotConfiguredWithKnownTypes() { + // TODO: Test on Mono platform + // Arrange var sampleInput = new SomeDummyClass { SampleInt = 1, SampleString = "TestString" }; var formatter = new XmlDataContractSerializerOutputFormatter(); var outputFormatterContext = GetOutputFormatterContext(sampleInput, typeof(DummyClass)); // Act & Assert - await Assert.ThrowsAsync(typeof(SerializationException), - async () => await formatter.WriteAsync(outputFormatterContext)); + await Assert.ThrowsAsync(typeof(SerializationException), async () => await formatter.WriteAsync(outputFormatterContext)); } [Fact] public async Task XmlDataContractSerializerOutputFormatterThrowsWhenNotConfiguredWithPreserveReferences() { + // TODO: Test on Mono platform + // Arrange var child = new Child { Id = 1 }; var parent = new Parent { Name = "Parent", Children = new List { child } }; @@ -393,8 +394,7 @@ namespace Microsoft.AspNet.Mvc.Core var outputFormatterContext = GetOutputFormatterContext(parent, parent.GetType()); // Act & Assert - await Assert.ThrowsAsync(typeof(SerializationException), - async () => await formatter.WriteAsync(outputFormatterContext)); + await Assert.ThrowsAsync(typeof(SerializationException), async () => await formatter.WriteAsync(outputFormatterContext)); } [Fact] @@ -402,9 +402,9 @@ namespace Microsoft.AspNet.Mvc.Core { // Arrange var sampleInt = 10; - var SubstituteRootName = "SomeOtherClass"; - var SubstituteRootNamespace = "http://tempuri.org"; - var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + const string SubstituteRootName = "SomeOtherClass"; + const string SubstituteRootNamespace = "http://tempuri.org"; + const string InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; var expectedOutput = string.Format( "<{0} xmlns:i=\"{2}\" xmlns=\"{1}\">{3}", @@ -444,8 +444,8 @@ namespace Microsoft.AspNet.Mvc.Core // Arrange var sampleInt = 10; var sampleString = "TestString"; - var KnownTypeName = "SomeDummyClass"; - var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + const string KnownTypeName = "SomeDummyClass"; + const string InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; var expectedOutput = string.Format( "{2}" @@ -488,8 +488,8 @@ namespace Microsoft.AspNet.Mvc.Core // Arrange var sampleId = 1; var sampleName = "Parent"; - var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; - var SerializationNamespace = "http://schemas.microsoft.com/2003/10/Serialization/"; + const string InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + const string SerializationNamespace = "http://schemas.microsoft.com/2003/10/Serialization/"; var expectedOutput = string.Format( "" +