| File: RunSettings.cs | Web Access |
| Project: src\vstest\src\Microsoft.TestPlatform.Common\Microsoft.TestPlatform.Common.csproj (Microsoft.VisualStudio.TestPlatform.Common) |
// 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.Collections.Generic; using System.Globalization; using System.IO; using System.Xml; using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework.Utilities; using Microsoft.VisualStudio.TestPlatform.Common.Interfaces; using Microsoft.VisualStudio.TestPlatform.Common.Logging; using Microsoft.VisualStudio.TestPlatform.Common.SettingsProvider; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using CommonResources = Microsoft.VisualStudio.TestPlatform.Common.Resources.Resources; using ObjectModelCommonResources = Microsoft.VisualStudio.TestPlatform.ObjectModel.Resources.CommonResources; namespace Microsoft.VisualStudio.TestPlatform.Common; /// <summary> /// Used for loading settings for a run. /// </summary> public class RunSettings : IRunSettings { /// <summary> /// Map of the settings names in the file to their associated settings provider. /// </summary> private readonly Dictionary<string, LazyExtension<ISettingsProvider, ISettingsProviderCapabilities>> _settings; /// <summary> /// Used to keep track if settings have been loaded. /// </summary> private bool _isSettingsLoaded; /// <summary> /// Initializes a new instance of the <see cref="RunSettings"/> class. /// </summary> public RunSettings() { _settings = new Dictionary<string, LazyExtension<ISettingsProvider, ISettingsProviderCapabilities>>(); } /// <summary> /// Gets the settings in the form of Xml string. /// </summary> public string? SettingsXml { get; private set; } /// <summary> /// Get the settings for the provided settings name. /// </summary> /// <param name="settingsName">Name of the settings section to get.</param> /// <returns>The settings provider for the settings or null if one was not found.</returns> public ISettingsProvider? GetSettings(string? settingsName) { if (settingsName.IsNullOrWhiteSpace()) { throw new ArgumentException(ObjectModelCommonResources.CannotBeNullOrEmpty, nameof(settingsName)); } // Try and lookup the settings provider. _settings.TryGetValue(settingsName, out var provider); // If a provider was found, return it. return provider?.Value; } /// <summary> /// Load the settings from the provided xml string. /// </summary> /// <param name="settings">xml string</param> public void LoadSettingsXml(string settings) { if (StringUtilities.IsNullOrWhiteSpace(settings)) { throw new ArgumentException(ObjectModelCommonResources.CannotBeNullOrEmpty, settings); } using var stringReader = new StringReader(settings); using var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); ValidateAndSaveSettings(reader); } /// <summary> /// Initialize settings providers with the settings xml. /// </summary> /// <param name="settings"> The settings xml string. </param> public void InitializeSettingsProviders(string settings) { using var stringReader = new StringReader(settings); using var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); ReadRunSettings(reader); } /// <summary> /// Validate the runsettings checking that it is well formed. /// This would throw XML exception on failure. /// </summary> /// <param name="reader">A xml reader instance.</param> private void ValidateAndSaveSettings(XmlReader reader) { try { var dom = new XmlDocument(); dom.Load(reader); using var writer = new StringWriter(CultureInfo.InvariantCulture); dom.Save(writer); SettingsXml = writer.ToString(); } catch (Exception e) { throw new SettingsException( string.Format( CultureInfo.CurrentCulture, CommonResources.RunSettingsParseError, e.Message), e); } } /// <summary> /// Reads test run settings from XmlReader /// </summary> /// <param name="reader"></param> private void ReadRunSettings(XmlReader reader) { // If settings have already been loaded, throw. if (_isSettingsLoaded) { throw new InvalidOperationException(CommonResources.RunSettingsAlreadyLoaded); } _isSettingsLoaded = true; try { // Read to the root element. XmlReaderUtilities.ReadToRootNode(reader); // Read to the first section. reader.ReadToNextElement(); // Lookup the settings provider for each of the elements. var settingsExtensionManager = SettingsProviderExtensionManager.Create(); while (!reader.EOF) { LoadSection(reader, settingsExtensionManager); reader.SkipToNextElement(); } } catch (SettingsException) { throw; } catch (Exception e) { throw new SettingsException( string.Format( CultureInfo.CurrentCulture, CommonResources.RunSettingsParseError, e.Message), e); } } /// <summary> /// Loads the section for the current element of the reader. /// </summary> /// <param name="reader">Reader to load the section from.</param> /// <param name="settingsExtensionManager">Settings extension manager to get the provider from.</param> private void LoadSection(XmlReader reader, SettingsProviderExtensionManager settingsExtensionManager) { ValidateArg.NotNull(reader, nameof(reader)); ValidateArg.NotNull(settingsExtensionManager, nameof(settingsExtensionManager)); // Check for duplicate settings if (_settings.ContainsKey(reader.Name)) { TestSessionMessageLogger.Instance.SendMessage( TestMessageLevel.Error, string.Format(CultureInfo.CurrentCulture, CommonResources.DuplicateSettingsProvided, reader.Name)); return; } // Look up the section for this node. var provider = settingsExtensionManager.GetSettingsProvider(reader.Name); if (provider != null) { try { // Have the provider load the settings. provider.Value.Load(reader.ReadSubtree()); } catch (Exception e) { // Setup to throw the exception when the section is requested. provider = CreateLazyThrower( string.Format( CultureInfo.CurrentCulture, CommonResources.SettingsProviderInitializationError, provider.Metadata.SettingsName, e.Message), provider.Metadata, e); } } else { // Setup to throw when this section is requested. var metadata = new TestSettingsProviderMetadata(reader.Name); var message = string.Format( CultureInfo.CurrentCulture, CommonResources.SettingsProviderNotFound, metadata.SettingsName); provider = CreateLazyThrower(message, metadata); } // Cache the provider instance so it can be looked up later when the section is requested. _settings.Add(provider.Metadata.SettingsName, provider); } /// <summary> /// Creates a lazy instance which will throw a SettingsException when the value property is accessed. /// </summary> /// <param name="message">Message for the exception.</param> /// <param name="metadata">Metadata to use for the lazy instance.</param> /// <param name="innerException">Inner exception to include in the exception which is thrown.</param> /// <returns>Lazy instance setup to throw when the value property is accessed.</returns> private static LazyExtension<ISettingsProvider, ISettingsProviderCapabilities> CreateLazyThrower( string message, ISettingsProviderCapabilities metadata, Exception? innerException = null) { return new LazyExtension<ISettingsProvider, ISettingsProviderCapabilities>( () => throw new SettingsException(message, innerException), metadata); } }