// 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); } } }