From d3cbae8ea82e5e59ef28b77fb1ac094bc01eae81 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Wed, 11 Mar 2020 23:37:35 -0700 Subject: [PATCH 01/13] Test and XElement update --- .../src/System/Xml/Linq/XElement.cs | 2 + .../tests/misc/LoadSaveAsyncTests.cs | 57 ++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs index 9d9445bcdbdb6..dcca9477fbacf 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs @@ -1083,6 +1083,7 @@ public async Task SaveAsync(Stream stream, SaveOptions options, CancellationToke using (XmlWriter w = XmlWriter.Create(stream, ws)) { await SaveAsync(w, cancellationToken).ConfigureAwait(false); + await w.FlushAsync().ConfigureAwait(false); } } @@ -1144,6 +1145,7 @@ public async Task SaveAsync(TextWriter textWriter, SaveOptions options, Cancella using (XmlWriter w = XmlWriter.Create(textWriter, ws)) { await SaveAsync(w, cancellationToken).ConfigureAwait(false); + await w.FlushAsync().ConfigureAwait(false); } } diff --git a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs index 5fcab26a8fba3..1aa56c7b9639c 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs @@ -202,5 +202,60 @@ public static IEnumerable RoundtripOptions_MemberData } } + [Fact] + public async Task XDocumentSaveAsync_ShouldCall_FlushAsyncWriteAsyncOnly() + { + var doc = XDocument.Parse("Test document async save"); + var element = XElement.Parse(" Test element async save"); + using (TestMemoryStream stream = new TestMemoryStream(true)) + { + await doc.SaveAsync(stream, SaveOptions.None, CancellationToken.None); + await element.SaveAsync(stream, SaveOptions.None, CancellationToken.None); + } + } + + [Fact] + public void XDocumentSave_ShouldNotCall_FlushAsyncWriteAsync() + { + var doc = XDocument.Parse("Test document async save"); + var element = XElement.Parse(" Test element save"); + using (TestMemoryStream stream = new TestMemoryStream(false)) + { + doc.Save(stream); + element.Save(stream); + } + } + } + public class TestMemoryStream : MemoryStream + { + private bool _isAsync; + + public TestMemoryStream(bool async) + { + _isAsync = async; + } + public override void Flush() + { + Assert.False(_isAsync, "Sync operation not allowed"); + base.Flush(); + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + Assert.True(_isAsync, "Not a sync operation"); + return Task.CompletedTask; + } + + public override void Write(byte[] buffer, int offset, int count) + { + Assert.False(_isAsync, "Not a sync operation"); + base.Write(buffer, offset, count); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + Assert.True(_isAsync, "Sync operation not allowed"); + return Task.CompletedTask; + } } -} \ No newline at end of file +} From ccd8763598f366028595efd8ca7f440bc1b7d4e4 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 13 Mar 2020 15:24:59 -0700 Subject: [PATCH 02/13] Update XmlWriter to implement IAsyncDisposable --- .../src/System/Xml/Linq/XDocument.cs | 4 +- .../src/System/Xml/Linq/XElement.cs | 6 +- .../tests/misc/LoadSaveAsyncTests.cs | 49 +++++++------ .../src/System.Private.Xml.csproj | 3 + .../System/Xml/Core/XmlAsyncCheckWriter.cs | 6 ++ .../Xml/Core/XmlEncodedRawTextWriterAsync.cs | 56 +++++++++++++++ .../XmlRawTextWriterGeneratorAsync.ttinclude | 70 +++++++++++++++++-- .../src/System/Xml/Core/XmlRawWriterAsync.cs | 5 ++ .../Xml/Core/XmlUtf8RawTextWriterAsync.cs | 36 +++++++++- .../Xml/Core/XmlWellFormedWriterAsync.cs | 58 +++++++++++++++ .../src/System/Xml/Core/XmlWriterAsync.cs | 18 ++++- .../tests/XmlWriter/DisposeTests.cs | 61 ++++++++++++++++ .../tests/XmlWriter/WriteWithEncoding.cs | 33 +++++++++ .../ref/System.Xml.ReaderWriter.cs | 5 +- 14 files changed, 375 insertions(+), 35 deletions(-) diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs index b3b9cf563e312..91315cb8589f7 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs @@ -633,7 +633,7 @@ public async Task SaveAsync(Stream stream, SaveOptions options, CancellationToke } } - using (XmlWriter w = XmlWriter.Create(stream, ws)) + await using (XmlWriter w = XmlWriter.Create(stream, ws)) { await WriteToAsync(w, cancellationToken).ConfigureAwait(false); await w.FlushAsync().ConfigureAwait(false); @@ -706,7 +706,7 @@ public async Task SaveAsync(TextWriter textWriter, SaveOptions options, Cancella ws.Async = true; - using (XmlWriter w = XmlWriter.Create(textWriter, ws)) + await using (XmlWriter w = XmlWriter.Create(textWriter, ws)) { await WriteToAsync(w, cancellationToken).ConfigureAwait(false); await w.FlushAsync().ConfigureAwait(false); diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs index dcca9477fbacf..48442ca3a1b7f 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs @@ -1080,10 +1080,9 @@ public async Task SaveAsync(Stream stream, SaveOptions options, CancellationToke ws.Async = true; - using (XmlWriter w = XmlWriter.Create(stream, ws)) + await using (XmlWriter w = XmlWriter.Create(stream, ws)) { await SaveAsync(w, cancellationToken).ConfigureAwait(false); - await w.FlushAsync().ConfigureAwait(false); } } @@ -1142,10 +1141,9 @@ public async Task SaveAsync(TextWriter textWriter, SaveOptions options, Cancella ws.Async = true; - using (XmlWriter w = XmlWriter.Create(textWriter, ws)) + await using (XmlWriter w = XmlWriter.Create(textWriter, ws)) { await SaveAsync(w, cancellationToken).ConfigureAwait(false); - await w.FlushAsync().ConfigureAwait(false); } } diff --git a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs index 1aa56c7b9639c..004eeff5af30f 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; @@ -185,6 +186,7 @@ public static async Task RoundtripSyncAsyncMatches_Stream(bool document, LoadOpt // Compare to make sure the synchronous and asynchronous results are the same Assert.Equal(syncOutput.ToArray(), asyncOutput.ToArray()); + Console.WriteLine(Encoding.ASCII.GetString(asyncOutput.ToArray())); } // Inputs to the Roundtrip* tests: @@ -201,36 +203,43 @@ public static IEnumerable RoundtripOptions_MemberData yield return new object[] { doc, loadOptions, saveOptions }; } } - - [Fact] - public async Task XDocumentSaveAsync_ShouldCall_FlushAsyncWriteAsyncOnly() + public static IEnumerable IsAsync_SaveOptions_Data { - var doc = XDocument.Parse("Test document async save"); - var element = XElement.Parse(" Test element async save"); - using (TestMemoryStream stream = new TestMemoryStream(true)) + get { - await doc.SaveAsync(stream, SaveOptions.None, CancellationToken.None); - await element.SaveAsync(stream, SaveOptions.None, CancellationToken.None); + foreach (bool isAsync in new[] { true, false }) + foreach (SaveOptions saveOptions in Enum.GetValues(typeof(SaveOptions))) + yield return new object[] { isAsync, saveOptions }; } } - [Fact] - public void XDocumentSave_ShouldNotCall_FlushAsyncWriteAsync() + [Theory] + [MemberData(nameof(IsAsync_SaveOptions_Data))] + public async Task SaveAsync_CallsAsyncOnly_SaveSync_CallsSyncOnly(bool isAsync, SaveOptions saveOptions) { - var doc = XDocument.Parse("Test document async save"); - var element = XElement.Parse(" Test element save"); - using (TestMemoryStream stream = new TestMemoryStream(false)) + XDocument document = XDocument.Parse("Test document async save"); + var element = new XElement("Test"); + using (ForceSyncAsyncStream stream = new ForceSyncAsyncStream(isAsync)) { - doc.Save(stream); - element.Save(stream); + if (isAsync) + { + await document.SaveAsync(stream, saveOptions, CancellationToken.None).ConfigureAwait(false); + await element.SaveAsync(stream, saveOptions, CancellationToken.None).ConfigureAwait(false); + } + else + { + document.Save(stream); + element.Save(stream); + } } } } - public class TestMemoryStream : MemoryStream + + public class ForceSyncAsyncStream : MemoryStream { private bool _isAsync; - public TestMemoryStream(bool async) + public ForceSyncAsyncStream(bool async) { _isAsync = async; } @@ -242,19 +251,19 @@ public override void Flush() public override Task FlushAsync(CancellationToken cancellationToken) { - Assert.True(_isAsync, "Not a sync operation"); + Assert.True(_isAsync, "Async operation not allowed"); return Task.CompletedTask; } public override void Write(byte[] buffer, int offset, int count) { - Assert.False(_isAsync, "Not a sync operation"); + Assert.False(_isAsync, "Sync operation not allowed"); base.Write(buffer, offset, count); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - Assert.True(_isAsync, "Sync operation not allowed"); + Assert.True(_isAsync, "Async operation not allowed"); return Task.CompletedTask; } } diff --git a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj index c0215be859644..c9c7dfdbaadc5 100644 --- a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj +++ b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj @@ -807,4 +807,7 @@ + + + diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs index 0d7289e680c54..3f270b3d128b0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs @@ -576,6 +576,12 @@ public override Task WriteNodeAsync(XPathNavigator navigator, bool defattr) _lastTask = task; return task; } + + public override ValueTask CloseAsync() + { + CheckAsync(); + return _coreWriter.CloseAsync(); + } #endregion } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs index 3dd97591e12f9..970ba4cf5b047 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs @@ -73,6 +73,62 @@ internal override Task WriteXmlDeclarationAsync(string xmldecl) return Task.CompletedTask; } + public override async ValueTask CloseAsync() + { + try + { + await FlushBufferAsync().ConfigureAwait(false); + } + finally + { + // Future calls to Close or Flush shouldn't write to Stream or Writer + writeToNull = true; + + if (stream != null) + { + try + { + await stream.FlushAsync().ConfigureAwait(false); + } + finally + { + try + { + if (closeOutput) + { + await stream.DisposeAsync().ConfigureAwait(false); + } + } + finally + { + stream = null; + } + } + } + else if (writer != null) + { + try + { + await writer.FlushAsync().ConfigureAwait(false); + } + finally + { + try + { + if (closeOutput) + { + await writer.DisposeAsync().ConfigureAwait(false); + } + } + finally + { + writer = null; + } + } + } + } + } + // Serialize the document type declaration. public override async Task WriteDocTypeAsync(string name, string pubid, string sysid, string subset) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude index 772a269eb1cee..7bc67f77470b0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude @@ -75,6 +75,64 @@ namespace System.Xml return Task.CompletedTask; } + public override async ValueTask CloseAsync() + { + try + { + await FlushBufferAsync().ConfigureAwait(false); + } + finally + { + // Future calls to Close or Flush shouldn't write to Stream or Writer + writeToNull = true; + + if (stream != null) + { + try + { + await stream.FlushAsync().ConfigureAwait(false); + } + finally + { + try + { + if (closeOutput) + { + await stream.DisposeAsync().ConfigureAwait(false); + } + } + finally + { + stream = null; + } + } + } +<# if (WriterType == RawTextWriterType.Encoded) { #> + else if (writer != null) + { + try + { + await writer.FlushAsync().ConfigureAwait(false); + } + finally + { + try + { + if (closeOutput) + { + await writer.DisposeAsync().ConfigureAwait(false); + } + } + finally + { + writer = null; + } + } + } +<# } #> + } + } + // Serialize the document type declaration. public override async Task WriteDocTypeAsync(string name, string pubid, string sysid, string subset) { @@ -689,7 +747,7 @@ namespace System.Xml <#= BufferType #>* pDst = pDstBegin + bufPos; int ch = 0; - for (;;) + while (true) { <#= BufferType #>* pDstEnd = pDst + (pSrcEnd - pSrc); if (pDstEnd > pDstBegin + bufLen) @@ -879,7 +937,7 @@ namespace System.Xml <#= BufferType #>* pDst = pDstBegin + bufPos; int ch = 0; - for (;;) + while (true) { <#= BufferType #>* pDstEnd = pDst + (pSrcEnd - pSrc); if (pDstEnd > pDstBegin + bufLen) @@ -1105,7 +1163,7 @@ namespace System.Xml char* pSrc = pSrcBegin; int ch = 0; - for (;;) + while (true) { <#= BufferType #>* pDstEnd = pDst + (pSrcEnd - pSrc); if (pDstEnd > pDstBegin + bufLen) @@ -1266,7 +1324,7 @@ namespace System.Xml <#= BufferType #>* pDst = pDstBegin + bufPos; int ch = 0; - for (;;) + while (true) { <#= BufferType #>* pDstEnd = pDst + (pSrcEnd - pSrc); if (pDstEnd > pDstBegin + bufLen) @@ -1454,7 +1512,7 @@ namespace System.Xml <#= BufferType #>* pDst = pDstBegin + bufPos; int ch = 0; - for (;;) + while (true) { <#= BufferType #>* pDstEnd = pDst + (pSrcEnd - pSrc); if (pDstEnd > pDstBegin + bufLen) @@ -1630,7 +1688,7 @@ namespace System.Xml <#= BufferType #>* pDst = pDstBegin + bufPos; int ch = 0; - for (;;) + while (true) { <#= BufferType #>* pDstEnd = pDst + (pSrcEnd - pSrc); if (pDstEnd > pDstBegin + bufLen) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriterAsync.cs index 77ebbf5034a94..ebc14c56f24d0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriterAsync.cs @@ -226,5 +226,10 @@ internal virtual Task WriteEndBase64Async() // The Flush will call WriteRaw to write out the rest of the encoded characters return base64Encoder.FlushAsync(); } + + internal virtual ValueTask CloseAsync(WriteState currentState) + { + return CloseAsync(); + } } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs index 526f12feb17ed..2105651638232 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs @@ -37,7 +37,6 @@ internal override async Task WriteXmlDeclarationAsync(XmlStandalone standalone) // Output xml declaration only if user allows it and it was not already output if (!omitXmlDeclaration && !autoXmlDeclaration) { - await RawTextAsync(" 0) + { + await WriteEndElementAsync().ConfigureAwait(false); + } + } + else + { + if (_currentState != State.Error && _elemTop > 0) + { + //finish the start element tag '>' + try + { + await AdvanceStateAsync(Token.EndElement).ConfigureAwait(false); + } + catch + { + _currentState = State.Error; + throw; + } + } + } + + if (InBase64 && _rawWriter != null) + { + await _rawWriter.WriteEndBase64Async().ConfigureAwait(false); + } + + await _writer.FlushAsync().ConfigureAwait(false); + } + finally + { + try + { + if (_rawWriter != null) + { + await _rawWriter.CloseAsync(WriteState).ConfigureAwait(false); + } + else + { + await _writer.CloseAsync().ConfigureAwait(false); + } + } + finally + { + _currentState = State.Closed; + } + } + } + } } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs index 961be36f42f31..bf444b2b9a199 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs @@ -18,7 +18,7 @@ namespace System.Xml { // Represents a writer that provides fast non-cached forward-only way of generating XML streams containing XML documents // that conform to the W3C Extensible Markup Language (XML) 1.0 specification and the Namespaces in XML specification. - public abstract partial class XmlWriter : IDisposable + public abstract partial class XmlWriter : IDisposable, IAsyncDisposable { // Write methods // Writes out the XML declaration with the version "1.0". @@ -599,5 +599,21 @@ private async Task WriteLocalNamespacesAsync(XPathNavigator nsNav) await WriteAttributeStringAsync("xmlns", prefix, XmlReservedNs.NsXmlNs, ns).ConfigureAwait(false); } } + + public ValueTask DisposeAsync() + { + if (WriteState != WriteState.Closed) + { + return CloseAsync(); + } + + return default; + } + + public virtual ValueTask CloseAsync() + { + Close(); + return default; + } } } diff --git a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs index 7757543bd6622..4205e8a07a3be 100644 --- a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs @@ -4,6 +4,8 @@ using System.IO; using System.Text; +using System.Threading; +using System.Threading.Tasks; using Xunit; namespace System.Xml.Tests @@ -121,5 +123,64 @@ public static void XmlWriterDisposeWorksWithDerivingClasses() mywriter.Dispose(); Assert.True(mywriter.IsDisposed); } + + [Fact] + public static async Task AsyncWriterDispose_ShouldCall_FlushAsyncWriteAsyncOnly_UtfWriter() + { + using (var stream = new ForceAsyncSyncStream()) + await using (var writer = XmlWriter.Create(stream, new XmlWriterSettings() { Async = true })) + { + await writer.WriteStartDocumentAsync(); + await writer.WriteStartElementAsync(string.Empty, "root", null); + await writer.WriteStartElementAsync(null, "test", null); + await writer.WriteAttributeStringAsync(string.Empty, "abc", string.Empty, "1"); + await writer.WriteEndElementAsync(); + await writer.WriteEndElementAsync(); + } + } + + [Fact] + public static async Task AsyncWriterDispose_ShouldCall_FlushAsyncWriteAsyncOnly_TextWriter() + { + using (var sw = new AsyncOnlyWriter()) + await using (var writer = XmlWriter.Create(sw, new XmlWriterSettings() { Async = true })) + { + await writer.WriteStartElementAsync(null, "book", null); + await writer.WriteElementStringAsync(null, "price", null, "19.95"); + await writer.WriteEndElementAsync(); + } + } + + internal class AsyncOnlyWriter : StringWriter + { + public override void Flush() + { + Assert.True(false, "Sync operation not allowed"); + base.Flush(); + } + } + + internal class ForceAsyncSyncStream : MemoryStream + { + public override void Flush() + { + Assert.True(false, "Sync operation not allowed"); + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public override void Write(byte[] buffer, int offset, int count) + { + Assert.True(false, "Sync operation not allowed"); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } } } diff --git a/src/libraries/System.Private.Xml/tests/XmlWriter/WriteWithEncoding.cs b/src/libraries/System.Private.Xml/tests/XmlWriter/WriteWithEncoding.cs index b764701630e65..549e714246d38 100644 --- a/src/libraries/System.Private.Xml/tests/XmlWriter/WriteWithEncoding.cs +++ b/src/libraries/System.Private.Xml/tests/XmlWriter/WriteWithEncoding.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Threading.Tasks; using System.Xml.Xsl; using Xunit; @@ -71,5 +72,37 @@ public void WriteWithUtf32EncodingNoBom() // Then, last '>' will be cut off in resulting string if BOM is present Assert.Equal("", string.Concat(resultString.Take(39))); } + + [Fact] + public static async Task AsyncSyncWrite_StreamResult_ShouldMatch() + { + using (var syncStream = new MemoryStream()) + using (var asyncStream = new MemoryStream()) + { + await using (var writer = XmlWriter.Create(asyncStream, new XmlWriterSettings() { Async = true })) + { + await writer.WriteStartDocumentAsync(); + await writer.WriteStartElementAsync(string.Empty, "root", null); + await writer.WriteStartElementAsync(null, "test", null); + await writer.WriteAttributeStringAsync(string.Empty, "abc", string.Empty, "1"); + await writer.WriteStringAsync("value"); + await writer.WriteEndElementAsync(); + await writer.WriteEndElementAsync(); + } + + using (var writer = XmlWriter.Create(syncStream, new XmlWriterSettings() { Async = false })) + { + writer.WriteStartDocument(); + writer.WriteStartElement("root"); + writer.WriteStartElement("test"); + writer.WriteAttributeString("abc", "1"); + writer.WriteString("value"); + writer.WriteEndElement(); + writer.WriteEndElement(); + } + + Assert.Equal(syncStream.ToArray(), asyncStream.ToArray()); + } + } } } diff --git a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs index 83fa6c584289c..fbb169f86bffa 100644 --- a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs +++ b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs @@ -5,6 +5,8 @@ // Changes to this file must follow the https://aka.ms/api-review process. // ------------------------------------------------------------------------------ +using System.Threading.Tasks; + namespace System.Xml { public enum ConformanceLevel @@ -1201,7 +1203,7 @@ public partial class XmlWhitespace : System.Xml.XmlCharacterData public override void WriteContentTo(System.Xml.XmlWriter w) { } public override void WriteTo(System.Xml.XmlWriter w) { } } - public abstract partial class XmlWriter : System.IDisposable + public abstract partial class XmlWriter : System.IDisposable, System.IAsyncDisposable { protected XmlWriter() { } public virtual System.Xml.XmlWriterSettings Settings { get { throw null; } } @@ -1306,6 +1308,7 @@ public virtual void WriteValue(float value) { } public virtual void WriteValue(string value) { } public abstract void WriteWhitespace(string ws); public virtual System.Threading.Tasks.Task WriteWhitespaceAsync(string ws) { throw null; } + public ValueTask DisposeAsync() { throw null; } } public sealed partial class XmlWriterSettings { From c38ad0be5ec0f818be9872d5112b799e1b7cc92f Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 13 Mar 2020 15:39:45 -0700 Subject: [PATCH 03/13] Reverting removing unwanted changes --- .../System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs | 2 -- .../System.Private.Xml/src/System.Private.Xml.csproj | 3 --- .../System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs | 4 +--- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs index 004eeff5af30f..ad4fe72c148de 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; @@ -186,7 +185,6 @@ public static async Task RoundtripSyncAsyncMatches_Stream(bool document, LoadOpt // Compare to make sure the synchronous and asynchronous results are the same Assert.Equal(syncOutput.ToArray(), asyncOutput.ToArray()); - Console.WriteLine(Encoding.ASCII.GetString(asyncOutput.ToArray())); } // Inputs to the Roundtrip* tests: diff --git a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj index c9c7dfdbaadc5..c0215be859644 100644 --- a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj +++ b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj @@ -807,7 +807,4 @@ - - - diff --git a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs index fbb169f86bffa..5fd634a57db80 100644 --- a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs +++ b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs @@ -5,8 +5,6 @@ // Changes to this file must follow the https://aka.ms/api-review process. // ------------------------------------------------------------------------------ -using System.Threading.Tasks; - namespace System.Xml { public enum ConformanceLevel @@ -1308,7 +1306,7 @@ public virtual void WriteValue(float value) { } public virtual void WriteValue(string value) { } public abstract void WriteWhitespace(string ws); public virtual System.Threading.Tasks.Task WriteWhitespaceAsync(string ws) { throw null; } - public ValueTask DisposeAsync() { throw null; } + public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } } public sealed partial class XmlWriterSettings { From cd59e80be74f9bf7c3ed7730a06dd6c649b4f781 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 20 Apr 2020 18:47:45 -0700 Subject: [PATCH 04/13] Apply API review result --- .../src/System/Xml/Core/XmlAsyncCheckWriter.cs | 18 ++++-------------- .../Xml/Core/XmlEncodedRawTextWriterAsync.cs | 15 ++++++++------- .../XmlRawTextWriterGeneratorAsync.ttinclude | 3 ++- .../src/System/Xml/Core/XmlRawWriterAsync.cs | 4 ++-- .../Xml/Core/XmlUtf8RawTextWriterAsync.cs | 3 ++- .../Xml/Core/XmlWellFormedWriterAsync.cs | 6 +++--- .../src/System/Xml/Core/XmlWriterAsync.cs | 7 ++++--- .../ref/System.Xml.ReaderWriter.cs | 1 + 8 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs index 3f270b3d128b0..a2f685f6a33e6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs @@ -330,17 +330,6 @@ public override void WriteNode(XPathNavigator navigator, bool defattr) _coreWriter.WriteNode(navigator, defattr); } - protected override void Dispose(bool disposing) - { - if (disposing) - { - CheckAsync(); - //since it is protected method, we can't call coreWriter.Dispose(disposing). - //Internal, it is always called to Dispose(true). So call coreWriter.Dispose() is OK. - _coreWriter.Dispose(); - } - } - #endregion #region Async Methods @@ -577,11 +566,12 @@ public override Task WriteNodeAsync(XPathNavigator navigator, bool defattr) return task; } - public override ValueTask CloseAsync() + protected override ValueTask DisposeAsyncCore() { - CheckAsync(); - return _coreWriter.CloseAsync(); + CheckAsync(); + return _coreWriter.DisposeAsync(); // _coreWriter.DisposeAsyncCoreAsync(); } + #endregion } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs index 970ba4cf5b047..03b9d7a4c3109 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs @@ -73,7 +73,7 @@ internal override Task WriteXmlDeclarationAsync(string xmldecl) return Task.CompletedTask; } - public override async ValueTask CloseAsync() + protected override async ValueTask DisposeAsyncCore() { try { @@ -127,6 +127,7 @@ public override async ValueTask CloseAsync() } } } + GC.SuppressFinalize(this); } // Serialize the document type declaration. @@ -818,7 +819,7 @@ protected unsafe int WriteAttributeTextBlockNoFlush(char* pSrc, char* pSrcEnd) pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, true); pSrc++; @@ -1022,7 +1023,7 @@ protected unsafe int WriteElementTextBlockNoFlush(char* pSrc, char* pSrcEnd, out pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, true); pSrc++; @@ -1206,7 +1207,7 @@ protected unsafe int RawTextNoFlush(char* pSrcBegin, char* pSrcEnd) pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; @@ -1425,7 +1426,7 @@ protected unsafe int WriteRawWithCharCheckingNoFlush(char* pSrcBegin, char* pSrc pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; @@ -1656,7 +1657,7 @@ protected unsafe int WriteCommentOrPiNoFlush(string text, int index, int count, pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; @@ -1840,7 +1841,7 @@ protected unsafe int WriteCDataSectionNoFlush(string text, int index, int count, pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude index 7bc67f77470b0..d8927c9c7aa47 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude @@ -75,7 +75,7 @@ namespace System.Xml return Task.CompletedTask; } - public override async ValueTask CloseAsync() + protected override async ValueTask DisposeAsyncCore() { try { @@ -131,6 +131,7 @@ namespace System.Xml } <# } #> } + GC.SuppressFinalize(this); } // Serialize the document type declaration. diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriterAsync.cs index ebc14c56f24d0..b602201b1eb20 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawWriterAsync.cs @@ -227,9 +227,9 @@ internal virtual Task WriteEndBase64Async() return base64Encoder.FlushAsync(); } - internal virtual ValueTask CloseAsync(WriteState currentState) + internal virtual ValueTask DisposeAsyncCore(WriteState currentState) { - return CloseAsync(); + return DisposeAsyncCore(); } } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs index 2105651638232..2521ce03c22c3 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs @@ -72,7 +72,7 @@ internal override Task WriteXmlDeclarationAsync(string xmldecl) return Task.CompletedTask; } - public override async ValueTask CloseAsync() + protected override async ValueTask DisposeAsyncCore() { try { @@ -105,6 +105,7 @@ public override async ValueTask CloseAsync() } } } + GC.SuppressFinalize(this); } // Serialize the document type declaration. diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterAsync.cs index 61b9121081ad6..44e396c8a89fb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterAsync.cs @@ -1473,7 +1473,7 @@ private Task StartElementContentAsync() return Task.CompletedTask; } - public override async ValueTask CloseAsync() + protected override async ValueTask DisposeAsyncCore() { if (_currentState != State.Closed) { @@ -1516,11 +1516,11 @@ public override async ValueTask CloseAsync() { if (_rawWriter != null) { - await _rawWriter.CloseAsync(WriteState).ConfigureAwait(false); + await _rawWriter.DisposeAsyncCore(WriteState).ConfigureAwait(false); } else { - await _writer.CloseAsync().ConfigureAwait(false); + await _writer.DisposeAsync().ConfigureAwait(false); } } finally diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs index bf444b2b9a199..e30f9d08924d5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs @@ -604,15 +604,16 @@ public ValueTask DisposeAsync() { if (WriteState != WriteState.Closed) { - return CloseAsync(); + return DisposeAsyncCore(); } return default; } - public virtual ValueTask CloseAsync() + protected virtual ValueTask DisposeAsyncCore() { - Close(); + Dispose(true); + GC.SuppressFinalize(this); return default; } } diff --git a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs index 5fd634a57db80..8eab71f3d4251 100644 --- a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs +++ b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs @@ -1307,6 +1307,7 @@ public virtual void WriteValue(string value) { } public abstract void WriteWhitespace(string ws); public virtual System.Threading.Tasks.Task WriteWhitespaceAsync(string ws) { throw null; } public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } + protected virtual System.Threading.Tasks.ValueTask DisposeAsyncCore() { throw null; } } public sealed partial class XmlWriterSettings { From 3ff296725e7733c76c651a2bc139ede7bd3fb1bc Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 20 Apr 2020 23:00:38 -0700 Subject: [PATCH 05/13] Small fixes and count check for write when disposing --- .../tests/misc/LoadSaveAsyncTests.cs | 1 + .../System/Xml/Core/XmlAsyncCheckWriter.cs | 2 +- .../Xml/Core/XmlEncodedRawTextWriter.cs | 7 +- .../Xml/Core/XmlEncodedRawTextWriterAsync.cs | 19 ++-- .../Core/XmlRawTextWriterGenerator.ttinclude | 14 ++- .../XmlRawTextWriterGeneratorAsync.ttinclude | 14 ++- .../System/Xml/Core/XmlUtf8RawTextWriter.cs | 7 +- .../Xml/Core/XmlUtf8RawTextWriterAsync.cs | 7 +- .../tests/XmlWriter/DisposeTests.cs | 93 ++++++++++++++++++- 9 files changed, 140 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs index ad4fe72c148de..505fcd010c11c 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs @@ -201,6 +201,7 @@ public static IEnumerable RoundtripOptions_MemberData yield return new object[] { doc, loadOptions, saveOptions }; } } + public static IEnumerable IsAsync_SaveOptions_Data { get diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs index a2f685f6a33e6..2b37ea1c446ae 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs @@ -569,7 +569,7 @@ public override Task WriteNodeAsync(XPathNavigator navigator, bool defattr) protected override ValueTask DisposeAsyncCore() { CheckAsync(); - return _coreWriter.DisposeAsync(); // _coreWriter.DisposeAsyncCoreAsync(); + return _coreWriter.DisposeAsync(); } #endregion diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs index 5e170f0d817bf..713f5bcc262af 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs @@ -823,8 +823,11 @@ protected virtual void FlushBuffer() } else { - // Write text to TextWriter - writer.Write(bufChars, 1, bufPos - 1); + if (bufPos - 1 > 0) + { + // Write text to TextWriter + writer.Write(bufChars, 1, bufPos - 1); + } } } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs index 03b9d7a4c3109..2356a6c6bf913 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs @@ -643,8 +643,11 @@ protected virtual async Task FlushBufferAsync() } else { - // Write text to TextWriter - await writer.WriteAsync(bufChars, 1, bufPos - 1).ConfigureAwait(false); + if (bufPos - 1 > 0) + { + // Write text to TextWriter + await writer.WriteAsync(bufChars, 1, bufPos - 1).ConfigureAwait(false); + } } } } @@ -819,7 +822,7 @@ protected unsafe int WriteAttributeTextBlockNoFlush(char* pSrc, char* pSrcEnd) pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, true); pSrc++; @@ -1023,7 +1026,7 @@ protected unsafe int WriteElementTextBlockNoFlush(char* pSrc, char* pSrcEnd, out pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, true); pSrc++; @@ -1207,7 +1210,7 @@ protected unsafe int RawTextNoFlush(char* pSrcBegin, char* pSrcEnd) pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; @@ -1426,7 +1429,7 @@ protected unsafe int WriteRawWithCharCheckingNoFlush(char* pSrcBegin, char* pSrc pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; @@ -1657,7 +1660,7 @@ protected unsafe int WriteCommentOrPiNoFlush(string text, int index, int count, pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; @@ -1841,7 +1844,7 @@ protected unsafe int WriteCDataSectionNoFlush(string text, int index, int count, pSrc += 2; } /* Invalid XML character */ - else if (ch <= 0x7F || ch >= 0xFFFE) + else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude index 99c9681661fb7..10464300bf5a7 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGenerator.ttinclude @@ -820,8 +820,11 @@ namespace System.Xml if (!writeToNull) { <# if (WriterType == RawTextWriterType.Utf8) { #> - Debug.Assert(stream != null); - stream.Write(<#= BufferName #>, 1, bufPos - 1); + if (bufPos - 1 > 0) + { + Debug.Assert(stream != null); + stream.Write(<#= BufferName #>, 1, bufPos - 1); + } <# } else { #> Debug.Assert(stream != null || writer != null); @@ -849,8 +852,11 @@ namespace System.Xml } else { - // Write text to TextWriter - writer.Write(<#= BufferName #>, 1, bufPos - 1); + if (bufPos - 1 > 0) + { + // Write text to TextWriter + writer.Write(<#= BufferName #>, 1, bufPos - 1); + } } <# } #> } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude index d8927c9c7aa47..2c1778c05898e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude @@ -622,8 +622,11 @@ namespace System.Xml if (!writeToNull) { <# if (WriterType == RawTextWriterType.Utf8) { #> - Debug.Assert(stream != null); - await stream.WriteAsync(bufBytes, 1, bufPos - 1).ConfigureAwait(false); + if (bufPos - 1 > 0) + { + Debug.Assert(stream != null); + await stream.WriteAsync(bufBytes, 1, bufPos - 1).ConfigureAwait(false); + } <# } else { #> Debug.Assert(stream != null || writer != null); @@ -651,8 +654,11 @@ namespace System.Xml } else { - // Write text to TextWriter - await writer.WriteAsync(<#= BufferName #>, 1, bufPos - 1).ConfigureAwait(false); + if (bufPos - 1 > 0) + { + // Write text to TextWriter + await writer.WriteAsync(<#= BufferName #>, 1, bufPos - 1).ConfigureAwait(false); + } } <# } #> } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs index d989bf2e9cd8b..3781b016706e5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs @@ -670,8 +670,11 @@ protected virtual void FlushBuffer() // Output all characters (except for previous characters stored at beginning of buffer) if (!writeToNull) { - Debug.Assert(stream != null); - stream.Write(bufBytes, 1, bufPos - 1); + if (bufPos - 1 > 0) + { + Debug.Assert(stream != null); + stream.Write(bufBytes, 1, bufPos - 1); + } } } catch diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs index 2521ce03c22c3..683b7824f2059 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs @@ -550,8 +550,11 @@ protected virtual async Task FlushBufferAsync() // Output all characters (except for previous characters stored at beginning of buffer) if (!writeToNull) { - Debug.Assert(stream != null); - await stream.WriteAsync(bufBytes, 1, bufPos - 1).ConfigureAwait(false); + if (bufPos - 1 > 0) + { + Debug.Assert(stream != null); + await stream.WriteAsync(bufBytes, 1, bufPos - 1).ConfigureAwait(false); + } } } catch diff --git a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs index 4205e8a07a3be..9fb9a25cbc7c9 100644 --- a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs @@ -125,7 +125,7 @@ public static void XmlWriterDisposeWorksWithDerivingClasses() } [Fact] - public static async Task AsyncWriterDispose_ShouldCall_FlushAsyncWriteAsyncOnly_UtfWriter() + public static async Task AsyncWriter_DisposeAsync_ShouldCall_FlushAsyncWriteAsyncOnly_StreamWriter() { using (var stream = new ForceAsyncSyncStream()) await using (var writer = XmlWriter.Create(stream, new XmlWriterSettings() { Async = true })) @@ -137,6 +137,50 @@ public static async Task AsyncWriterDispose_ShouldCall_FlushAsyncWriteAsyncOnly_ await writer.WriteEndElementAsync(); await writer.WriteEndElementAsync(); } + + } + + [Fact] + public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_StreamWriter() + { + using (var stream1 = new MemoryStream()) + using (var stream2 = new MemoryStream()) + using (var stream3 = new MemoryStream()) + { + using (var asyncWriter = XmlWriter.Create(stream1, new XmlWriterSettings() { Async = true })) + { + await asyncWriter.WriteStartDocumentAsync(); + await asyncWriter.WriteStartElementAsync(string.Empty, "root", null); + await asyncWriter.WriteStartElementAsync(null, "test", null); + await asyncWriter.WriteAttributeStringAsync(string.Empty, "abc", string.Empty, "1"); + await asyncWriter.WriteEndElementAsync(); + await asyncWriter.WriteEndElementAsync(); + } + + await using (var asyncWriter = XmlWriter.Create(stream2, new XmlWriterSettings() { Async = true })) + { + await asyncWriter.WriteStartDocumentAsync(); + await asyncWriter.WriteStartElementAsync(string.Empty, "root", null); + await asyncWriter.WriteStartElementAsync(null, "test", null); + await asyncWriter.WriteAttributeStringAsync(string.Empty, "abc", string.Empty, "1"); + await asyncWriter.WriteEndElementAsync(); + await asyncWriter.WriteEndElementAsync(); + } + + using (var syncWriter = XmlWriter.Create(stream3, new XmlWriterSettings() { Async = false })) + { + syncWriter.WriteStartDocument(); + syncWriter.WriteStartElement(string.Empty, "root", null); + syncWriter.WriteStartElement(null, "test", null); + syncWriter.WriteAttributeString(string.Empty, "abc", string.Empty, "1"); + syncWriter.WriteEndElement(); + syncWriter.WriteEndElement(); + } + + Assert.Equal(Encoding.UTF8.GetString(stream1.GetBuffer()), Encoding.UTF8.GetString(stream2.GetBuffer())); + Assert.Equal(Encoding.UTF8.GetString(stream2.GetBuffer()), Encoding.UTF8.GetString(stream3.GetBuffer())); + } + } [Fact] @@ -151,6 +195,39 @@ public static async Task AsyncWriterDispose_ShouldCall_FlushAsyncWriteAsyncOnly_ } } + [Fact] + public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_TextWriter() + { + using (var sw1 = new StringWriter()) + using (var sw2 = new StringWriter()) + using (var sw3 = new StringWriter()) + { + using (var asyncWriter = XmlWriter.Create(sw1, new XmlWriterSettings() { Async = true })) + { + await asyncWriter.WriteStartElementAsync(null, "book", null); + await asyncWriter.WriteElementStringAsync(null, "price", null, "19.95"); + await asyncWriter.WriteEndElementAsync(); + } + + await using (var asyncWriter = XmlWriter.Create(sw2, new XmlWriterSettings() { Async = true })) + { + await asyncWriter.WriteStartElementAsync(null, "book", null); + await asyncWriter.WriteElementStringAsync(null, "price", null, "19.95"); + await asyncWriter.WriteEndElementAsync(); + } + + using (var syncWriter = XmlWriter.Create(sw3, new XmlWriterSettings() { Async = false })) + { + syncWriter.WriteStartElement(null, "book", null); + syncWriter.WriteElementString(null, "price", null, "19.95"); + syncWriter.WriteEndElement(); + } + + Assert.Equal(sw1.ToString(), sw2.ToString()); + Assert.Equal(sw1.ToString(), sw3.ToString()); + } + } + internal class AsyncOnlyWriter : StringWriter { public override void Flush() @@ -158,6 +235,20 @@ public override void Flush() Assert.True(false, "Sync operation not allowed"); base.Flush(); } + public override Task FlushAsync() + { + return Task.CompletedTask; + } + + public override void Write(char[] buffer, int offset, int count) + { + Assert.True(false, "Sync operation not allowed"); + } + + public override Task WriteAsync(char[] buffer, int offset, int count) + { + return Task.CompletedTask; + } } internal class ForceAsyncSyncStream : MemoryStream From 0269d250d08ddc154340feb1f7b561316769593f Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 21 Apr 2020 11:25:51 -0700 Subject: [PATCH 06/13] Applying feedback --- .../src/System/Xml/Linq/XDocument.cs | 6 ++++-- .../src/System/Xml/Linq/XElement.cs | 6 ++++-- .../tests/misc/LoadSaveAsyncTests.cs | 4 ++-- .../src/System/Xml/Core/XmlAsyncCheckWriter.cs | 11 +++++++++++ .../src/System/Xml/Core/XmlWriterAsync.cs | 14 ++++++-------- 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs index 91315cb8589f7..55cea1d11b17d 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs @@ -633,7 +633,8 @@ public async Task SaveAsync(Stream stream, SaveOptions options, CancellationToke } } - await using (XmlWriter w = XmlWriter.Create(stream, ws)) + XmlWriter w = XmlWriter.Create(stream, ws); + await using (w.ConfigureAwait(false)) { await WriteToAsync(w, cancellationToken).ConfigureAwait(false); await w.FlushAsync().ConfigureAwait(false); @@ -706,7 +707,8 @@ public async Task SaveAsync(TextWriter textWriter, SaveOptions options, Cancella ws.Async = true; - await using (XmlWriter w = XmlWriter.Create(textWriter, ws)) + XmlWriter w = XmlWriter.Create(textWriter, ws); + await using (w.ConfigureAwait(false)) { await WriteToAsync(w, cancellationToken).ConfigureAwait(false); await w.FlushAsync().ConfigureAwait(false); diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs index 48442ca3a1b7f..e71ac289188d7 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs @@ -1080,7 +1080,8 @@ public async Task SaveAsync(Stream stream, SaveOptions options, CancellationToke ws.Async = true; - await using (XmlWriter w = XmlWriter.Create(stream, ws)) + XmlWriter w = XmlWriter.Create(stream, ws); + await using (w.ConfigureAwait(false)) { await SaveAsync(w, cancellationToken).ConfigureAwait(false); } @@ -1141,7 +1142,8 @@ public async Task SaveAsync(TextWriter textWriter, SaveOptions options, Cancella ws.Async = true; - await using (XmlWriter w = XmlWriter.Create(textWriter, ws)) + XmlWriter w = XmlWriter.Create(textWriter, ws); + await using (w.ConfigureAwait(false)) { await SaveAsync(w, cancellationToken).ConfigureAwait(false); } diff --git a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs index 505fcd010c11c..ad7fb4e1cb940 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs @@ -222,8 +222,8 @@ public async Task SaveAsync_CallsAsyncOnly_SaveSync_CallsSyncOnly(bool isAsync, { if (isAsync) { - await document.SaveAsync(stream, saveOptions, CancellationToken.None).ConfigureAwait(false); - await element.SaveAsync(stream, saveOptions, CancellationToken.None).ConfigureAwait(false); + await document.SaveAsync(stream, saveOptions, CancellationToken.None); + await element.SaveAsync(stream, saveOptions, CancellationToken.None); } else { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs index 2b37ea1c446ae..78dc6ac0bde4e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs @@ -330,6 +330,17 @@ public override void WriteNode(XPathNavigator navigator, bool defattr) _coreWriter.WriteNode(navigator, defattr); } + protected override void Dispose(bool disposing) + { + if (disposing) + { + CheckAsync(); + //since it is protected method, we can't call coreWriter.Dispose(disposing). + //Internal, it is always called to Dispose(true). So call coreWriter.Dispose() is OK. + _coreWriter.Dispose(); + } + } + #endregion #region Async Methods diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs index e30f9d08924d5..d67493166d9c0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs @@ -602,18 +602,16 @@ private async Task WriteLocalNamespacesAsync(XPathNavigator nsNav) public ValueTask DisposeAsync() { - if (WriteState != WriteState.Closed) - { - return DisposeAsyncCore(); - } - - return default; + GC.SuppressFinalize(this); + return DisposeAsyncCore(); } protected virtual ValueTask DisposeAsyncCore() { - Dispose(true); - GC.SuppressFinalize(this); + if (WriteState != WriteState.Closed) + { + Dispose(true); + } return default; } } From 01979cb163636e68fe873dcc6548f95c7efc8077 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 21 Apr 2020 13:14:39 -0700 Subject: [PATCH 07/13] Remove GC.SuppressFinilize() from overrides --- .../src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs | 1 - .../src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude | 1 - .../src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs index 2356a6c6bf913..13a694c1f98aa 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs @@ -127,7 +127,6 @@ protected override async ValueTask DisposeAsyncCore() } } } - GC.SuppressFinalize(this); } // Serialize the document type declaration. diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude index 2c1778c05898e..52032722d5d9a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlRawTextWriterGeneratorAsync.ttinclude @@ -131,7 +131,6 @@ namespace System.Xml } <# } #> } - GC.SuppressFinalize(this); } // Serialize the document type declaration. diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs index 683b7824f2059..d4916eb83445a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs @@ -105,7 +105,6 @@ protected override async ValueTask DisposeAsyncCore() } } } - GC.SuppressFinalize(this); } // Serialize the document type declaration. From 6c654263f18f7ddab4fbca3a28ce9990f19f6292 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 21 Apr 2020 14:13:24 -0700 Subject: [PATCH 08/13] More feedback --- .../System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs index d67493166d9c0..ea052e68a3ead 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs @@ -600,10 +600,11 @@ private async Task WriteLocalNamespacesAsync(XPathNavigator nsNav) } } - public ValueTask DisposeAsync() + public async ValueTask DisposeAsync() { + await DisposeAsyncCore().ConfigureAwait(false); + Dispose(false); GC.SuppressFinalize(this); - return DisposeAsyncCore(); } protected virtual ValueTask DisposeAsyncCore() From 75704a9804fee843cd90c818e58cd6515b11ad20 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 23 Apr 2020 16:25:53 -0700 Subject: [PATCH 09/13] Applying feedback --- .../System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs | 1 + .../System.Private.Xml/tests/XmlWriter/DisposeTests.cs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs index ad7fb4e1cb940..6c87c1fe2f8e6 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs @@ -242,6 +242,7 @@ public ForceSyncAsyncStream(bool async) { _isAsync = async; } + public override void Flush() { Assert.False(_isAsync, "Sync operation not allowed"); diff --git a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs index 9fb9a25cbc7c9..6a85f5f3cfd54 100644 --- a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs @@ -137,7 +137,6 @@ public static async Task AsyncWriter_DisposeAsync_ShouldCall_FlushAsyncWriteAsyn await writer.WriteEndElementAsync(); await writer.WriteEndElementAsync(); } - } [Fact] @@ -180,7 +179,6 @@ public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_Str Assert.Equal(Encoding.UTF8.GetString(stream1.GetBuffer()), Encoding.UTF8.GetString(stream2.GetBuffer())); Assert.Equal(Encoding.UTF8.GetString(stream2.GetBuffer()), Encoding.UTF8.GetString(stream3.GetBuffer())); } - } [Fact] @@ -233,8 +231,8 @@ internal class AsyncOnlyWriter : StringWriter public override void Flush() { Assert.True(false, "Sync operation not allowed"); - base.Flush(); } + public override Task FlushAsync() { return Task.CompletedTask; From d2eb4c053879f7e7420e54da64bad64cd945c5ee Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 23 Apr 2020 17:56:49 -0700 Subject: [PATCH 10/13] Update test --- .../System.Private.Xml/tests/XmlWriter/DisposeTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs index 6a85f5f3cfd54..7bbed0142264b 100644 --- a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs @@ -176,8 +176,12 @@ public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_Str syncWriter.WriteEndElement(); } + Assert.Equal(@"?", Encoding.UTF8.GetString(stream1.GetBuffer())); Assert.Equal(Encoding.UTF8.GetString(stream1.GetBuffer()), Encoding.UTF8.GetString(stream2.GetBuffer())); Assert.Equal(Encoding.UTF8.GetString(stream2.GetBuffer()), Encoding.UTF8.GetString(stream3.GetBuffer())); + + Assert.Equal(stream1.GetBuffer(), stream2.GetBuffer()); + Assert.Equal(stream2.GetBuffer(), stream3.GetBuffer()); } } From 2f04d5dcf5665ccc2eba1a8c5d522f2e7ddac351 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 24 Apr 2020 17:18:37 -0700 Subject: [PATCH 11/13] Using UTF8Encoding instead of Encoding.Utf for reading as string --- .../tests/XmlWriter/DisposeTests.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs index 7bbed0142264b..3c36792b3d783 100644 --- a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs @@ -142,11 +142,12 @@ public static async Task AsyncWriter_DisposeAsync_ShouldCall_FlushAsyncWriteAsyn [Fact] public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_StreamWriter() { + var settings = new XmlWriterSettings() { Async = true, Encoding = new UTF8Encoding(false) }; using (var stream1 = new MemoryStream()) using (var stream2 = new MemoryStream()) using (var stream3 = new MemoryStream()) { - using (var asyncWriter = XmlWriter.Create(stream1, new XmlWriterSettings() { Async = true })) + using (var asyncWriter = XmlWriter.Create(stream1, settings)) { await asyncWriter.WriteStartDocumentAsync(); await asyncWriter.WriteStartElementAsync(string.Empty, "root", null); @@ -154,9 +155,10 @@ public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_Str await asyncWriter.WriteAttributeStringAsync(string.Empty, "abc", string.Empty, "1"); await asyncWriter.WriteEndElementAsync(); await asyncWriter.WriteEndElementAsync(); + Console.WriteLine(asyncWriter.Settings.Encoding); } - await using (var asyncWriter = XmlWriter.Create(stream2, new XmlWriterSettings() { Async = true })) + await using (var asyncWriter = XmlWriter.Create(stream2, settings)) { await asyncWriter.WriteStartDocumentAsync(); await asyncWriter.WriteStartElementAsync(string.Empty, "root", null); @@ -166,7 +168,8 @@ public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_Str await asyncWriter.WriteEndElementAsync(); } - using (var syncWriter = XmlWriter.Create(stream3, new XmlWriterSettings() { Async = false })) + settings.Async = false; + using (var syncWriter = XmlWriter.Create(stream3, settings)) { syncWriter.WriteStartDocument(); syncWriter.WriteStartElement(string.Empty, "root", null); @@ -176,15 +179,17 @@ public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_Str syncWriter.WriteEndElement(); } - Assert.Equal(@"?", Encoding.UTF8.GetString(stream1.GetBuffer())); - Assert.Equal(Encoding.UTF8.GetString(stream1.GetBuffer()), Encoding.UTF8.GetString(stream2.GetBuffer())); - Assert.Equal(Encoding.UTF8.GetString(stream2.GetBuffer()), Encoding.UTF8.GetString(stream3.GetBuffer())); - Assert.Equal(stream1.GetBuffer(), stream2.GetBuffer()); Assert.Equal(stream2.GetBuffer(), stream3.GetBuffer()); + + Assert.Equal(@"", readAsString(stream1.GetBuffer(), stream1.Length)); + Assert.Equal(@"", readAsString(stream2.GetBuffer(), stream1.Length)); + Assert.Equal(@"", readAsString(stream3.GetBuffer(), stream1.Length)); } } + private static string readAsString(byte[] bytes, long length) => new UTF8Encoding().GetString(bytes, 0, (int)length); + [Fact] public static async Task AsyncWriterDispose_ShouldCall_FlushAsyncWriteAsyncOnly_TextWriter() { From c8a52f0509baac825a0801669e0880c340121285 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 27 Apr 2020 14:07:07 -0700 Subject: [PATCH 12/13] Applying feedback --- .../tests/misc/LoadSaveAsyncTests.cs | 8 +++---- .../tests/XmlWriter/DisposeTests.cs | 21 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs index 6c87c1fe2f8e6..0e436827430fe 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs @@ -245,25 +245,25 @@ public ForceSyncAsyncStream(bool async) public override void Flush() { - Assert.False(_isAsync, "Sync operation not allowed"); + Assert.False(_isAsync, "Stream is in asynchronous mode when synchronous Flush is called"); base.Flush(); } public override Task FlushAsync(CancellationToken cancellationToken) { - Assert.True(_isAsync, "Async operation not allowed"); + Assert.True(_isAsync, "Stream is not in asynchronous mode when asynchronous Flush is called"); return Task.CompletedTask; } public override void Write(byte[] buffer, int offset, int count) { - Assert.False(_isAsync, "Sync operation not allowed"); + Assert.False(_isAsync, "Stream is in asynchronous mode when synchronous Write is called"); base.Write(buffer, offset, count); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - Assert.True(_isAsync, "Async operation not allowed"); + Assert.True(_isAsync, "Stream is not in asynchronous mode when asynchronous Write is called"); return Task.CompletedTask; } } diff --git a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs index 3c36792b3d783..339ebfd750fbb 100644 --- a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs @@ -127,7 +127,7 @@ public static void XmlWriterDisposeWorksWithDerivingClasses() [Fact] public static async Task AsyncWriter_DisposeAsync_ShouldCall_FlushAsyncWriteAsyncOnly_StreamWriter() { - using (var stream = new ForceAsyncSyncStream()) + using (var stream = new AsyncOnlyStream()) await using (var writer = XmlWriter.Create(stream, new XmlWriterSettings() { Async = true })) { await writer.WriteStartDocumentAsync(); @@ -155,7 +155,6 @@ public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_Str await asyncWriter.WriteAttributeStringAsync(string.Empty, "abc", string.Empty, "1"); await asyncWriter.WriteEndElementAsync(); await asyncWriter.WriteEndElementAsync(); - Console.WriteLine(asyncWriter.Settings.Encoding); } await using (var asyncWriter = XmlWriter.Create(stream2, settings)) @@ -182,13 +181,13 @@ public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_Str Assert.Equal(stream1.GetBuffer(), stream2.GetBuffer()); Assert.Equal(stream2.GetBuffer(), stream3.GetBuffer()); - Assert.Equal(@"", readAsString(stream1.GetBuffer(), stream1.Length)); - Assert.Equal(@"", readAsString(stream2.GetBuffer(), stream1.Length)); - Assert.Equal(@"", readAsString(stream3.GetBuffer(), stream1.Length)); + Assert.Equal(@"", ReadAsString(stream1.GetBuffer(), stream1.Length)); + Assert.Equal(@"", ReadAsString(stream2.GetBuffer(), stream1.Length)); + Assert.Equal(@"", ReadAsString(stream3.GetBuffer(), stream1.Length)); } } - private static string readAsString(byte[] bytes, long length) => new UTF8Encoding().GetString(bytes, 0, (int)length); + private static string ReadAsString(byte[] bytes, long length) => Encoding.UTF8.GetString(bytes, 0, (int)length); [Fact] public static async Task AsyncWriterDispose_ShouldCall_FlushAsyncWriteAsyncOnly_TextWriter() @@ -239,7 +238,7 @@ internal class AsyncOnlyWriter : StringWriter { public override void Flush() { - Assert.True(false, "Sync operation not allowed"); + throw new InvalidOperationException("Sync operations are not allowed."); } public override Task FlushAsync() @@ -249,7 +248,7 @@ public override Task FlushAsync() public override void Write(char[] buffer, int offset, int count) { - Assert.True(false, "Sync operation not allowed"); + throw new InvalidOperationException("Sync operations are not allowed."); } public override Task WriteAsync(char[] buffer, int offset, int count) @@ -258,11 +257,11 @@ public override Task WriteAsync(char[] buffer, int offset, int count) } } - internal class ForceAsyncSyncStream : MemoryStream + internal class AsyncOnlyStream : MemoryStream { public override void Flush() { - Assert.True(false, "Sync operation not allowed"); + throw new InvalidOperationException("Sync operations are not allowed."); } public override Task FlushAsync(CancellationToken cancellationToken) @@ -272,7 +271,7 @@ public override Task FlushAsync(CancellationToken cancellationToken) public override void Write(byte[] buffer, int offset, int count) { - Assert.True(false, "Sync operation not allowed"); + throw new InvalidOperationException("Sync operations are not allowed."); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) From 2102d23f1639667e2b793b9bc671c20bdedff8ef Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 27 Apr 2020 14:59:52 -0700 Subject: [PATCH 13/13] Small typo --- .../System.Private.Xml/tests/XmlWriter/DisposeTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs index 339ebfd750fbb..7701003b2c186 100644 --- a/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlWriter/DisposeTests.cs @@ -182,8 +182,8 @@ public static async Task XmlWriter_AsyncSyncResult_ShouldBeSame_AfterDispose_Str Assert.Equal(stream2.GetBuffer(), stream3.GetBuffer()); Assert.Equal(@"", ReadAsString(stream1.GetBuffer(), stream1.Length)); - Assert.Equal(@"", ReadAsString(stream2.GetBuffer(), stream1.Length)); - Assert.Equal(@"", ReadAsString(stream3.GetBuffer(), stream1.Length)); + Assert.Equal(@"", ReadAsString(stream2.GetBuffer(), stream2.Length)); + Assert.Equal(@"", ReadAsString(stream3.GetBuffer(), stream3.Length)); } }