From 545084d4bde082915acddd2af1b2ee94765ed920 Mon Sep 17 00:00:00 2001 From: Jeremy Kuhne Date: Thu, 5 Jan 2017 11:02:03 -0800 Subject: [PATCH] Add more config tests (dotnet/corefx#14883) - Adds a number of new tests - Cleans up / clarifies some code - Removes a chunk of duplicate code Commit migrated from https://github.com/dotnet/corefx/commit/4a6cead5d3a0cdc3f0b6481ac082ad983ffd63bd --- .../ConfigurationCollectionAttribute.cs | 6 +- .../Configuration/ConfigurationElement.cs | 223 +++++++---------- .../ConfigurationElementCollection.cs | 5 +- .../ConfigurationLockCollection.cs | 2 + .../Configuration/ConfigurationProperty.cs | 228 +++++++++-------- .../Configuration/ConnectionStringSettings.cs | 2 +- .../InternalConfigConfigurationFactory.cs | 3 +- .../Internal/WriteFileContext.cs | 6 +- .../KeyValueConfigurationCollection.cs | 2 + .../ProtectedProviderSettings.cs | 11 +- .../tests/System.Configuration.Tests.csproj | 6 + .../System/Configuration/AppSettingsTests.cs | 21 ++ .../Configuration/BasicCustomSectionTests.cs | 194 +++++++++++++++ .../ConfigurationElementCollectionTests.cs | 187 ++++++++++++++ .../ConfigurationElementTests.cs | 81 ++++++ .../ConfigurationPropertyAttributeTests.cs | 71 ++++++ .../ConfigurationPropertyTests.cs | 231 ++++++++++++++++++ .../Configuration/ConfigurationTests.cs | 57 +++++ .../tests/System/Configuration/TestData.cs | 11 +- 19 files changed, 1089 insertions(+), 258 deletions(-) create mode 100644 src/libraries/System.Configuration/tests/System/Configuration/BasicCustomSectionTests.cs create mode 100644 src/libraries/System.Configuration/tests/System/Configuration/ConfigurationElementCollectionTests.cs create mode 100644 src/libraries/System.Configuration/tests/System/Configuration/ConfigurationElementTests.cs create mode 100644 src/libraries/System.Configuration/tests/System/Configuration/ConfigurationPropertyAttributeTests.cs create mode 100644 src/libraries/System.Configuration/tests/System/Configuration/ConfigurationPropertyTests.cs create mode 100644 src/libraries/System.Configuration/tests/System/Configuration/ConfigurationTests.cs diff --git a/src/libraries/System.Configuration/src/System/Configuration/ConfigurationCollectionAttribute.cs b/src/libraries/System.Configuration/src/System/Configuration/ConfigurationCollectionAttribute.cs index 27259833412..95ca2a337c1 100644 --- a/src/libraries/System.Configuration/src/System/Configuration/ConfigurationCollectionAttribute.cs +++ b/src/libraries/System.Configuration/src/System/Configuration/ConfigurationCollectionAttribute.cs @@ -4,8 +4,10 @@ namespace System.Configuration { - // This attribute is expected on section properties of type derivied from ConfigurationElementCollection - // or on the itself + /// + /// Used on classes derived from ConfigurationElementCollection. Specifies the collection item type and + /// verbs used for add/remove/clear. + /// [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] public sealed class ConfigurationCollectionAttribute : Attribute { diff --git a/src/libraries/System.Configuration/src/System/Configuration/ConfigurationElement.cs b/src/libraries/System.Configuration/src/System/Configuration/ConfigurationElement.cs index 8f45464fa12..d48e624a832 100644 --- a/src/libraries/System.Configuration/src/System/Configuration/ConfigurationElement.cs +++ b/src/libraries/System.Configuration/src/System/Configuration/ConfigurationElement.cs @@ -47,14 +47,14 @@ public abstract class ConfigurationElement private static readonly ConfigurationElementProperty s_elementProperty = new ConfigurationElementProperty(new DefaultValidator()); - private bool _bInited; - private bool _bModified; - private bool _bReadOnly; + private bool _initialized; + private bool _modified; + private bool _readOnly; internal BaseConfigurationRecord _configRecord; private ConfigurationElementProperty _elementProperty = s_elementProperty; internal ContextInformation _evalContext; private volatile ElementInformation _evaluationElement; - internal ConfigurationValueFlags _fItemLocked; + internal ConfigurationValueFlags _itemLockedFlag; internal ConfigurationLockCollection _lockedAllExceptAttributesList; internal ConfigurationLockCollection _lockedAllExceptElementsList; internal ConfigurationLockCollection _lockedAttributesList; @@ -70,6 +70,11 @@ protected ConfigurationElement() ApplyValidator(this); } + internal static bool IsNullOrNullProperty(object value) + { + return value == null || value == s_nullPropertyValue; + } + internal bool DataToWriteInternal { get; set; } internal bool ElementPresent { get; set; } @@ -80,7 +85,7 @@ protected ConfigurationElement() internal ConfigurationLockCollection LockedAllExceptAttributesList => _lockedAllExceptAttributesList; - internal ConfigurationValueFlags ItemLocked => _fItemLocked; + internal ConfigurationValueFlags ItemLocked => _itemLockedFlag; public ConfigurationLockCollection LockAttributes => _lockedAttributesList ?? (_lockedAttributesList = new ConfigurationLockCollection(this, ConfigurationLockCollectionType.LockedAttributes)); @@ -96,15 +101,18 @@ protected ConfigurationElement() public bool LockItem { - get { return (_fItemLocked & ConfigurationValueFlags.Locked) != 0; } + get { return (_itemLockedFlag & ConfigurationValueFlags.Locked) != 0; } set { - if ((_fItemLocked & ConfigurationValueFlags.Inherited) == 0) + if ((_itemLockedFlag & ConfigurationValueFlags.Inherited) == 0) + { + _itemLockedFlag = value ? ConfigurationValueFlags.Locked : ConfigurationValueFlags.Default; + _itemLockedFlag |= ConfigurationValueFlags.Modified; + } + else { - _fItemLocked = value ? ConfigurationValueFlags.Locked : ConfigurationValueFlags.Default; - _fItemLocked |= ConfigurationValueFlags.Modified; + throw new ConfigurationErrorsException(string.Format(SR.Config_base_attribute_locked, LockItemKey)); } - else throw new ConfigurationErrorsException(string.Format(SR.Config_base_attribute_locked, LockItemKey)); } } @@ -124,7 +132,7 @@ public bool LockItem { ConfigurationElement childElement = CreateElement(prop.Type); - if (_bReadOnly) childElement.SetReadOnly(); + if (_readOnly) childElement.SetReadOnly(); if (typeof(ConfigurationElementCollection).IsAssignableFrom(prop.Type)) { @@ -189,10 +197,8 @@ protected internal virtual ConfigurationPropertyCollection Properties ConfigurationPropertyCollection result = null; if (PropertiesFromType(GetType(), out result)) - { - ApplyInstanceAttributes(this); // Redundant but preserved to minimize code changes for Whidbey RTM ApplyValidatorsRecursive(this); - } + return result; } } @@ -238,16 +244,16 @@ protected internal virtual void Init() // If Init is called by the derived class, we may be able // to set _bInited to true if the derived class properly // calls Init on its base. - _bInited = true; + _initialized = true; } internal void CallInit() { // Ensure Init is called just once - if (!_bInited) + if (!_initialized) { Init(); - _bInited = true; + _initialized = true; } } @@ -255,9 +261,9 @@ internal void MergeLocks(ConfigurationElement source) { if (source == null) return; - _fItemLocked = (source._fItemLocked & ConfigurationValueFlags.Locked) != 0 - ? ConfigurationValueFlags.Inherited | source._fItemLocked - : _fItemLocked; + _itemLockedFlag = (source._itemLockedFlag & ConfigurationValueFlags.Locked) != 0 + ? ConfigurationValueFlags.Inherited | source._itemLockedFlag + : _itemLockedFlag; if (source._lockedAttributesList != null) { @@ -421,7 +427,7 @@ internal virtual void AssociateContext(BaseConfigurationRecord configRecord) protected internal virtual bool IsModified() { - if (_bModified) + if (_modified) return true; if ((_lockedAttributesList != null) && _lockedAttributesList.IsModified) return true; @@ -431,7 +437,7 @@ protected internal virtual bool IsModified() return true; if ((_lockedAllExceptElementsList != null) && _lockedAllExceptElementsList.IsModified) return true; - if ((_fItemLocked & ConfigurationValueFlags.Modified) != 0) + if ((_itemLockedFlag & ConfigurationValueFlags.Modified) != 0) return true; foreach (ConfigurationElement elem in Values.ConfigurationElements) if (elem.IsModified()) return true; @@ -440,7 +446,7 @@ protected internal virtual bool IsModified() protected internal virtual void ResetModified() { - _bModified = false; + _modified = false; _lockedAttributesList?.ResetModified(); _lockedAllExceptAttributesList?.ResetModified(); _lockedElementsList?.ResetModified(); @@ -451,18 +457,18 @@ protected internal virtual void ResetModified() public virtual bool IsReadOnly() { - return _bReadOnly; + return _readOnly; } protected internal virtual void SetReadOnly() { - _bReadOnly = true; + _readOnly = true; foreach (ConfigurationElement elem in Values.ConfigurationElements) elem.SetReadOnly(); } internal void SetLocked() { - _fItemLocked = ConfigurationValueFlags.Locked | ConfigurationValueFlags.XmlParentInherited; + _itemLockedFlag = ConfigurationValueFlags.Locked | ConfigurationValueFlags.XmlParentInherited; foreach (ConfigurationProperty prop in Properties) { @@ -523,12 +529,14 @@ internal void CheckLockedElement(string elementName, XmlReader reader) // have to check if clear was locked! if (elementName != null) { - if (((_lockedElementsList != null) && - (_lockedElementsList.DefinedInParent(LockAll) || _lockedElementsList.DefinedInParent(elementName))) || + bool lockedInParent = (_lockedElementsList != null) && + (_lockedElementsList.DefinedInParent(LockAll) || _lockedElementsList.DefinedInParent(elementName)); + + if (lockedInParent || ((_lockedAllExceptElementsList != null) && (_lockedAllExceptElementsList.Count != 0) && _lockedAllExceptElementsList.HasParentElements && !_lockedAllExceptElementsList.DefinedInParent(elementName)) || - ((_fItemLocked & ConfigurationValueFlags.Inherited) != 0) + ((_itemLockedFlag & ConfigurationValueFlags.Inherited) != 0) ) { throw new ConfigurationErrorsException(string.Format(SR.Config_base_element_locked, elementName), @@ -554,8 +562,8 @@ internal void ResetLockLists(ConfigurationElement parentElement) if (parentElement == null) return; - _fItemLocked = (parentElement._fItemLocked & ConfigurationValueFlags.Locked) != 0 - ? ConfigurationValueFlags.Inherited | parentElement._fItemLocked + _itemLockedFlag = (parentElement._itemLockedFlag & ConfigurationValueFlags.Locked) != 0 + ? ConfigurationValueFlags.Inherited | parentElement._itemLockedFlag : ConfigurationValueFlags.Default; if (parentElement._lockedAttributesList != null) @@ -683,24 +691,27 @@ protected internal virtual void Reset(ConfigurationElement parentElement) public override bool Equals(object compareTo) { - ConfigurationElement compareToElem = compareTo as ConfigurationElement; + ConfigurationElement otherElement = compareTo as ConfigurationElement; - if ((compareToElem == null) || + if ((otherElement == null) || (compareTo.GetType() != GetType()) || - (compareToElem.Properties.Count != Properties.Count)) + (otherElement.Properties.Count != Properties.Count)) return false; foreach (ConfigurationProperty configProperty in Properties) - if (!Equals(Values[configProperty.Name], compareToElem.Values[configProperty.Name])) + { + object thisValue = Values[configProperty.Name]; + object otherValue = otherElement.Values[configProperty.Name]; + + if (!Equals(thisValue, otherValue)) { - if (!((((Values[configProperty.Name] == null) || - (Values[configProperty.Name] == s_nullPropertyValue)) && - Equals(compareToElem.Values[configProperty.Name], configProperty.DefaultValue)) || - (((compareToElem.Values[configProperty.Name] == null) || - (compareToElem.Values[configProperty.Name] == s_nullPropertyValue)) && - Equals(Values[configProperty.Name], configProperty.DefaultValue)))) + // The values don't match- if either is 'null' default is ok + if (!((IsNullOrNullProperty(thisValue) && Equals(otherValue, configProperty.DefaultValue)) + || (IsNullOrNullProperty(otherValue) && Equals(thisValue, configProperty.DefaultValue)))) return false; } + } + return true; } @@ -715,70 +726,6 @@ public override int GetHashCode() return hHashCode; } - // Note: this method is completelly redundant ( the code is duplaicated in ConfigurationProperty( PropertyInfo ) ) - // We do not remove the code now to minimize code changes for Whidbey RTM but this method and all calls leading to it should - // be removed post-Whidbey - private static void ApplyInstanceAttributes(object instance) - { - Debug.Assert(instance is ConfigurationElement, "instance is ConfigurationElement"); - Type type = instance.GetType(); - - foreach ( - PropertyInfo propertyInformation in - type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) - { - ConfigurationPropertyAttribute attribProperty = - Attribute.GetCustomAttribute(propertyInformation, - typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute; - - if (attribProperty == null) continue; - - Type propertyType = propertyInformation.PropertyType; - if (typeof(ConfigurationElementCollection).IsAssignableFrom(propertyType)) - { - // Collections need some customization when the collection attribute is present - ConfigurationCollectionAttribute attribCollection = - Attribute.GetCustomAttribute(propertyInformation, - typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute ?? - Attribute.GetCustomAttribute(propertyType, - typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute; - - ConfigurationElementCollection coll = - propertyInformation.GetValue(instance, null) as ConfigurationElementCollection; - - if (coll == null) - { - throw new ConfigurationErrorsException(string.Format(SR.Config_element_null_instance, - propertyInformation.Name, attribProperty.Name)); - } - - // If the attribute is found - get the collection instance and set the data from the attribute - if (attribCollection != null) - { - if (attribCollection.AddItemName.IndexOf(',') == -1) - coll.AddElementName = attribCollection.AddItemName; - coll.RemoveElementName = attribCollection.RemoveItemName; - coll.ClearElementName = attribCollection.ClearItemsName; - } - } - else - { - if (typeof(ConfigurationElement).IsAssignableFrom(propertyType)) - { - // Nested configuration element - handle recursively - object element = propertyInformation.GetValue(instance, null); - if (element == null) - { - throw new ConfigurationErrorsException(string.Format(SR.Config_element_null_instance, - propertyInformation.Name, attribProperty.Name)); - } - - ApplyInstanceAttributes(element); - } - } - } - } - private static bool PropertiesFromType(Type type, out ConfigurationPropertyCollection result) { ConfigurationPropertyCollection properties = (ConfigurationPropertyCollection)s_propertyBags[type]; @@ -807,28 +754,24 @@ private static ConfigurationPropertyCollection CreatePropertyBagFromType(Type ty // For ConfigurationElement derived classes - get the per-type validator if (typeof(ConfigurationElement).IsAssignableFrom(type)) { - ConfigurationValidatorAttribute attribValidator = + ConfigurationValidatorAttribute validatorAttribute = Attribute.GetCustomAttribute(type, typeof(ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute; - if (attribValidator != null) + if (validatorAttribute != null) { - attribValidator.SetDeclaringType(type); - ConfigurationValidatorBase validator = attribValidator.ValidatorInstance; - + validatorAttribute.SetDeclaringType(type); + ConfigurationValidatorBase validator = validatorAttribute.ValidatorInstance; if (validator != null) CachePerTypeValidator(type, validator); } } ConfigurationPropertyCollection properties = new ConfigurationPropertyCollection(); - foreach ( - PropertyInfo propertyInformation in - type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + foreach (PropertyInfo propertyInformation in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { - ConfigurationProperty newProp = CreateConfigurationPropertyFromAttributes(propertyInformation); - - if (newProp != null) properties.Add(newProp); + ConfigurationProperty property = CreateConfigurationPropertyFromAttributes(propertyInformation); + if (property != null) properties.Add(property); } return properties; @@ -844,8 +787,9 @@ private static ConfigurationProperty CreateConfigurationPropertyFromAttributes(P Attribute.GetCustomAttribute(propertyInformation, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute; - // If there is no ConfigurationProperty attrib - this is not considered a property - if (attribProperty != null) result = new ConfigurationProperty(propertyInformation); + // If there is no ConfigurationPropertyAttribute - this is not considered a property + if (attribProperty != null) + result = new ConfigurationProperty(propertyInformation); // Handle some special cases of property types if ((result != null) && typeof(ConfigurationElement).IsAssignableFrom(result.Type)) @@ -889,8 +833,7 @@ private static void ApplyValidatorsRecursive(ConfigurationElement root) // Apply the validator on 'root' ApplyValidator(root); - // Apply validators on child elements ( note - we will do this only on already created child elements - // The non created ones will get their validators in the ctor + // Apply validators on created child elements (non created ones will get their validators run when constructed) foreach (ConfigurationElement elem in root.Values.ConfigurationElements) ApplyValidatorsRecursive(elem); } @@ -911,11 +854,11 @@ protected void SetPropertyValue(ConfigurationProperty prop, object value, bool i !_lockedAllExceptAttributesList.DefinedInParent(prop.Name)) || ((_lockedAttributesList != null) && (_lockedAttributesList.DefinedInParent(prop.Name) || _lockedAttributesList.DefinedInParent(LockAll))) || - (((_fItemLocked & ConfigurationValueFlags.Locked) != 0) && - ((_fItemLocked & ConfigurationValueFlags.Inherited) != 0)))) + (((_itemLockedFlag & ConfigurationValueFlags.Locked) != 0) && + ((_itemLockedFlag & ConfigurationValueFlags.Inherited) != 0)))) throw new ConfigurationErrorsException(string.Format(SR.Config_base_attribute_locked, prop.Name)); - _bModified = true; + _modified = true; // Run the new value through the validator to make sure its ok to store it if (value != null) prop.Validate(value); @@ -958,7 +901,7 @@ internal virtual void Dump(TextWriter tw) _lockedAllExceptAttributesList = sourceElement._lockedAllExceptAttributesList; _lockedAllExceptElementsList = sourceElement._lockedAllExceptElementsList; - _fItemLocked = sourceElement._fItemLocked; + _itemLockedFlag = sourceElement._itemLockedFlag; _lockedAttributesList = sourceElement._lockedAttributesList; _lockedElementsList = sourceElement._lockedElementsList; AssociateContext(sourceElement._configRecord); @@ -1023,8 +966,7 @@ internal virtual void Dump(TextWriter tw) object value = sourceElement.Values[prop.Name]; // if the property is required or we are writing a full config make sure we have defaults - if ((prop.IsRequired || (saveMode == ConfigurationSaveMode.Full)) && - ((value == null) || (value == s_nullPropertyValue))) + if ((prop.IsRequired || (saveMode == ConfigurationSaveMode.Full)) && IsNullOrNullProperty(value)) { // If the default value is null, this means there wasnt a reasonable default for the value // and there is nothing more we can do. Otherwise reset the value to the default @@ -1035,14 +977,10 @@ internal virtual void Dump(TextWriter tw) value = prop.DefaultValue; // need to make sure required properties are persisted } - if ((value == null) || (value == s_nullPropertyValue)) continue; - - object value2 = null; - if (parentElement != null) // Is there a parent - value2 = parentElement.Values[prop.Name]; // if so get it's value + if (IsNullOrNullProperty(value)) continue; - if (value2 == null) // no parent use default - value2 = prop.DefaultValue; + // Use the parent value if available + object value2 = parentElement?.Values[prop.Name] ?? prop.DefaultValue; // If changed and not same as parent write or required switch (saveMode) @@ -1063,7 +1001,7 @@ internal virtual void Dump(TextWriter tw) Values[prop.Name] = value; break; case ConfigurationSaveMode.Full: - if ((value != null) && (value != s_nullPropertyValue)) + if (IsNullOrNullProperty(value)) Values[prop.Name] = value; else Values[prop.Name] = value2; @@ -1149,7 +1087,10 @@ protected internal virtual bool SerializeElement(XmlWriter writer, bool serializ } - if (prop.IsConfigurationElementType) hasAnyChildElements = true; + if (prop.IsConfigurationElementType) + { + hasAnyChildElements = true; + } else { if (((_lockedAllExceptAttributesList != null) && _lockedAllExceptAttributesList.HasParentElements && @@ -1196,9 +1137,9 @@ protected internal virtual bool SerializeElement(XmlWriter writer, bool serializ dataToWrite |= SerializeLockList(_lockedAllExceptAttributesList, LockAllAttributesExceptKey, writer); dataToWrite |= SerializeLockList(_lockedElementsList, LockElementsKey, writer); dataToWrite |= SerializeLockList(_lockedAllExceptElementsList, LockAllElementsExceptKey, writer); - if (((_fItemLocked & ConfigurationValueFlags.Locked) != 0) && - ((_fItemLocked & ConfigurationValueFlags.Inherited) == 0) && - ((_fItemLocked & ConfigurationValueFlags.XmlParentInherited) == 0)) + if (((_itemLockedFlag & ConfigurationValueFlags.Locked) != 0) && + ((_itemLockedFlag & ConfigurationValueFlags.Inherited) == 0) && + ((_itemLockedFlag & ConfigurationValueFlags.XmlParentInherited) == 0)) { dataToWrite = true; writer?.WriteAttributeString(LockItemKey, true.ToString().ToLower(CultureInfo.InvariantCulture)); @@ -1487,8 +1428,8 @@ protected internal virtual void DeserializeElement(XmlReader reader, bool serial (_lockedElementsList.Contains(LockAll) && (reader.Name != ElementTagName)))) || ((_lockedAllExceptElementsList != null) && (_lockedAllExceptElementsList.Count != 0) && !_lockedAllExceptElementsList.Contains(reader.Name)) || - (((_fItemLocked & ConfigurationValueFlags.Locked) != 0) && - ((_fItemLocked & ConfigurationValueFlags.Inherited) != 0)) + (((_itemLockedFlag & ConfigurationValueFlags.Locked) != 0) && + ((_itemLockedFlag & ConfigurationValueFlags.Inherited) != 0)) ) throw new ConfigurationErrorsException(string.Format(SR.Config_base_element_locked, reader.Name), reader); @@ -1657,7 +1598,7 @@ protected internal virtual void DeserializeElement(XmlReader reader, bool serial if (itemLockedLocally) { SetLocked(); - _fItemLocked = ConfigurationValueFlags.Locked; + _itemLockedFlag = ConfigurationValueFlags.Locked; } if (lockedAttributesList != null) diff --git a/src/libraries/System.Configuration/src/System/Configuration/ConfigurationElementCollection.cs b/src/libraries/System.Configuration/src/System/Configuration/ConfigurationElementCollection.cs index 991dc0798e1..1cdc03896dc 100644 --- a/src/libraries/System.Configuration/src/System/Configuration/ConfigurationElementCollection.cs +++ b/src/libraries/System.Configuration/src/System/Configuration/ConfigurationElementCollection.cs @@ -254,7 +254,7 @@ public override int GetHashCode() Hashtable inheritance = new Hashtable(); _lockedAllExceptAttributesList = sourceElement._lockedAllExceptAttributesList; _lockedAllExceptElementsList = sourceElement._lockedAllExceptElementsList; - _fItemLocked = sourceElement._fItemLocked; + _itemLockedFlag = sourceElement._itemLockedFlag; _lockedAttributesList = sourceElement._lockedAttributesList; _lockedElementsList = sourceElement._lockedElementsList; @@ -661,12 +661,13 @@ private void BaseAddInternal(int index, ConfigurationElement element, bool flagA Items.Insert(index, new Entry(entryType, key, element)); } else + { Items.Add(new Entry(entryType, key, element)); + } _modified = true; } - protected virtual void BaseAdd(int index, ConfigurationElement element) { BaseAdd(index, element, false); diff --git a/src/libraries/System.Configuration/src/System/Configuration/ConfigurationLockCollection.cs b/src/libraries/System.Configuration/src/System/Configuration/ConfigurationLockCollection.cs index d0bb431c0a7..c71e0ac9c84 100644 --- a/src/libraries/System.Configuration/src/System/Configuration/ConfigurationLockCollection.cs +++ b/src/libraries/System.Configuration/src/System/Configuration/ConfigurationLockCollection.cs @@ -83,6 +83,7 @@ public bool HasParentElements { // return true if there is at least one element that was defined in the parent bool result = false; + // Check to see if the exception list is empty as a result of a merge from config // If so the there were some parent elements because empty string is invalid in config. // and the only way to get an empty list is for the merged config to have no elements @@ -211,6 +212,7 @@ internal bool DefinedInParent(string name) { if (name == null) return false; + if (!ExceptionList) { return _internalDictionary.Contains(name) && diff --git a/src/libraries/System.Configuration/src/System/Configuration/ConfigurationProperty.cs b/src/libraries/System.Configuration/src/System/Configuration/ConfigurationProperty.cs index f89c634f0c3..13222ce9044 100644 --- a/src/libraries/System.Configuration/src/System/Configuration/ConfigurationProperty.cs +++ b/src/libraries/System.Configuration/src/System/Configuration/ConfigurationProperty.cs @@ -24,11 +24,15 @@ public ConfigurationProperty(string name, Type type) ConstructorInit(name, type, ConfigurationPropertyOptions.None, null, null); - if (type == typeof(string)) defaultValue = string.Empty; - else + if (type == typeof(string)) + { + defaultValue = string.Empty; + } + else if (type.IsValueType) { - if (type.IsValueType) defaultValue = TypeUtil.CreateInstance(type); + defaultValue = TypeUtil.CreateInstance(type); } + SetDefaultValue(defaultValue); } @@ -66,94 +70,91 @@ internal ConfigurationProperty(PropertyInfo info) { Debug.Assert(info != null, "info != null"); - // Bellow are the attributes we handle - ConfigurationPropertyAttribute attribProperty = null; + ConfigurationPropertyAttribute propertyAttribute = null; + DescriptionAttribute descriptionAttribute = null; - // Compatability attributes - // If the approprite data is provided in the ConfigPropAttribute then the one bellow will be ignored - DescriptionAttribute attribStdDescription = null; - DefaultValueAttribute attribStdDefault = null; + // For compatibility we read the component model default value attribute. It is only + // used if ConfigurationPropertyAttribute doesn't provide the default value. + DefaultValueAttribute defaultValueAttribute = null; TypeConverter typeConverter = null; ConfigurationValidatorBase validator = null; - // Find the interesting attributes in the collection + // Look for relevant attributes foreach (Attribute attribute in Attribute.GetCustomAttributes(info)) + { if (attribute is TypeConverterAttribute) { - TypeConverterAttribute attribConverter = (TypeConverterAttribute)attribute; - typeConverter = TypeUtil.CreateInstance(attribConverter.ConverterTypeName); + typeConverter = TypeUtil.CreateInstance(((TypeConverterAttribute)attribute).ConverterTypeName); } - else + else if (attribute is ConfigurationPropertyAttribute) { - if (attribute is ConfigurationPropertyAttribute) - attribProperty = (ConfigurationPropertyAttribute)attribute; - else + propertyAttribute = (ConfigurationPropertyAttribute)attribute; + } + else if (attribute is ConfigurationValidatorAttribute) + { + if (validator != null) { - if (attribute is ConfigurationValidatorAttribute) - { - // There could be more then one validator attribute specified on a property - // Currently we consider this an error since it's too late to fix it for whidbey - // but the right thing to do is to introduce new validator type ( CompositeValidator ) that is a list of validators and executes - // them all - - if (validator != null) - { - throw new ConfigurationErrorsException( - string.Format(SR.Validator_multiple_validator_attributes, info.Name)); - } - - ConfigurationValidatorAttribute attribValidator = (ConfigurationValidatorAttribute)attribute; - attribValidator.SetDeclaringType(info.DeclaringType); - validator = attribValidator.ValidatorInstance; - } - else - { - if (attribute is DescriptionAttribute) - attribStdDescription = (DescriptionAttribute)attribute; - else - { - if (attribute is DefaultValueAttribute) - attribStdDefault = (DefaultValueAttribute)attribute; - } - } + // We only allow one validator to be specified on a property. + // + // Consider: introduce a new validator type ( CompositeValidator ) that is a + // list of validators and executes them all + + throw new ConfigurationErrorsException( + string.Format(SR.Validator_multiple_validator_attributes, info.Name)); } + + ConfigurationValidatorAttribute validatorAttribute = (ConfigurationValidatorAttribute)attribute; + validatorAttribute.SetDeclaringType(info.DeclaringType); + validator = validatorAttribute.ValidatorInstance; } + else if (attribute is DescriptionAttribute) + { + descriptionAttribute = (DescriptionAttribute)attribute; + } + else if (attribute is DefaultValueAttribute) + { + defaultValueAttribute = (DefaultValueAttribute)attribute; + } + } Type propertyType = info.PropertyType; - // Collections need some customization when the collection attribute is present + + // If the property is a Collection we need to look for the ConfigurationCollectionAttribute for + // additional customization. if (typeof(ConfigurationElementCollection).IsAssignableFrom(propertyType)) { - ConfigurationCollectionAttribute attribCollection = + // Look for the ConfigurationCollection attribute on the property itself, fall back + // on the property type. + ConfigurationCollectionAttribute collectionAttribute = Attribute.GetCustomAttribute(info, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute ?? Attribute.GetCustomAttribute(propertyType, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute; - // If none on the property - see if there is an attribute on the collection type itself - if (attribCollection != null) + if (collectionAttribute != null) { - if (attribCollection.AddItemName.IndexOf(',') == -1) AddElementName = attribCollection.AddItemName; - RemoveElementName = attribCollection.RemoveItemName; - ClearElementName = attribCollection.ClearItemsName; + if (collectionAttribute.AddItemName.IndexOf(',') == -1) AddElementName = collectionAttribute.AddItemName; + RemoveElementName = collectionAttribute.RemoveItemName; + ClearElementName = collectionAttribute.ClearItemsName; } } - // This constructor shouldnt be invoked if the reflection info is not for an actual config property - Debug.Assert(attribProperty != null, "attribProperty != null"); + // This constructor shouldn't be invoked if the reflection info is not for an actual config property + Debug.Assert(propertyAttribute != null, "attribProperty != null"); - ConstructorInit(attribProperty.Name, + ConstructorInit(propertyAttribute.Name, info.PropertyType, - attribProperty.Options, + propertyAttribute.Options, validator, typeConverter); // Figure out the default value - InitDefaultValueFromTypeInfo(attribProperty, attribStdDefault); + InitDefaultValueFromTypeInfo(propertyAttribute, defaultValueAttribute); // Get the description - if (!string.IsNullOrEmpty(attribStdDescription?.Description)) - Description = attribStdDescription.Description; + if (!string.IsNullOrEmpty(descriptionAttribute?.Description)) + Description = descriptionAttribute.Description; } public string Name { get; private set; } @@ -166,7 +167,8 @@ internal bool IsConfigurationElementType { get { - if (_isTypeInited) return _isConfigurationElementType; + if (_isTypeInited) + return _isConfigurationElementType; _isConfigurationElementType = typeof(ConfigurationElement).IsAssignableFrom(Type); _isTypeInited = true; @@ -209,7 +211,8 @@ public TypeConverter Converter internal string ClearElementName { get; } - private void ConstructorInit(string name, + private void ConstructorInit( + string name, Type type, ConfigurationPropertyOptions options, ConfigurationValidatorBase validator, @@ -221,11 +224,17 @@ public TypeConverter Converter string.Format(SR.Config_properties_may_not_be_derived_from_configuration_section, name)); } - ProvidedName = name; // save the provided name so we can check for default collection names - if (((options & ConfigurationPropertyOptions.IsDefaultCollection) != 0) && - string.IsNullOrEmpty(name)) + // save the provided name so we can check for default collection names + ProvidedName = name; + + if (((options & ConfigurationPropertyOptions.IsDefaultCollection) != 0) && string.IsNullOrEmpty(name)) + { name = s_defaultCollectionPropertyName; - else ValidatePropertyName(name); + } + else + { + ValidatePropertyName(name); + } Name = name; Type = type; @@ -234,7 +243,10 @@ public TypeConverter Converter _converter = converter; // Use the default validator if none was supplied - if (Validator == null) Validator = s_defaultValidatorInstance; + if (Validator == null) + { + Validator = s_defaultValidatorInstance; + } else { // Make sure the supplied validator supports the type of this property @@ -245,7 +257,8 @@ public TypeConverter Converter private void ValidatePropertyName(string name) { - if (string.IsNullOrEmpty(name)) throw new ArgumentException(SR.String_null_or_empty, nameof(name)); + if (string.IsNullOrEmpty(name)) + throw new ArgumentException(SR.String_null_or_empty, nameof(name)); if (BaseConfigurationRecord.IsReservedAttributeName(name)) throw new ArgumentException(string.Format(SR.Property_name_reserved, name)); @@ -254,34 +267,34 @@ private void ValidatePropertyName(string name) private void SetDefaultValue(object value) { // Validate the default value if any. This should make errors from invalid defaults easier to catch - if ((value == null) || (value == ConfigurationElement.s_nullPropertyValue)) return; + if (ConfigurationElement.IsNullOrNullProperty(value)) + return; if (!Type.IsInstanceOfType(value)) { - if (Converter.CanConvertFrom(value.GetType())) - value = Converter.ConvertFrom(value); - else + if (!Converter.CanConvertFrom(value.GetType())) throw new ConfigurationErrorsException(string.Format(SR.Default_value_wrong_type, Name)); + + value = Converter.ConvertFrom(value); } Validate(value); DefaultValue = value; } - private void InitDefaultValueFromTypeInfo(ConfigurationPropertyAttribute attribProperty, - DefaultValueAttribute attribStdDefault) + private void InitDefaultValueFromTypeInfo( + ConfigurationPropertyAttribute configurationProperty, + DefaultValueAttribute defaultValueAttribute) { - object defaultValue = attribProperty.DefaultValue; + object defaultValue = configurationProperty.DefaultValue; - // If there is no default value there - try the other attribute ( the clr standard one ) - if (((defaultValue == null) || (defaultValue == ConfigurationElement.s_nullPropertyValue)) && - (attribStdDefault != null)) - defaultValue = attribStdDefault.Value; + // If the ConfigurationPropertyAttribute has no default try the DefaultValueAttribute + if (ConfigurationElement.IsNullOrNullProperty(defaultValue)) + defaultValue = defaultValueAttribute?.Value; - // If there was a default value in the prop attribute - check if we need to convert it from string + // Convert the default value from string if necessary if (defaultValue is string && (Type != typeof(string))) { - // Use the converter to parse this property default value try { defaultValue = Converter.ConvertFromInvariantString((string)defaultValue); @@ -293,14 +306,19 @@ private void SetDefaultValue(object value) } } - if ((defaultValue == null) || (defaultValue == ConfigurationElement.s_nullPropertyValue)) + // If we still have no default, use string Empty for string or the default for value types + if (ConfigurationElement.IsNullOrNullProperty(defaultValue)) { - if (Type == typeof(string)) defaultValue = string.Empty; - else + if (Type == typeof(string)) + { + defaultValue = string.Empty; + } + else if (Type.IsValueType) { - if (Type.IsValueType) defaultValue = TypeUtil.CreateInstance(Type); + defaultValue = TypeUtil.CreateInstance(Type); } } + SetDefaultValue(defaultValue); } @@ -323,21 +341,23 @@ internal object ConvertFromString(string value) internal string ConvertToString(object value) { - string result; - try { if (Type == typeof(bool)) - result = (bool)value ? "true" : "false"; // the converter will break 1.1 compat for bool - else result = Converter.ConvertToInvariantString(value); + { + // The boolean converter will break 1.1 compat for bool + return (bool)value ? "true" : "false"; + } + else + { + return Converter.ConvertToInvariantString(value); + } } catch (Exception ex) { - throw new ConfigurationErrorsException(string.Format(SR.Top_level_conversion_error_to_string, Name, - ex.Message)); + throw new ConfigurationErrorsException( + string.Format(SR.Top_level_conversion_error_to_string, Name, ex.Message)); } - - return result; } internal void Validate(object value) @@ -348,31 +368,37 @@ internal void Validate(object value) } catch (Exception ex) { - throw new ConfigurationErrorsException(string.Format(SR.Top_level_validation_error, Name, ex.Message), - ex); + throw new ConfigurationErrorsException( + string.Format(SR.Top_level_validation_error, Name, ex.Message), ex); } } private void CreateConverter() { - // Some properties cannot have type converters. - // Such examples are properties that are ConfigurationElement ( derived classes ) - // or properties which are user-defined and the user code handles serialization/desirialization so - // the property itself is never converted to/from string - if (_converter != null) return; - // Enums are exception. We use our custom converter for all enums - if (Type.IsEnum) _converter = new GenericEnumConverter(Type); + if (Type.IsEnum) + { + // We use our custom converter for all enums + _converter = new GenericEnumConverter(Type); + } + else if (Type.IsSubclassOf(typeof(ConfigurationElement))) + { + // Type converters aren't allowed on ConfigurationElement + // derived classes. + return; + } else { - if (Type.IsSubclassOf(typeof(ConfigurationElement))) return; - _converter = TypeDescriptor.GetConverter(Type); + if ((_converter == null) || !_converter.CanConvertFrom(typeof(string)) || !_converter.CanConvertTo(typeof(string))) + { + // Need to be able to convert to/from string throw new ConfigurationErrorsException(string.Format(SR.No_converter, Name, Type.Name)); + } } } } diff --git a/src/libraries/System.Configuration/src/System/Configuration/ConnectionStringSettings.cs b/src/libraries/System.Configuration/src/System/Configuration/ConnectionStringSettings.cs index bf17013912d..50e30f28230 100644 --- a/src/libraries/System.Configuration/src/System/Configuration/ConnectionStringSettings.cs +++ b/src/libraries/System.Configuration/src/System/Configuration/ConnectionStringSettings.cs @@ -47,7 +47,7 @@ public ConnectionStringSettings(string name, string connectionString, string pro protected internal override ConfigurationPropertyCollection Properties => s_properties; [ConfigurationProperty("name", - Options = ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsKey, DefaultValue = "")] + Options = ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsKey, DefaultValue = "")] public string Name { get { return (string)base[s_propName]; } diff --git a/src/libraries/System.Configuration/src/System/Configuration/Internal/InternalConfigConfigurationFactory.cs b/src/libraries/System.Configuration/src/System/Configuration/Internal/InternalConfigConfigurationFactory.cs index 6746a5de28f..e70d6d2d3d0 100644 --- a/src/libraries/System.Configuration/src/System/Configuration/Internal/InternalConfigConfigurationFactory.cs +++ b/src/libraries/System.Configuration/src/System/Configuration/Internal/InternalConfigConfigurationFactory.cs @@ -8,7 +8,8 @@ internal sealed class InternalConfigConfigurationFactory : IInternalConfigConfig { private InternalConfigConfigurationFactory() { } - Configuration IInternalConfigConfigurationFactory.Create(Type typeConfigHost, + Configuration IInternalConfigConfigurationFactory.Create( + Type typeConfigHost, params object[] hostInitConfigurationParams) { return new Configuration(null, typeConfigHost, hostInitConfigurationParams); diff --git a/src/libraries/System.Configuration/src/System/Configuration/Internal/WriteFileContext.cs b/src/libraries/System.Configuration/src/System/Configuration/Internal/WriteFileContext.cs index f8c28dc6916..6eecb2b3dbe 100644 --- a/src/libraries/System.Configuration/src/System/Configuration/Internal/WriteFileContext.cs +++ b/src/libraries/System.Configuration/src/System/Configuration/Internal/WriteFileContext.cs @@ -144,9 +144,9 @@ private void ValidateWriteAccess(string filename) } } - // Replace one file with another using MoveFileEx. This will - // retry the operation if the file is locked because someone - // is reading it + /// + /// Replace one file with another, retrying if locked. + /// private void ReplaceFile(string source, string target) { bool writeSucceeded; diff --git a/src/libraries/System.Configuration/src/System/Configuration/KeyValueConfigurationCollection.cs b/src/libraries/System.Configuration/src/System/Configuration/KeyValueConfigurationCollection.cs index 84ed668d007..ee96433b430 100644 --- a/src/libraries/System.Configuration/src/System/Configuration/KeyValueConfigurationCollection.cs +++ b/src/libraries/System.Configuration/src/System/Configuration/KeyValueConfigurationCollection.cs @@ -38,7 +38,9 @@ public void Add(KeyValueConfigurationElement keyValue) // when add is called and teh key already exists. KeyValueConfigurationElement oldValue = (KeyValueConfigurationElement)BaseGet(keyValue.Key); if (oldValue == null) + { BaseAdd(keyValue); + } else { oldValue.Value += "," + keyValue.Value; diff --git a/src/libraries/System.Configuration/src/System/Configuration/ProtectedProviderSettings.cs b/src/libraries/System.Configuration/src/System/Configuration/ProtectedProviderSettings.cs index bc409f584ac..544d88be971 100644 --- a/src/libraries/System.Configuration/src/System/Configuration/ProtectedProviderSettings.cs +++ b/src/libraries/System.Configuration/src/System/Configuration/ProtectedProviderSettings.cs @@ -7,8 +7,11 @@ namespace System.Configuration public class ProtectedProviderSettings : ConfigurationElement { private readonly ConfigurationProperty _propProviders = - new ConfigurationProperty(null, typeof(ProviderSettingsCollection), null, - ConfigurationPropertyOptions.IsDefaultCollection); + new ConfigurationProperty( + name: null, + type: typeof(ProviderSettingsCollection), + defaultValue: null, + options: ConfigurationPropertyOptions.IsDefaultCollection); private readonly ConfigurationPropertyCollection _properties; @@ -20,9 +23,7 @@ public ProtectedProviderSettings() protected internal override ConfigurationPropertyCollection Properties => _properties; - - [ConfigurationProperty("", IsDefaultCollection = true, - Options = ConfigurationPropertyOptions.IsDefaultCollection)] + [ConfigurationProperty("", IsDefaultCollection = true, Options = ConfigurationPropertyOptions.IsDefaultCollection)] public ProviderSettingsCollection Providers => (ProviderSettingsCollection)base[_propProviders]; } } \ No newline at end of file diff --git a/src/libraries/System.Configuration/tests/System.Configuration.Tests.csproj b/src/libraries/System.Configuration/tests/System.Configuration.Tests.csproj index 5db4c6a502c..30d81b27d16 100644 --- a/src/libraries/System.Configuration/tests/System.Configuration.Tests.csproj +++ b/src/libraries/System.Configuration/tests/System.Configuration.Tests.csproj @@ -52,6 +52,12 @@ + + + + + + diff --git a/src/libraries/System.Configuration/tests/System/Configuration/AppSettingsTests.cs b/src/libraries/System.Configuration/tests/System/Configuration/AppSettingsTests.cs index 5aaf0a63498..0828f4a02e8 100644 --- a/src/libraries/System.Configuration/tests/System/Configuration/AppSettingsTests.cs +++ b/src/libraries/System.Configuration/tests/System/Configuration/AppSettingsTests.cs @@ -83,5 +83,26 @@ public void AddToAppSettings_Save() Assert.Equal("NewValue", config.AppSettings.Settings["NewKey"].Value); } } + + [Fact] + public void AppSettingsCannotLoadFromUser() + { + // By default you can't load a section from a user config file- validating that appSettings falls in this bucket + using (var machine = new TempConfig(TestData.ImplicitMachineConfig)) + using (var exe = new TempConfig(TestData.EmptyConfig)) + using (var user = new TempConfig(TestData.SimpleConfig)) + { + ExeConfigurationFileMap map = new ExeConfigurationFileMap + { + MachineConfigFilename = machine.ConfigPath, + ExeConfigFilename = exe.ConfigPath, + RoamingUserConfigFilename = user.ConfigPath + }; + + var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.PerUserRoaming); + + Assert.Throws(() => config.AppSettings); + } + } } } diff --git a/src/libraries/System.Configuration/tests/System/Configuration/BasicCustomSectionTests.cs b/src/libraries/System.Configuration/tests/System/Configuration/BasicCustomSectionTests.cs new file mode 100644 index 00000000000..433e8c30a99 --- /dev/null +++ b/src/libraries/System.Configuration/tests/System/Configuration/BasicCustomSectionTests.cs @@ -0,0 +1,194 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Configuration; +using System.IO; +using Xunit; + +namespace System.ConfigurationTests +{ + public class BasicCustomSectionTests + { + public class SimpleCustomSection : ConfigurationSection + { + [ConfigurationProperty("test")] + public string Test + { + get { return (string)this["test"]; } + set { this["test"] = value; } + } + } + + public static string SimpleCustomData = +@" + + +
+ + +"; + + [Fact] + public void SimpleCustomSectionExists() + { + using (var temp = new TempConfig(SimpleCustomData)) + { + var config = ConfigurationManager.OpenExeConfiguration(temp.ExePath); + SimpleCustomSection section = config.GetSection("simpleCustomSection") as SimpleCustomSection; + Assert.NotNull(section); + Assert.Equal(string.Empty, section.Test); + } + } + + public static string SimpleCustomDataWithValue = +@" + + +
+ + +"; + + [Fact] + public void SimpleCustomSectionWithData() + { + using (var temp = new TempConfig(SimpleCustomDataWithValue)) + { + var config = ConfigurationManager.OpenExeConfiguration(temp.ExePath); + SimpleCustomSection section = config.GetSection("simpleCustomSection") as SimpleCustomSection; + Assert.NotNull(section); + Assert.Equal("Foo", section.Test); + } + } + + public class SimpleCustomSectionRequiredValue : ConfigurationSection + { + [ConfigurationProperty("test", IsRequired = true)] + public string Test + { + get { return (string)this["test"]; } + set { this["test"] = value; } + } + } + + public static string MissingRequiredData = +@" + + +
+ + +"; + + [Fact] + public void SimpleCustomSectionMissingRequired() + { + using (var temp = new TempConfig(MissingRequiredData)) + { + var config = ConfigurationManager.OpenExeConfiguration(temp.ExePath); + + Assert.Throws(() => config.GetSection("simpleCustomSectionRequiredValue") as SimpleCustomSectionRequiredValue); + } + } + + public class SimpleCustomSectionDefaultValue: ConfigurationSection + { + [ConfigurationProperty("test", DefaultValue = "Bar" )] + public string Test + { + get { return (string)this["test"]; } + set { this["test"] = value; } + } + } + + public static string DefaultAppliedData = +@" + + +
+ + +"; + + [Fact] + public void SimpleCustomSectionWithDefault() + { + using (var temp = new TempConfig(DefaultAppliedData)) + { + var config = ConfigurationManager.OpenExeConfiguration(temp.ExePath); + SimpleCustomSectionDefaultValue section = config.GetSection("simpleCustomSectionDefaultValue") as SimpleCustomSectionDefaultValue; + Assert.NotNull(section); + Assert.Equal("Bar", section.Test); + } + } + + public class SimpleDefaultCollectionSection : ConfigurationSection + { + [ConfigurationProperty("", IsDefaultCollection = true)] + public KeyValueConfigurationCollection Settings => (KeyValueConfigurationCollection)base[""]; + } + + public static string SimpleDefaultCollection = +@" + + +
+ + + + + +"; + + [Fact] + public void CustomSectionDefaultCollection() + { + using (var temp = new TempConfig(SimpleDefaultCollection)) + { + var config = ConfigurationManager.OpenExeConfiguration(temp.ExePath); + SimpleDefaultCollectionSection section = config.GetSection("simpleDefaultCollectionSection") as SimpleDefaultCollectionSection; + Assert.NotNull(section); + + Assert.Equal(2, section.Settings.Count); + Assert.Equal("Pear", section.Settings["Fruit"].Value); + Assert.Equal("Cashew", section.Settings["Nut"].Value); + } + } + + public class SimpleCollectionSection : ConfigurationSection + { + [ConfigurationProperty("foods", IsDefaultCollection = true)] + public KeyValueConfigurationCollection Foods => (KeyValueConfigurationCollection)base["foods"]; + } + + public static string SimpleCollection = +@" + + +
+ + + + + + + +"; + + [Fact] + public void CustomSectionCollection() + { + using (var temp = new TempConfig(SimpleCollection)) + { + var config = ConfigurationManager.OpenExeConfiguration(temp.ExePath); + SimpleCollectionSection section = config.GetSection("simpleCollectionSection") as SimpleCollectionSection; + Assert.NotNull(section); + + Assert.Equal(2, section.Foods.Count); + Assert.Equal("Pear", section.Foods["Fruit"].Value); + Assert.Equal("Cashew", section.Foods["Nut"].Value); + } + } + } +} diff --git a/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationElementCollectionTests.cs b/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationElementCollectionTests.cs new file mode 100644 index 00000000000..61a8a6da2f9 --- /dev/null +++ b/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationElementCollectionTests.cs @@ -0,0 +1,187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Configuration; +using System.IO; +using Xunit; + +namespace System.ConfigurationTests +{ + public class ConfigurationElementCollectionTests + { + public class SimpleElement : ConfigurationElement + { + } + + private class SimpleCollection : ConfigurationElementCollection + { + public SimpleCollection() + : base() + { } + + public SimpleCollection(IComparer comparer) + : base(comparer) + { } + + protected override ConfigurationElement CreateNewElement() + { + throw new NotImplementedException(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return "FooKey"; + } + + public bool TestThrowOnDuplicate => ThrowOnDuplicate; + public string TestElementName => ElementName; + + public void TestBaseAdd(ConfigurationElement element) + { + BaseAdd(element); + } + + public void TestBaseAdd(int index, ConfigurationElement element) + { + BaseAdd(index, element); + } + } + + private class ReadOnlySimpleCollection : SimpleCollection + { + public override bool IsReadOnly() => true; + } + + [Fact] + public void NullComparerThrows() + { + Assert.Equal("comparer", Assert.Throws(() => new SimpleCollection(null)).ParamName); + } + + [Fact] + public void ReadOnlyFalseByDefault() + { + Assert.False(new SimpleCollection().IsReadOnly()); + } + + [Fact] + public void InitialCountIsZero() + { + Assert.Equal(0, new SimpleCollection().Count); + } + + [Fact] + public void ElementNameIsEmpty() + { + Assert.Equal("", new SimpleCollection().TestElementName); + } + + [Fact] + public void CollectionTypeIsAddRemoveMap() + { + Assert.Equal(ConfigurationElementCollectionType.AddRemoveClearMap, new SimpleCollection().CollectionType); + } + + [Fact] + public void SyncRootIsNull() + { + Assert.Null(new SimpleCollection().SyncRoot); + } + + [Fact] + public void SynchronizedIsFalse() + { + Assert.False(new SimpleCollection().IsSynchronized); + } + + [Fact] + public void EmitClearIsFalse() + { + Assert.False(new SimpleCollection().EmitClear); + } + + [Fact] + public void SetEmitClear() + { + var collection = new SimpleCollection(); + collection.EmitClear = true; + Assert.True(collection.EmitClear); + } + + [Fact] + public void AddBaseElementSucceeds() + { + var collection = new SimpleCollection(); + collection.TestBaseAdd(new SimpleElement()); + Assert.Equal(1, collection.Count); + } + + [Fact] + public void AddBaseElementAtIndexSucceeds() + { + var collection = new SimpleCollection(); + collection.TestBaseAdd(-1, new SimpleElement()); + Assert.Equal(1, collection.Count); + } + + [Fact] + public void BaseAddIndexOutOfRangeThrows() + { + Assert.Throws(() => new SimpleCollection().TestBaseAdd(-2, null)); + } + + [Fact] + public void BaseAddReadOnlyThrows() + { + Assert.Throws(() => new ReadOnlySimpleCollection().TestBaseAdd(null)); + } + + [Fact] + public void BaseAddIndexReadOnlyThrows() + { + Assert.Throws(() => new ReadOnlySimpleCollection().TestBaseAdd(-1, null)); + } + + [Fact] + public void BaseAddNullThrows() + { + Assert.Throws(() => new SimpleCollection().TestBaseAdd(null)); + } + + [Fact] + public void BaseAddIndexNullThrows() + { + Assert.Throws(() => new SimpleCollection().TestBaseAdd(-1, null)); + } + + [Fact] + public void ThrowOnDuplicateIsTrue() + { + Assert.True(new SimpleCollection().TestThrowOnDuplicate); + } + + public class BasicCollection : ConfigurationElementCollection + { + protected override ConfigurationElement CreateNewElement() + { + throw new NotImplementedException(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + throw new NotImplementedException(); + } + + public override ConfigurationElementCollectionType CollectionType => ConfigurationElementCollectionType.BasicMap; + public bool TestThrowOnDuplicate => ThrowOnDuplicate; + } + + [Fact] + public void BasicThrowOnDuplicateIsFalse() + { + Assert.False(new BasicCollection().TestThrowOnDuplicate); + } + } +} diff --git a/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationElementTests.cs b/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationElementTests.cs new file mode 100644 index 00000000000..37dfd567bbc --- /dev/null +++ b/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationElementTests.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Configuration; +using Xunit; + +namespace System.ConfigurationTests +{ + public class ConfigurationElementTests + { + [Fact] + public void HasNoConfiguration() + { + Assert.Null(new SimpleElement().CurrentConfiguration); + } + + [Fact] + public void HasNoContext() + { + // There should be no configuration record and thus, no context + Assert.False(new SimpleElement().TestHasContext); + } + + [Fact] + public void EvaluationContextThrows() + { + // If we haven't associated a configuration record we should fail + SimpleElement element = new SimpleElement(); + Assert.Throws(() => element.TestEvaluationContext); + } + + [Fact] + public void TransformedNullTypeStringIsNull() + { + Assert.Null(new SimpleElement().TestGetTransformedTypeString(null)); + } + + [Fact] + public void TransformedTypeStringIsOriginal() + { + string foo = "foo"; + + // With no config record the transformed typename should be original + Assert.Same(foo, new SimpleElement().TestGetTransformedTypeString(foo)); + } + + [Fact] + public void TransformedNullAssemblyStringIsNull() + { + Assert.Null(new SimpleElement().TestGetTransformedAssemblyString(null)); + } + + [Fact] + public void TransformedAssemblyStringIsOriginal() + { + string foo = "foo"; + + // With no config record the transformed assembly name should be original + Assert.Same(foo, new SimpleElement().TestGetTransformedAssemblyString(foo)); + } + + public class SimpleElement : ConfigurationElement + { + public ContextInformation TestEvaluationContext => EvaluationContext; + + public bool TestHasContext => HasContext; + + public string TestGetTransformedTypeString(string typeName) + { + return GetTransformedTypeString(typeName); + } + + public string TestGetTransformedAssemblyString(string assemblyName) + { + return GetTransformedAssemblyString(assemblyName); + } + } + + } +} diff --git a/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationPropertyAttributeTests.cs b/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationPropertyAttributeTests.cs new file mode 100644 index 00000000000..478d68b792f --- /dev/null +++ b/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationPropertyAttributeTests.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Configuration; +using Xunit; + +namespace System.ConfigurationTests +{ + public class ConfigurationPropertyAttributeTests + { + [Fact] + public void DefaultValueIsNullObject() + { + // It isn't publicly exposed anywhere else, first check that it is the same object instance here + ConfigurationPropertyAttribute one = new ConfigurationPropertyAttribute("one"); + ConfigurationPropertyAttribute two = new ConfigurationPropertyAttribute("two"); + + Assert.IsType(one.DefaultValue); + Assert.Same(one.DefaultValue, two.DefaultValue); + } + + [Fact] + public void DefaultOptionsIsNone() + { + Assert.Equal(ConfigurationPropertyOptions.None, new ConfigurationPropertyAttribute("foo").Options); + } + + [Fact] + public void IsDefaultCollection() + { + ConfigurationPropertyAttribute attribute = new ConfigurationPropertyAttribute("foo"); + Assert.False(attribute.IsDefaultCollection); + + attribute.Options = ConfigurationPropertyOptions.IsDefaultCollection; + Assert.True(attribute.IsDefaultCollection); + attribute.IsDefaultCollection = false; + Assert.False(attribute.IsDefaultCollection); + + Assert.Equal(ConfigurationPropertyOptions.None, attribute.Options); + } + + [Fact] + public void IsRequired() + { + ConfigurationPropertyAttribute attribute = new ConfigurationPropertyAttribute("foo"); + Assert.False(attribute.IsDefaultCollection); + + attribute.Options = ConfigurationPropertyOptions.IsRequired; + Assert.True(attribute.IsRequired); + attribute.IsRequired = false; + Assert.False(attribute.IsRequired); + + Assert.Equal(ConfigurationPropertyOptions.None, attribute.Options); + } + + [Fact] + public void IsKey() + { + ConfigurationPropertyAttribute attribute = new ConfigurationPropertyAttribute("foo"); + Assert.False(attribute.IsDefaultCollection); + + attribute.Options = ConfigurationPropertyOptions.IsKey; + Assert.True(attribute.IsKey); + attribute.IsKey = false; + Assert.False(attribute.IsKey); + + Assert.Equal(ConfigurationPropertyOptions.None, attribute.Options); + } + } +} diff --git a/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationPropertyTests.cs b/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationPropertyTests.cs new file mode 100644 index 00000000000..1f3fbe34f8f --- /dev/null +++ b/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationPropertyTests.cs @@ -0,0 +1,231 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.Configuration; +using Xunit; + +namespace System.ConfigurationTests +{ + public class ConfigurationPropertyTests + { + [Fact] + public void ConfigurationSectionThrows() + { + Assert.Throws(() => new ConfigurationProperty("foo", typeof(ConfigurationSection))); + } + + [Fact] + public void AppSettingsSectionThrows() + { + Assert.Throws(() => new ConfigurationProperty("foo", typeof(AppSettingsSection))); + } + + [Fact] + public void NullNameThrows() + { + Assert.Throws(() => new ConfigurationProperty(null, typeof(string))); + } + + [Fact] + public void EmptyNameThrows() + { + Assert.Throws(() => new ConfigurationProperty("", typeof(string))); + } + + [Theory + InlineData("lock") + InlineData("locks") + InlineData("config") + InlineData("configuration") + ] + public void ReservedNameThrows(string name) + { + Assert.Throws(() => new ConfigurationProperty(name, typeof(string))); + } + + [Theory + InlineData("Lock") + InlineData("ilock") + InlineData("LOCKS") + InlineData("CoNfig") + InlineData("conFIGuration") + ] + public void ReservedNameOrdinallyCompared(string name) + { + // Want to make sure the comparison is ordinal and starts with if people have depended on this + new ConfigurationProperty(name, typeof(string)); + } + + // Base class returns false for CanValidate by default + public class CantValidateValidator : ConfigurationValidatorBase + { + public override void Validate(object value) + { + throw new NotImplementedException(); + } + } + + [Fact] + public void NonMatchingValidatorThrows() + { + CantValidateValidator validator = new CantValidateValidator(); + Assert.Throws(() => new ConfigurationProperty("foo", typeof(string), null, null, validator, ConfigurationPropertyOptions.None)); + } + + public class FooFailsValidator : ConfigurationValidatorBase + { + public override bool CanValidate(Type type) + { + return true; + } + + public override void Validate(object value) + { + if (string.Equals(value, "foo")) + throw new InvalidOperationException(); + } + } + + [Fact] + public void BadDefaultValueThrows() + { + FooFailsValidator validator = new FooFailsValidator(); + Action action = () => new ConfigurationProperty("bar", typeof(string), "foo", null, validator, ConfigurationPropertyOptions.None); + Assert.IsType(Assert.Throws(action).InnerException); + } + + [TypeConverter(typeof(DummyCantConverter))] + public class SimpleConfigurationElement : ConfigurationElement + { + } + + // By default can't convert from or to + public class DummyCantConverter : TypeConverter + { + } + + public class DummyCanConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return destinationType == typeof(string); + } + } + + [Fact] + public void ConfigurationElementConverterIgnored() + { + ConfigurationProperty property = new ConfigurationProperty("foo", typeof(SimpleConfigurationElement)); + Assert.Null(property.Converter); + } + + [TypeConverter(typeof(DummyCanConverter))] + public class MyConvertableClass + { + } + + [Fact] + public void TypeConverterRecognized() + { + ConfigurationProperty property = new ConfigurationProperty("foo", typeof(MyConvertableClass)); + Assert.IsType(property.Converter); + } + + [TypeConverter(typeof(DummyCantConverter))] + public class MyUnconvertableClass + { + } + + [Fact] + public void UnconvertableFailsOnConverterAccess() + { + ConfigurationProperty property = new ConfigurationProperty("foo", typeof(MyUnconvertableClass)); + Assert.Throws(() => property.Converter); + } + + [TypeConverter(typeof(DummyCantConverter))] + public enum AllSay + { + Yea, + Nay + } + + [Fact] + public void EnumsGetGenericEnumConverter() + { + ConfigurationProperty property = new ConfigurationProperty("foo", typeof(AllSay)); + Assert.IsType(property.Converter); + } + + [Theory + InlineData(typeof(string), typeof(StringConverter)) + InlineData(typeof(int), typeof(Int32Converter)) + ] + public void FindConverterForBuiltInTypes(Type type, Type converterType) + { + ConfigurationProperty property = new ConfigurationProperty("foo", type); + Assert.IsType(converterType, property.Converter); + } + + [Fact] + public void IsRequiredExposed() + { + Assert.False(new ConfigurationProperty("foo", typeof(string)).IsRequired); + Assert.True(new ConfigurationProperty("foo", typeof(string), null, ConfigurationPropertyOptions.IsRequired).IsRequired); + } + + [Fact] + public void IsKeyExposed() + { + Assert.False(new ConfigurationProperty("foo", typeof(string)).IsRequired); + Assert.True(new ConfigurationProperty("foo", typeof(string), null, ConfigurationPropertyOptions.IsKey).IsKey); + } + + [Fact] + public void IsDefaultCollectionExposed() + { + Assert.False(new ConfigurationProperty("foo", typeof(string)).IsDefaultCollection); + Assert.True(new ConfigurationProperty("foo", typeof(string), null, ConfigurationPropertyOptions.IsDefaultCollection).IsDefaultCollection); + } + + [Fact] + public void IsTypeStringTransformationRequiredExposed() + { + Assert.False(new ConfigurationProperty("foo", typeof(string)).IsTypeStringTransformationRequired); + Assert.True(new ConfigurationProperty("foo", typeof(string), null, ConfigurationPropertyOptions.IsTypeStringTransformationRequired).IsTypeStringTransformationRequired); + } + + [Fact] + public void IsAssemblyStringTransformationRequiredExposed() + { + Assert.False(new ConfigurationProperty("foo", typeof(string)).IsAssemblyStringTransformationRequired); + Assert.True(new ConfigurationProperty("foo", typeof(string), null, ConfigurationPropertyOptions.IsAssemblyStringTransformationRequired).IsAssemblyStringTransformationRequired); + } + + [Fact] + public void IsVersionCheckRequiredExposed() + { + Assert.False(new ConfigurationProperty("foo", typeof(string)).IsVersionCheckRequired); + Assert.True(new ConfigurationProperty("foo", typeof(string), null, ConfigurationPropertyOptions.IsVersionCheckRequired).IsVersionCheckRequired); + } + + [Fact] + public void TypeIsExposed() + { + Assert.Equal(typeof(string), new ConfigurationProperty("foo", typeof(string)).Type); + } + + [Fact] + public void DefaultValueIsExposed() + { + Assert.Equal("bar", new ConfigurationProperty("foo", typeof(string), "bar").DefaultValue); + } + } +} diff --git a/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationTests.cs b/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationTests.cs new file mode 100644 index 00000000000..1b7d36165d9 --- /dev/null +++ b/src/libraries/System.Configuration/tests/System/Configuration/ConfigurationTests.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Configuration; +using System.IO; +using Xunit; + +namespace System.ConfigurationTests +{ + public class ConfigurationTests + { + [Fact] + public void UnspecifiedTypeStringTransformer() + { + using (var temp = new TempConfig(TestData.EmptyConfig)) + { + var config = ConfigurationManager.OpenExeConfiguration(temp.ExePath); + Assert.Null(config.TypeStringTransformer); + } + } + + [Fact] + public void SpecifiedTypeStringTransformerReturned() + { + using (var temp = new TempConfig(TestData.EmptyConfig)) + { + var config = ConfigurationManager.OpenExeConfiguration(temp.ExePath); + Func transformer = s => s; + config.TypeStringTransformer = transformer; + Assert.Same(transformer, config.TypeStringTransformer); + } + } + + [Fact] + public void UnspecifiedAssemblyStringTransformer() + { + using (var temp = new TempConfig(TestData.EmptyConfig)) + { + var config = ConfigurationManager.OpenExeConfiguration(temp.ExePath); + Assert.Null(config.AssemblyStringTransformer); + } + } + + [Fact] + public void SpecifiedAssemblyStringTransformerReturned() + { + using (var temp = new TempConfig(TestData.EmptyConfig)) + { + var config = ConfigurationManager.OpenExeConfiguration(temp.ExePath); + Func transformer = s => s; + config.AssemblyStringTransformer = transformer; + Assert.Same(transformer, config.AssemblyStringTransformer); + } + } + } +} diff --git a/src/libraries/System.Configuration/tests/System/Configuration/TestData.cs b/src/libraries/System.Configuration/tests/System/Configuration/TestData.cs index 625ce91fab0..2c0aa0e4884 100644 --- a/src/libraries/System.Configuration/tests/System/Configuration/TestData.cs +++ b/src/libraries/System.Configuration/tests/System/Configuration/TestData.cs @@ -6,6 +6,13 @@ namespace System.ConfigurationTests { public static class TestData { + public static string ImplicitMachineConfig = +@" + +
+ +"; + public static string EmptyConfig = @" @@ -15,8 +22,8 @@ public static class TestData @" - - + + "; } -- GitLab