diff --git a/src/Microsoft.TestPlatform.Extensions.HtmlLogger/HtmlTransformer.cs b/src/Microsoft.TestPlatform.Extensions.HtmlLogger/HtmlTransformer.cs index af5313cf1b..d75c2e8311 100644 --- a/src/Microsoft.TestPlatform.Extensions.HtmlLogger/HtmlTransformer.cs +++ b/src/Microsoft.TestPlatform.Extensions.HtmlLogger/HtmlTransformer.cs @@ -36,7 +36,10 @@ public void Transform(string xmlFile, string htmlFile) using XmlReader xr = XmlReader.Create(xmlFile, new XmlReaderSettings() { CheckCharacters = false }); // Use output settings from the xslt, especially the output method, which is HTML, which avoids outputting broken
tags. - using XmlWriter xw = XmlWriter.Create(htmlFile, _xslTransform.OutputSettings); + // However, we also need to ensure the writer is tolerant of invalid XML characters, similar to the reader. + var outputSettings = _xslTransform.OutputSettings?.Clone() ?? new XmlWriterSettings(); + outputSettings.CheckCharacters = false; + using XmlWriter xw = XmlWriter.Create(htmlFile, outputSettings); _xslTransform.Transform(xr, xw); } diff --git a/test/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/HtmlLoggerTests.cs b/test/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/HtmlLoggerTests.cs index 821093aa91..523fb78c94 100644 --- a/test/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/HtmlLoggerTests.cs +++ b/test/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/HtmlLoggerTests.cs @@ -591,6 +591,66 @@ public void TestCompleteHandlerShouldNotDivideByZeroWhenThereAre0TestResults() Assert.AreEqual(0, _htmlLogger.TestRunDetails.Summary.PassPercentage); } + [TestMethod] + public void TestResultHandlerShouldHandleSpecialCharactersInDataRowWithoutThrowingException() + { + // This test is for the issue where HTML logger throws exception when DataRow contains special characters + var testCase = CreateTestCase("TestWithSpecialChars"); + testCase.DisplayName = "TestMethod(\"test with special chars: \\u0001\\u0002\\uFFFF\")"; + + var testResult = new ObjectModel.TestResult(testCase) + { + Outcome = TestOutcome.Passed, + DisplayName = "TestMethod(\"test with special chars: \\u0001\\u0002\\uFFFF\")", + ErrorMessage = "Error with special chars: \u0001\u0002\uFFFF" + }; + + // This should not throw an exception + try + { + _htmlLogger.TestResultHandler(new object(), new Mock(testResult).Object); + Assert.AreEqual(1, _htmlLogger.TotalTests, "Total Tests"); + } + catch (Exception ex) + { + Assert.Fail($"TestResultHandler threw an exception when handling special characters: {ex.Message}"); + } + } + + [TestMethod] + public void TestCompleteHandlerShouldHandleSpecialCharactersInDataRowDuringTransformation() + { + // Create a test case with special characters that would cause XML writing issues + var testCase = CreateTestCase("TestWithSpecialChars"); + testCase.DisplayName = "TestMethod with special characters"; + + var testResult = new ObjectModel.TestResult(testCase) + { + Outcome = TestOutcome.Failed, + DisplayName = "TestMethod(\"special chars: \u0001\u0002\")", + ErrorMessage = "Error message with special chars: \u0001\u0002\uFFFF", + ErrorStackTrace = "Stack trace with special chars: \u0001\u0002" + }; + + _mockFileHelper.Setup(x => x.GetStream(It.IsAny(), FileMode.OpenOrCreate, FileAccess.ReadWrite)) + .Returns(new MemoryStream()); + + try + { + _htmlLogger.TestResultHandler(new object(), new Mock(testResult).Object); + + // This is where the issue would typically occur - during TestRunCompleteHandler + // when XML is serialized and then transformed to HTML + _htmlLogger.TestRunCompleteHandler(new object(), new TestRunCompleteEventArgs(null, false, true, null, null, null, TimeSpan.Zero)); + + Assert.AreEqual(1, _htmlLogger.TotalTests, "Total Tests"); + } + catch (Exception ex) + { + Assert.Fail($"HTML logger threw an exception when handling special characters during transformation: {ex.Message}"); + } + } + private static TestCase CreateTestCase(string testCaseName) { return new TestCase(testCaseName, new Uri("some://uri"), "DummySourceFileName"); diff --git a/test/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/HtmlTransformerTests.cs b/test/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/HtmlTransformerTests.cs new file mode 100644 index 0000000000..7284bb5512 --- /dev/null +++ b/test/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/HtmlTransformerTests.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Text; + +using Microsoft.VisualStudio.TestPlatform.Extensions.HtmlLogger; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests; + +[TestClass] +public class HtmlTransformerTests +{ + [TestMethod] + public void TransformShouldHandleSpecialCharactersWithoutThrowingException() + { + // Create a simple XML with special characters that could cause issues + var xmlContent = @" + + + + + + Test failed with special chars: ￿ + Stack trace with special chars:  + + + + +"; + + var xmlFile = Path.GetTempFileName(); + var htmlFile = Path.ChangeExtension(xmlFile, ".html"); + + try + { + File.WriteAllText(xmlFile, xmlContent, Encoding.UTF8); + + var transformer = new HtmlTransformer(); + // This should not throw an exception, even with special characters + try + { + transformer.Transform(xmlFile, htmlFile); + } + catch (Exception ex) + { + Assert.Fail($"HtmlTransformer.Transform should not throw an exception with special characters: {ex.Message}"); + } + + // Verify that HTML file was created + Assert.IsTrue(File.Exists(htmlFile), "HTML file should be created"); + + // Verify that HTML file has content + var htmlContent = File.ReadAllText(htmlFile); + Assert.IsFalse(string.IsNullOrEmpty(htmlContent), "HTML content should not be empty"); + } + finally + { + if (File.Exists(xmlFile)) File.Delete(xmlFile); + if (File.Exists(htmlFile)) File.Delete(htmlFile); + } + } + + [TestMethod] + public void TransformShouldCreateValidHtmlOutput() + { + // Create a basic XML structure + var xmlContent = @" + + + + + Test output + + + +"; + + var xmlFile = Path.GetTempFileName(); + var htmlFile = Path.ChangeExtension(xmlFile, ".html"); + + try + { + File.WriteAllText(xmlFile, xmlContent, Encoding.UTF8); + + var transformer = new HtmlTransformer(); + transformer.Transform(xmlFile, htmlFile); + + // Verify that HTML file was created and has content + Assert.IsTrue(File.Exists(htmlFile), "HTML file should be created"); + var htmlContent = File.ReadAllText(htmlFile); + Assert.IsFalse(string.IsNullOrEmpty(htmlContent), "HTML content should not be empty"); + } + finally + { + if (File.Exists(xmlFile)) File.Delete(xmlFile); + if (File.Exists(htmlFile)) File.Delete(htmlFile); + } + } +}