// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if !WINDOWS_UWP
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
#endif
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
///
/// The run configuration settings.
///
internal sealed class RunConfigurationSettings
{
///
/// The settings name.
///
public const string SettingsName = "RunConfiguration";
///
/// Gets a value indicating the requested platform apartment state.
///
internal ApartmentState? ExecutionApartmentState { get; private set; }
///
/// Gets the configuration settings from the xml.
///
/// The xml with the settings passed from the test platform.
public static RunConfigurationSettings GetSettings(string? runSettingsXml)
{
if (StringEx.IsNullOrEmpty(runSettingsXml))
{
// This will contain default configuration settings
return new RunConfigurationSettings();
}
using var stringReader = new StringReader(runSettingsXml);
var reader = XmlReader.Create(stringReader, new() { IgnoreComments = true, IgnoreWhitespace = true, DtdProcessing = DtdProcessing.Prohibit });
// read to the fist child
ReadToRootNode(reader);
ReadToNextElement(reader);
// Read till we reach nodeName element or reach EOF
while (!string.Equals(reader.Name, SettingsName, StringComparison.OrdinalIgnoreCase)
&& !reader.EOF)
{
SkipToNextElement(reader);
}
if (!reader.EOF)
{
// read nodeName element.
return ToSettings(reader.ReadSubtree());
}
return new RunConfigurationSettings();
}
///
/// Convert the parameter xml to TestSettings.
///
/// Reader to load the settings from.
/// An instance of the class.
private static RunConfigurationSettings ToSettings(XmlReader reader)
{
if (reader is null)
{
throw new ArgumentNullException(nameof(reader));
}
// Expected format of the xml is: -
//
//
//
// true
// STA/MTA
//
//
RunConfigurationSettings settings = new();
// Read the first element in the section
ReadToNextElement(reader);
if (!reader.IsEmptyElement)
{
reader.Read();
while (reader.NodeType == XmlNodeType.Element)
{
string elementName = reader.Name.ToUpperInvariant();
switch (elementName)
{
case "EXECUTIONTHREADAPARTMENTSTATE":
{
if (Enum.TryParse(reader.ReadInnerXml(), out ApartmentStateSetting apartmentStateSetting))
{
settings.ExecutionApartmentState = apartmentStateSetting switch
{
ApartmentStateSetting.STA => ApartmentState.STA,
ApartmentStateSetting.MTA => ApartmentState.MTA,
_ => throw new NotSupportedException($"Platform apartment state '{apartmentStateSetting}' is not supported."),
};
}
break;
}
default:
{
SkipToNextElement(reader);
break;
}
}
}
}
return settings;
}
#if !WINDOWS_UWP
internal static RunConfigurationSettings SetRunConfigurationSettingsFromConfig(IConfiguration configuration, RunConfigurationSettings settings)
{
// Expected format of the json is: -
// "mstest" : {
// "execution": {
// "executionApartmentState": "STA"
// }
// }
string? apartmentStateValue = configuration["mstest:execution:executionApartmentState"];
if (Enum.TryParse(apartmentStateValue, out ApartmentStateSetting apartmentStateSetting))
{
settings.ExecutionApartmentState = apartmentStateSetting switch
{
ApartmentStateSetting.STA => ApartmentState.STA,
ApartmentStateSetting.MTA => ApartmentState.MTA,
_ => throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, apartmentStateSetting, "execution:executionApartmentState")),
};
}
return settings;
}
#endif
private static void ReadToRootNode(XmlReader reader)
{
ReadToNextElement(reader);
// Verify that it is a "RunSettings" node.
if (reader.Name != "RunSettings")
{
throw new FormatException($"The runsettings XML document has an invalid root element '<{reader.Name}>'. Expected root element is ''.");
}
}
private static void ReadToNextElement(XmlReader reader)
{
while (!reader.EOF && reader.Read() && reader.NodeType != XmlNodeType.Element)
{
}
}
private static void SkipToNextElement(XmlReader reader)
{
reader.Skip();
if (reader.NodeType != XmlNodeType.Element)
{
ReadToNextElement(reader);
}
}
}