diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 2b30782..8ff59ef 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -58,6 +58,18 @@ An enumeration representing possible test outcomes:
### Serialization Layer
+#### `Serializer`
+
+The `Serializer` class provides automatic format detection and deserialization:
+
+- Automatically identifies test result format (TRX or JUnit) based on XML structure
+- Delegates to the appropriate format-specific serializer
+- Simplifies usage when the input format is unknown
+- Provides a `TestResultFormat` enum with values: `Unknown`, `Trx`, and `JUnit`
+- Includes two main methods:
+ - `Identify(string contents)`: Detects the format without deserializing
+ - `Deserialize(string contents)`: Automatically detects format and deserializes
+
#### `TrxSerializer`
The `TrxSerializer` class is responsible for converting between the domain model and TRX XML format:
@@ -81,6 +93,8 @@ The `JUnitSerializer` class is responsible for converting between the domain mod
## Data Flow
+### Creating Test Results
+
```text
1. User creates TestResults instance
2. User adds TestResult objects to the Results collection
@@ -88,6 +102,15 @@ The `JUnitSerializer` class is responsible for converting between the domain mod
4. User saves the XML string to a .trx or .xml file
```
+### Reading Test Results
+
+```text
+1. User reads test result file contents
+2. User calls Serializer.Deserialize() for automatic format detection
+ OR calls TrxSerializer.Deserialize() or JUnitSerializer.Deserialize() for specific formats
+3. User receives TestResults instance with deserialized data
+```
+
## Design Patterns
- **Data Transfer Object (DTO)**: The `TestResults` and `TestResult` classes serve as DTOs for test data
@@ -148,7 +171,6 @@ Potential enhancements that could be considered:
1. **Additional Formats**: Support for other test result formats (NUnit XML, xUnit XML, etc.)
2. **Streaming**: Support for streaming large test result sets to avoid memory issues
3. **Validation**: Add schema validation to ensure generated files are well-formed
-4. **Format Detection**: Automatic detection of input format when deserializing
## Dependencies
diff --git a/README.md b/README.md
index 2057637..bc884bd 100644
--- a/README.md
+++ b/README.md
@@ -78,6 +78,29 @@ File.WriteAllText("results.trx", TrxSerializer.Serialize(results));
File.WriteAllText("results.xml", JUnitSerializer.Serialize(results));
```
+### Automatic Format Detection
+
+The library can automatically detect the format of test result files:
+
+```csharp
+using DemaConsulting.TestResults.IO;
+
+// Automatically detect and deserialize any supported format
+var testResultsXml = File.ReadAllText("test-results.xml");
+var results = Serializer.Deserialize(testResultsXml);
+
+// Or identify the format without deserializing
+var format = Serializer.Identify(testResultsXml);
+if (format == TestResultFormat.Trx)
+{
+ Console.WriteLine("This is a TRX file");
+}
+else if (format == TestResultFormat.JUnit)
+{
+ Console.WriteLine("This is a JUnit XML file");
+}
+```
+
### Converting Between Formats
The library supports reading and converting between TRX and JUnit formats:
@@ -85,19 +108,17 @@ The library supports reading and converting between TRX and JUnit formats:
```csharp
using DemaConsulting.TestResults.IO;
-// Read JUnit XML file
-var junitXml = File.ReadAllText("junit-results.xml");
-var results = JUnitSerializer.Deserialize(junitXml);
-
-// Convert to TRX format
+// Automatic format detection and conversion
+var testResultsXml = File.ReadAllText("test-results.xml");
+var results = Serializer.Deserialize(testResultsXml); // Works with TRX or JUnit
var trxXml = TrxSerializer.Serialize(results);
File.WriteAllText("converted.trx", trxXml);
-// Or convert TRX to JUnit
-var trxXml2 = File.ReadAllText("test-results.trx");
-var results2 = TrxSerializer.Deserialize(trxXml2);
-var junitXml2 = JUnitSerializer.Serialize(results2);
-File.WriteAllText("converted.xml", junitXml2);
+// Or use specific deserializers if format is known
+var junitXml = File.ReadAllText("junit-results.xml");
+var results2 = JUnitSerializer.Deserialize(junitXml);
+var trxXml2 = TrxSerializer.Serialize(results2);
+File.WriteAllText("converted-from-junit.trx", trxXml2);
```
## Advanced Usage
diff --git a/docs/guide/guide.md b/docs/guide/guide.md
index 6efad42..c2474ce 100644
--- a/docs/guide/guide.md
+++ b/docs/guide/guide.md
@@ -457,6 +457,53 @@ File.WriteAllText("metadata-results.trx", TrxSerializer.Serialize(results));
File.WriteAllText("metadata-results.xml", JUnitSerializer.Serialize(results));
```
+### Automatic Format Detection
+
+The library can automatically detect whether a test result file is in TRX or JUnit format, eliminating the need to
+manually determine the format:
+
+```csharp
+using System;
+using System.IO;
+using DemaConsulting.TestResults;
+using DemaConsulting.TestResults.IO;
+
+// Automatically detect and deserialize any supported format
+string testResultsXml = File.ReadAllText("test-results.xml");
+TestResults results = Serializer.Deserialize(testResultsXml);
+
+Console.WriteLine($"Loaded {results.Results.Count} test results");
+Console.WriteLine($"Test suite: {results.Name}");
+
+// You can also identify the format without deserializing
+string unknownFile = File.ReadAllText("unknown-format.xml");
+TestResultFormat format = Serializer.Identify(unknownFile);
+
+switch (format)
+{
+ case TestResultFormat.Trx:
+ Console.WriteLine("This is a TRX (Visual Studio) test result file");
+ break;
+ case TestResultFormat.JUnit:
+ Console.WriteLine("This is a JUnit XML test result file");
+ break;
+ case TestResultFormat.Unknown:
+ Console.WriteLine("Unknown or invalid test result format");
+ break;
+}
+```
+
+The `Serializer` class provides two key methods:
+
+- **`Identify(string contents)`**: Determines the format without deserializing the content
+- **`Deserialize(string contents)`**: Automatically detects the format and deserializes using the appropriate parser
+
+This is particularly useful when:
+
+- Processing test results from different CI/CD systems
+- Building tools that handle multiple formats
+- You don't know in advance which format will be provided
+
### Converting Between Formats
The library makes it easy to convert test results between TRX and JUnit formats:
@@ -467,18 +514,24 @@ using System.IO;
using DemaConsulting.TestResults;
using DemaConsulting.TestResults.IO;
-// Convert JUnit to TRX
+// Automatic format detection and conversion
+string testResultsXml = File.ReadAllText("test-results.xml");
+TestResults results = Serializer.Deserialize(testResultsXml); // Works with either format
+string trxXml = TrxSerializer.Serialize(results);
+File.WriteAllText("converted-to-trx.trx", trxXml);
+Console.WriteLine("Converted to TRX format");
+
+// Or use format-specific deserializers if you know the format
string junitXml = File.ReadAllText("original-junit-results.xml");
TestResults resultsFromJUnit = JUnitSerializer.Deserialize(junitXml);
-string trxXml = TrxSerializer.Serialize(resultsFromJUnit);
-File.WriteAllText("converted-to-trx.trx", trxXml);
+string trxXml2 = TrxSerializer.Serialize(resultsFromJUnit);
+File.WriteAllText("converted-junit-to-trx.trx", trxXml2);
Console.WriteLine("Converted JUnit to TRX format");
-// Convert TRX to JUnit
-string trxXml2 = File.ReadAllText("original-trx-results.trx");
-TestResults resultsFromTrx = TrxSerializer.Deserialize(trxXml2);
+string trxInput = File.ReadAllText("original-trx-results.trx");
+TestResults resultsFromTrx = TrxSerializer.Deserialize(trxInput);
string junitXml2 = JUnitSerializer.Serialize(resultsFromTrx);
-File.WriteAllText("converted-to-junit.xml", junitXml2);
+File.WriteAllText("converted-trx-to-junit.xml", junitXml2);
Console.WriteLine("Converted TRX to JUnit format");
```
@@ -686,15 +739,15 @@ using System.Linq;
using DemaConsulting.TestResults;
using DemaConsulting.TestResults.IO;
-// Read multiple test result files
+// Read multiple test result files (automatically detect format)
var unitTestsXml = File.ReadAllText("unit-tests.xml");
var integrationTestsXml = File.ReadAllText("integration-tests.xml");
var e2eTestsTrx = File.ReadAllText("e2e-tests.trx");
-// Deserialize each file
-var unitTests = JUnitSerializer.Deserialize(unitTestsXml);
-var integrationTests = JUnitSerializer.Deserialize(integrationTestsXml);
-var e2eTests = TrxSerializer.Deserialize(e2eTestsTrx);
+// Deserialize each file (format is automatically detected)
+var unitTests = Serializer.Deserialize(unitTestsXml);
+var integrationTests = Serializer.Deserialize(integrationTestsXml);
+var e2eTests = Serializer.Deserialize(e2eTestsTrx);
// Create combined results
var combinedResults = new TestResults
@@ -831,18 +884,46 @@ Both formats can be created from scratch or converted from one to the other.
Yes! The library provides bidirectional conversion between formats:
```csharp
-// TRX to JUnit
+// Automatic format detection and conversion
+var results = Serializer.Deserialize(File.ReadAllText("test-results.xml"));
+var trxXml = TrxSerializer.Serialize(results);
+var junitXml = JUnitSerializer.Serialize(results);
+
+// Or use format-specific deserializers
var trxResults = TrxSerializer.Deserialize(File.ReadAllText("test.trx"));
-var junitXml = JUnitSerializer.Serialize(trxResults);
+var junitFromTrx = JUnitSerializer.Serialize(trxResults);
-// JUnit to TRX
var junitResults = JUnitSerializer.Deserialize(File.ReadAllText("test.xml"));
-var trxXml = TrxSerializer.Serialize(junitResults);
+var trxFromJunit = TrxSerializer.Serialize(junitResults);
```
The conversion preserves all compatible information between formats, including test names, outcomes, durations, error
messages, and output.
+### How do I automatically detect the format of a test result file?
+
+Use the `Serializer` class to automatically identify and deserialize test result files:
+
+```csharp
+// Identify format without deserializing
+var format = Serializer.Identify(File.ReadAllText("test-results.xml"));
+if (format == TestResultFormat.Trx)
+{
+ Console.WriteLine("This is a TRX file");
+}
+else if (format == TestResultFormat.JUnit)
+{
+ Console.WriteLine("This is a JUnit XML file");
+}
+
+// Or just deserialize directly - format is detected automatically
+var results = Serializer.Deserialize(File.ReadAllText("test-results.xml"));
+```
+
+The `Serializer.Identify()` method analyzes the XML structure to determine if it's a TRX file (by checking for the
+Visual Studio namespace and TestRun root element) or a JUnit file (by checking for testsuites or testsuite root
+elements). It returns `TestResultFormat.Unknown` for invalid or unrecognized formats.
+
### How do I set custom properties or metadata?
Currently, the library supports the standard metadata fields available in `TestResults` and `TestResult` classes:
diff --git a/src/DemaConsulting.TestResults/IO/Serializer.cs b/src/DemaConsulting.TestResults/IO/Serializer.cs
new file mode 100644
index 0000000..59b9a27
--- /dev/null
+++ b/src/DemaConsulting.TestResults/IO/Serializer.cs
@@ -0,0 +1,121 @@
+// Copyright(c) 2025 DEMA Consulting
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+using System.Xml.Linq;
+
+namespace DemaConsulting.TestResults.IO;
+
+///
+/// Test result format types
+///
+public enum TestResultFormat
+{
+ ///
+ /// Unknown format
+ ///
+ Unknown,
+
+ ///
+ /// TRX (Visual Studio Test Results) format
+ ///
+ Trx,
+
+ ///
+ /// JUnit XML format
+ ///
+ JUnit
+}
+
+///
+/// General purpose serializer for test results
+///
+public static class Serializer
+{
+#pragma warning disable S1075 // URIs should not be hardcoded - This is an XML namespace URI, not a file path
+ ///
+ /// TRX namespace for identifying TRX files
+ ///
+ private const string TrxNamespaceUri = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010";
+#pragma warning restore S1075
+
+ ///
+ /// Identifies the test result format based on the contents
+ ///
+ /// The test result file contents
+ /// The identified test result format
+ public static TestResultFormat Identify(string contents)
+ {
+ if (string.IsNullOrWhiteSpace(contents))
+ {
+ return TestResultFormat.Unknown;
+ }
+
+ try
+ {
+ // Parse the XML document
+ var doc = XDocument.Parse(contents);
+ var root = doc.Root;
+
+ if (root == null)
+ {
+ return TestResultFormat.Unknown;
+ }
+
+ // Check for TRX format by namespace and root element
+ if (root.Name.Namespace == TrxNamespaceUri && root.Name.LocalName == "TestRun")
+ {
+ return TestResultFormat.Trx;
+ }
+
+ // Check for JUnit format by root element names
+ if (root.Name.LocalName == "testsuites" || root.Name.LocalName == "testsuite")
+ {
+ return TestResultFormat.JUnit;
+ }
+
+ return TestResultFormat.Unknown;
+ }
+ catch
+ {
+ // If XML parsing fails, format is unknown
+ return TestResultFormat.Unknown;
+ }
+ }
+
+ ///
+ /// Deserializes test result contents to TestResults using the appropriate deserializer
+ ///
+ /// The test result file contents
+ /// Deserialized test results
+ /// Thrown when format cannot be identified or deserialization fails
+ public static TestResults Deserialize(string contents)
+ {
+ // Identify the format
+ var format = Identify(contents);
+
+ // Deserialize based on format
+ return format switch
+ {
+ TestResultFormat.Trx => TrxSerializer.Deserialize(contents),
+ TestResultFormat.JUnit => JUnitSerializer.Deserialize(contents),
+ _ => throw new InvalidOperationException("Unable to identify test result format")
+ };
+ }
+}
diff --git a/test/DemaConsulting.TestResults.Tests/IO/SerializerTests.cs b/test/DemaConsulting.TestResults.Tests/IO/SerializerTests.cs
new file mode 100644
index 0000000..6cb67f0
--- /dev/null
+++ b/test/DemaConsulting.TestResults.Tests/IO/SerializerTests.cs
@@ -0,0 +1,385 @@
+// Copyright(c) 2025 DEMA Consulting
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+using DemaConsulting.TestResults.IO;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace DemaConsulting.TestResults.Tests.IO;
+
+///
+/// Tests for Serializer class
+///
+[TestClass]
+public sealed class SerializerTests
+{
+ ///
+ /// Test that Identify correctly identifies TRX format
+ ///
+ [TestMethod]
+ public void Identify_TrxContent_ReturnsTrx()
+ {
+ // Create a sample TRX content
+ var trxContent = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """;
+
+ // Identify format
+ var format = Serializer.Identify(trxContent);
+
+ // Verify it's identified as TRX
+ Assert.AreEqual(TestResultFormat.Trx, format);
+ }
+
+ ///
+ /// Test that Identify correctly identifies JUnit format with testsuites root
+ ///
+ [TestMethod]
+ public void Identify_JUnitTestsuitesContent_ReturnsJUnit()
+ {
+ // Create a sample JUnit content with testsuites root
+ var junitContent = """
+
+
+
+
+
+
+ """;
+
+ // Identify format
+ var format = Serializer.Identify(junitContent);
+
+ // Verify it's identified as JUnit
+ Assert.AreEqual(TestResultFormat.JUnit, format);
+ }
+
+ ///
+ /// Test that Identify correctly identifies JUnit format with testsuite root
+ ///
+ [TestMethod]
+ public void Identify_JUnitTestsuiteContent_ReturnsJUnit()
+ {
+ // Create a sample JUnit content with testsuite root
+ var junitContent = """
+
+
+
+
+ """;
+
+ // Identify format
+ var format = Serializer.Identify(junitContent);
+
+ // Verify it's identified as JUnit
+ Assert.AreEqual(TestResultFormat.JUnit, format);
+ }
+
+ ///
+ /// Test that Identify returns Unknown for empty content
+ ///
+ [TestMethod]
+ public void Identify_EmptyContent_ReturnsUnknown()
+ {
+ // Identify format of empty string
+ var format = Serializer.Identify(string.Empty);
+
+ // Verify it's identified as Unknown
+ Assert.AreEqual(TestResultFormat.Unknown, format);
+ }
+
+ ///
+ /// Test that Identify returns Unknown for null content
+ ///
+ [TestMethod]
+ public void Identify_NullContent_ReturnsUnknown()
+ {
+ // Identify format of null string
+ var format = Serializer.Identify(null!);
+
+ // Verify it's identified as Unknown
+ Assert.AreEqual(TestResultFormat.Unknown, format);
+ }
+
+ ///
+ /// Test that Identify returns Unknown for whitespace content
+ ///
+ [TestMethod]
+ public void Identify_WhitespaceContent_ReturnsUnknown()
+ {
+ // Identify format of whitespace string
+ var format = Serializer.Identify(" \n\t ");
+
+ // Verify it's identified as Unknown
+ Assert.AreEqual(TestResultFormat.Unknown, format);
+ }
+
+ ///
+ /// Test that Identify returns Unknown for invalid XML
+ ///
+ [TestMethod]
+ public void Identify_InvalidXml_ReturnsUnknown()
+ {
+ // Create invalid XML content
+ var invalidXml = "";
+
+ // Identify format
+ var format = Serializer.Identify(invalidXml);
+
+ // Verify it's identified as Unknown
+ Assert.AreEqual(TestResultFormat.Unknown, format);
+ }
+
+ ///
+ /// Test that Identify returns Unknown for unrecognized XML format
+ ///
+ [TestMethod]
+ public void Identify_UnrecognizedXmlFormat_ReturnsUnknown()
+ {
+ // Create XML with unrecognized root element
+ var unrecognizedXml = """
+
+
+ value
+
+ """;
+
+ // Identify format
+ var format = Serializer.Identify(unrecognizedXml);
+
+ // Verify it's identified as Unknown
+ Assert.AreEqual(TestResultFormat.Unknown, format);
+ }
+
+ ///
+ /// Test that Deserialize successfully deserializes TRX content
+ ///
+ [TestMethod]
+ public void Deserialize_TrxContent_ReturnsTestResults()
+ {
+ // Create a sample TRX content
+ var trxContent = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """;
+
+ // Deserialize
+ var results = Serializer.Deserialize(trxContent);
+
+ // Verify results
+ Assert.IsNotNull(results);
+ Assert.AreEqual("Test Run", results.Name);
+ Assert.AreEqual("User", results.UserName);
+ Assert.HasCount(1, results.Results);
+ Assert.AreEqual("Test1", results.Results[0].Name);
+ Assert.AreEqual(TestOutcome.Passed, results.Results[0].Outcome);
+ }
+
+ ///
+ /// Test that Deserialize successfully deserializes JUnit content
+ ///
+ [TestMethod]
+ public void Deserialize_JUnitContent_ReturnsTestResults()
+ {
+ // Create a sample JUnit content
+ var junitContent = """
+
+
+
+
+
+ Stack trace here
+
+
+
+ """;
+
+ // Deserialize
+ var results = Serializer.Deserialize(junitContent);
+
+ // Verify results
+ Assert.IsNotNull(results);
+ Assert.AreEqual("Test Suite", results.Name);
+ Assert.HasCount(2, results.Results);
+ Assert.AreEqual("Test1", results.Results[0].Name);
+ Assert.AreEqual(TestOutcome.Passed, results.Results[0].Outcome);
+ Assert.AreEqual("Test2", results.Results[1].Name);
+ Assert.AreEqual(TestOutcome.Failed, results.Results[1].Outcome);
+ Assert.AreEqual("Test failed", results.Results[1].ErrorMessage);
+ }
+
+ ///
+ /// Test that Deserialize can handle real TRX example file
+ ///
+ [TestMethod]
+ public void Deserialize_RealTrxExample_ReturnsTestResults()
+ {
+ // Load example TRX file
+ var trxContent = TestHelpers.GetEmbeddedResource(
+ "DemaConsulting.TestResults.Tests.IO.Examples.example1.trx");
+
+ // Identify and verify format
+ var format = Serializer.Identify(trxContent);
+ Assert.AreEqual(TestResultFormat.Trx, format);
+
+ // Deserialize
+ var results = Serializer.Deserialize(trxContent);
+
+ // Verify results
+ Assert.IsNotNull(results);
+ Assert.AreEqual("Sample TRX Import", results.Name);
+ Assert.AreEqual("Brian Mancini", results.UserName);
+ Assert.IsNotEmpty(results.Results);
+ }
+
+ ///
+ /// Test that Deserialize handles multiple test outcomes from TRX
+ ///
+ [TestMethod]
+ public void Deserialize_TrxWithMultipleOutcomes_ParsesCorrectly()
+ {
+ // Create TRX content with different outcomes
+ var trxContent = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """;
+
+ // Deserialize
+ var results = Serializer.Deserialize(trxContent);
+
+ // Verify results
+ Assert.IsNotNull(results);
+ Assert.HasCount(2, results.Results);
+
+ var passedTest = results.Results[0];
+ Assert.AreEqual("PassedTest", passedTest.Name);
+ Assert.AreEqual(TestOutcome.Passed, passedTest.Outcome);
+
+ var failedTest = results.Results[1];
+ Assert.AreEqual("FailedTest", failedTest.Name);
+ Assert.AreEqual(TestOutcome.Failed, failedTest.Outcome);
+ Assert.AreEqual("Test failed", failedTest.ErrorMessage);
+ }
+
+ ///
+ /// Test that Deserialize handles JUnit with system output and error
+ ///
+ [TestMethod]
+ public void Deserialize_JUnitWithSystemOutput_ParsesCorrectly()
+ {
+ // Create JUnit content with system output
+ var junitContent = """
+
+
+
+
+ Standard output
+ Standard error
+
+
+
+ """;
+
+ // Deserialize
+ var results = Serializer.Deserialize(junitContent);
+
+ // Verify results
+ Assert.IsNotNull(results);
+ Assert.HasCount(1, results.Results);
+ Assert.AreEqual("Test1", results.Results[0].Name);
+ Assert.AreEqual("Standard output", results.Results[0].SystemOutput);
+ Assert.AreEqual("Standard error", results.Results[0].SystemError);
+ }
+}