diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonOutputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonOutputFormatterTests.cs index 7126d60d54..a487a1944d 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonOutputFormatterTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonOutputFormatterTests.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Http; @@ -10,6 +11,7 @@ using Microsoft.AspNet.Http.Core.Collections; using Microsoft.Net.Http.Headers; using Moq; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using Xunit; @@ -87,8 +89,107 @@ namespace Microsoft.AspNet.Mvc.Core.Test.Formatters Assert.Equal(expectedOutput, streamReader.ReadToEnd()); } - private OutputFormatterContext GetOutputFormatterContext(object outputValue, Type outputType, - string contentType = "application/xml; charset=utf-8") + [Fact] + public async Task WriteToStreamAsync_RoundTripsJToken() + { + // Arrange + var beforeMessage = "Hello World"; + var formatter = new JsonOutputFormatter(); + var before = new JValue(beforeMessage); + var memStream = new MemoryStream(); + var outputFormatterContext = GetOutputFormatterContext( + beforeMessage, + typeof(string), + "application/json; charset=utf-8", + memStream); + + // Act + await formatter.WriteResponseBodyAsync(outputFormatterContext); + + // Assert + memStream.Position = 0; + var after = JToken.Load(new JsonTextReader(new StreamReader(memStream))); + var afterMessage = after.ToObject(); + + Assert.Equal(beforeMessage, afterMessage); + } + + public static TheoryData WriteCorrectCharacterEncoding + { + get + { + return new TheoryData + { + { "This is a test 激光這兩個字是甚麼意思 string written using utf-8", "utf-8", true }, + { "This is a test 激光這兩個字是甚麼意思 string written using utf-16", "utf-16", true }, + { "This is a test 激光這兩個字是甚麼意思 string written using utf-32", "utf-32", false }, + { "This is a test 激光這兩個字是甚麼意思 string written using shift_jis", "shift_jis", false }, + { "This is a test æøå string written using iso-8859-1", "iso-8859-1", false }, + { "This is a test 레이저 단어 뜻 string written using iso-2022-kr", "iso-2022-kr", false }, + }; + } + } + + [Theory] + [MemberData(nameof(WriteCorrectCharacterEncoding))] + public async Task WriteToStreamAsync_UsesCorrectCharacterEncoding( + string content, + string encodingAsString, + bool isDefaultEncoding) + { + // Arrange + var formatter = new JsonOutputFormatter(); + var formattedContent = "\"" + content + "\""; + var mediaType = string.Format("application/json; charset={0}", encodingAsString); + var encoding = CreateOrGetSupportedEncoding(formatter, encodingAsString, isDefaultEncoding); + var preamble = encoding.GetPreamble(); + var data = encoding.GetBytes(formattedContent); + var expectedData = new byte[preamble.Length + data.Length]; + Buffer.BlockCopy(preamble, 0, expectedData, 0, preamble.Length); + Buffer.BlockCopy(data, 0, expectedData, preamble.Length, data.Length); + + var memStream = new MemoryStream(); + var outputFormatterContext = new OutputFormatterContext + { + Object = content, + DeclaredType = typeof(string), + ActionContext = GetActionContext(MediaTypeHeaderValue.Parse(mediaType), memStream), + SelectedEncoding = encoding + }; + + // Act + await formatter.WriteResponseBodyAsync(outputFormatterContext); + + // Assert + var actualData = memStream.ToArray(); + Assert.Equal(expectedData, actualData); + } + + private static Encoding CreateOrGetSupportedEncoding( + JsonOutputFormatter formatter, + string encodingAsString, + bool isDefaultEncoding) + { + Encoding encoding = null; + if (isDefaultEncoding) + { + encoding = formatter.SupportedEncodings + .First((e) => e.WebName.Equals(encodingAsString, StringComparison.OrdinalIgnoreCase)); + } + else + { + encoding = Encoding.GetEncoding(encodingAsString); + formatter.SupportedEncodings.Add(encoding); + } + + return encoding; + } + + private static OutputFormatterContext GetOutputFormatterContext( + object outputValue, + Type outputType, + string contentType = "application/xml; charset=utf-8", + MemoryStream responseStream = null) { var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(contentType); @@ -96,12 +197,14 @@ namespace Microsoft.AspNet.Mvc.Core.Test.Formatters { Object = outputValue, DeclaredType = outputType, - ActionContext = GetActionContext(mediaTypeHeaderValue), + ActionContext = GetActionContext(mediaTypeHeaderValue, responseStream), SelectedEncoding = Encoding.GetEncoding(mediaTypeHeaderValue.Charset) }; } - private static ActionContext GetActionContext(MediaTypeHeaderValue contentType) + private static ActionContext GetActionContext( + MediaTypeHeaderValue contentType, + MemoryStream responseStream = null) { var request = new Mock(); var headers = new HeaderDictionary(); @@ -109,7 +212,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test.Formatters request.SetupGet(r => r.Headers).Returns(headers); headers[HeaderNames.AcceptCharset] = contentType.Charset; var response = new Mock(); - response.SetupGet(f => f.Body).Returns(new MemoryStream()); + response.SetupGet(f => f.Body).Returns(responseStream ?? new MemoryStream()); var httpContext = new Mock(); httpContext.SetupGet(c => c.Request).Returns(request.Object); httpContext.SetupGet(c => c.Response).Returns(response.Object);