diff --git a/src/BenchmarkDotNet/Toolchains/AppConfigGenerator.cs b/src/BenchmarkDotNet/Toolchains/AppConfigGenerator.cs index c1db452d2b..fcaf3824b8 100644 --- a/src/BenchmarkDotNet/Toolchains/AppConfigGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/AppConfigGenerator.cs @@ -8,6 +8,8 @@ using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Jobs; +#nullable enable + namespace BenchmarkDotNet.Toolchains { internal static class AppConfigGenerator @@ -26,22 +28,32 @@ internal static class AppConfigGenerator internal static void Generate(Job job, TextReader source, TextWriter destination, IResolver resolver) { - using (var xmlReader = XmlReader.Create(source)) - { - var xmlDocument = new XmlDocument(); + var xmlDocument = new XmlDocument(); + + XmlNode configurationElement; - var configurationElement = GetOrCreateConfigurationElement(xmlDocument, xmlReader); + if (source == TextReader.Null) + { + // Create a new configuration node. + configurationElement = xmlDocument.CreateNode(XmlNodeType.Element, "configuration", string.Empty); + xmlDocument.AppendChild(configurationElement); + } + else + { + // Try to get configuration node from specified TextReader. + using var xmlReader = XmlReader.Create(source); + configurationElement = GetOrCreateConfigurationElement(xmlDocument, xmlReader); + } - var runtimeElement = GetOrCreateRuntimeElement(xmlDocument, configurationElement); + var runtimeElement = GetOrCreateRuntimeElement(xmlDocument, configurationElement); - ClearStartupSettingsForCustomClr(configurationElement, job.Environment.Runtime); - ClearAllRuntimeSettingsThatCanBeSetOnlyByJobConfiguration(runtimeElement); + ClearStartupSettingsForCustomClr(configurationElement, job.Environment.Runtime); + ClearAllRuntimeSettingsThatCanBeSetOnlyByJobConfiguration(runtimeElement); - GenerateJitSettings(xmlDocument, runtimeElement, job.Environment); - GenerateGCSettings(xmlDocument, runtimeElement, job.Environment.Gc, resolver); + GenerateJitSettings(xmlDocument, runtimeElement, job.Environment); + GenerateGCSettings(xmlDocument, runtimeElement, job.Environment.Gc, resolver); - xmlDocument.Save(destination); - } + xmlDocument.Save(destination); } private static XmlNode GetOrCreateConfigurationElement(XmlDocument xmlDocument, XmlReader xmlReader) @@ -49,19 +61,23 @@ private static XmlNode GetOrCreateConfigurationElement(XmlDocument xmlDocument, try { xmlDocument.Load(xmlReader); - - return xmlDocument.SelectSingleNode("/configuration"); + var configurationNode = xmlDocument.SelectSingleNode("/configuration"); + if (configurationNode != null) + return configurationNode; } - catch // empty document + catch (XmlException) { - return xmlDocument.AppendChild(xmlDocument.CreateNode(XmlNodeType.Element, "configuration", string.Empty)); + // Failed to load XML content. } + + // If the XML is invalid or configuration node is not exists. Create a new configuration element + return xmlDocument.AppendChild(xmlDocument.CreateNode(XmlNodeType.Element, "configuration", string.Empty))!; } private static XmlNode GetOrCreateRuntimeElement(XmlDocument xmlDocument, XmlNode configurationElement) { return configurationElement.SelectSingleNode("runtime") - ?? configurationElement.AppendChild(xmlDocument.CreateNode(XmlNodeType.Element, "runtime", string.Empty)); + ?? configurationElement.AppendChild(xmlDocument.CreateNode(XmlNodeType.Element, "runtime", string.Empty))!; } private static void ClearAllRuntimeSettingsThatCanBeSetOnlyByJobConfiguration(XmlNode runtimeElement) @@ -75,7 +91,7 @@ private static void ClearAllRuntimeSettingsThatCanBeSetOnlyByJobConfiguration(Xm } } - private static void ClearStartupSettingsForCustomClr(XmlNode configurationElement, Runtime runtime) + private static void ClearStartupSettingsForCustomClr(XmlNode configurationElement, Runtime? runtime) { if (!(runtime is ClrRuntime clrRuntime) || string.IsNullOrEmpty(clrRuntime.Version)) return;