public void Configuration_Ctor_ExclusiveRequirementsSupplied_Multiple_ThrowsAggregate() { const int numberOfConflictingRequirements = 5; List <IConfigurationRequirement> requirements = new List <IConfigurationRequirement>() { TestUtil.CreateConfigurationRequirement() }; for (int counter = 0; counter < numberOfConflictingRequirements; counter++) { requirements.Add(TestUtil.CreateConfigurationRequirement( exclusiveWith: new IConfigurationRequirement[] { requirements.Last() })); } Dictionary <IConfigurationRequirement, object> values = requirements .Select(x => new KeyValuePair <IConfigurationRequirement, object>( x, TestUtil.GetDefaultValidObjectForRequirement(x))) .ToDictionary(x => x.Key, x => x.Value); IRequirementSource configurable = ConfigurationTests.CreateRequirementSource(requirements.ToArray()); AggregateException exception = Assert.ThrowsException <AggregateException>(() => new Configuration(configurable, values)); Assert.AreEqual(numberOfConflictingRequirements, exception.InnerExceptions.Count); Assert.IsTrue( exception .InnerExceptions .Select(x => x.Message) .All(x => x.Contains("has conflicting requirements specified.")), "Unexpected exception message encountered."); }
public void Configuration_CanBeEnumerated_Succeeds() { const int numberOfRequirements = 50; IConfigurationRequirement[] requirements = TestUtil .CreateIConfigurationRequirementCollection(numberOfRequirements, randomTypes: true) .ToArray(); Dictionary <IConfigurationRequirement, object> supplied = requirements .Select(x => new KeyValuePair <IConfigurationRequirement, object>( x, TestUtil.GetDefaultValidObjectForRequirement(x))) .ToDictionary(x => x.Key, x => x.Value); IMapping[] expected = supplied.Select(x => new Mapping(x.Key, x.Value)).ToArray(); IRequirementSource requirementSource = ConfigurationTests.CreateRequirementSource(requirements); IConfiguration configuration = new Configuration(requirementSource, supplied); IMapping[] actual = configuration.ToArray(); CollectionAssert.AreEquivalent(expected, actual); }
public void Configuration_Ctor_RequiredDependsOnMissing_MultipleMissing_ThrowsAggregate() { const int numberOfMissingDependedUpons = 5; IConfigurationRequirement[] dependedUpons = Enumerable .Range(1, numberOfMissingDependedUpons) .Select(x => TestUtil.CreateConfigurationRequirement(isOptional: true)) .ToArray(); IConfigurationRequirement[] dependsOns = dependedUpons .Select(x => TestUtil.CreateConfigurationRequirement(dependsOn: new IConfigurationRequirement[] { x })) .ToArray(); Dictionary <IConfigurationRequirement, object> values = dependsOns .Select(x => new KeyValuePair <IConfigurationRequirement, object>( x, TestUtil.GetDefaultValidObjectForRequirement(x))) .ToDictionary(x => x.Key, x => x.Value); IRequirementSource configurable = ConfigurationTests.CreateRequirementSource(dependsOns); AggregateException exception = Assert.ThrowsException <AggregateException>(() => new Configuration(configurable, values)); Assert.AreEqual(numberOfMissingDependedUpons, exception.InnerExceptions.Count); Assert.IsTrue( exception .InnerExceptions .Select(x => x.Message) .All(x => x.Contains("does not have its dependencies fulfilled.")), "Unexpected exception message encountered."); }
public void Configuration_Ctor_MultipleFailuresOccur_ThrowsAggregateException() { const string validationFailureMessage = "BOUNDCONFIGURATIONTESTS_MultipleFailuresOccur"; const string testFailureMessage = "Missing/too many matches for expected exception."; const int expectedFailureCount = 4; IConfigurationRequirement requiredButMissing = TestUtil.CreateConfigurationRequirement( baseName: nameof(requiredButMissing), isOptional: false); IConfigurationRequirement dependsOnIsMissing = TestUtil.CreateConfigurationRequirement( baseName: nameof(dependsOnIsMissing), dependsOn: new IConfigurationRequirement[] { requiredButMissing }); IConfigurationRequirement failsValidation = TestUtil.CreateConfigurationRequirement( baseName: nameof(failsValidation), validator: (x, y, z) => throw new NotImplementedException(validationFailureMessage)); IConfigurationRequirement exclusiveWith = TestUtil.CreateConfigurationRequirement( baseName: nameof(exclusiveWith), exclusiveWith: new IConfigurationRequirement[] { failsValidation }); IConfigurationRequirement isFine = TestUtil.CreateConfigurationRequirement( baseName: nameof(isFine), validator: (x, y, z) => null); IRequirementSource configurable = ConfigurationTests.CreateRequirementSource( requiredButMissing, dependsOnIsMissing, failsValidation, exclusiveWith, isFine); Dictionary <IConfigurationRequirement, object> bindings = new IConfigurationRequirement[] { dependsOnIsMissing, failsValidation, exclusiveWith, isFine } .Select(x => new KeyValuePair <IConfigurationRequirement, object>(x, null)) .ToDictionary(x => x.Key, x => x.Value); AggregateException exception = Assert.ThrowsException <AggregateException>(() => new Configuration(configurable, bindings)); Assert.AreEqual(expectedFailureCount, exception.InnerExceptions.Count); Assert.IsNotNull( exception .InnerExceptions .SingleOrDefault(x => x.Message.Contains(validationFailureMessage)), testFailureMessage); Assert.IsNotNull( exception .InnerExceptions .SingleOrDefault(x => x.Message.Contains("has conflicting requirements specified.")), testFailureMessage); Assert.IsNotNull( exception .InnerExceptions .SingleOrDefault(x => x.Message.Contains("does not have its dependencies fulfilled.")), testFailureMessage); Assert.IsNotNull( exception .InnerExceptions .SingleOrDefault(x => x.Message.Contains("Missing required requirement")), testFailureMessage); }
public void Configuration_Ctor_RequiredRequirementIsNotPresent_ThrowsAggregate() { IConfigurationRequirement required = TestUtil.CreateConfigurationRequirement(isOptional: false); IRequirementSource configurable = ConfigurationTests.CreateRequirementSource(required); AggregateException exception = Assert.ThrowsException <AggregateException>(() => new Configuration(configurable, new Dictionary <IConfigurationRequirement, object>())); Assert.AreEqual(1, exception.InnerExceptions.Count); StringAssert.Contains(exception.InnerExceptions.Single().Message, "Missing required requirement"); }
private static Configuration CreateConfiguration( Func <IConfigurationRequirement, object> valueFactory, out IConfigurationRequirement requirement, Func <IConfigurationRequirement> requirementFactory = null) { requirement = (requirementFactory ?? (() => TestUtil.CreateConfigurationRequirement())).Invoke(); Dictionary <IConfigurationRequirement, object> bindings = new Dictionary <IConfigurationRequirement, object>() { [requirement] = valueFactory.Invoke(requirement) }; IRequirementSource configurable = ConfigurationTests.CreateRequirementSource(requirement); return(new Configuration(configurable, bindings)); }
public void Configuration_Indexer_Get_Succeeds() { IConfigurationRequirement requirement = TestUtil.CreateConfigurationRequirement(); object expected = TestUtil.GetDefaultValidObjectForRequirement(requirement); Dictionary <IConfigurationRequirement, object> bindings = new Dictionary <IConfigurationRequirement, object>() { [requirement] = expected }; IRequirementSource configurable = ConfigurationTests.CreateRequirementSource(requirement); Configuration configuration = new Configuration(configurable, bindings); Assert.AreEqual(expected, configuration[requirement]); }
public void Configuration_GetOrDefault_ValueNotPresent_UsesValueFactory() { const string fallbackValue = "Configuration_GetOrDefault"; IConfigurationRequirement present = TestUtil.CreateConfigurationRequirement(); IConfigurationRequirement notPresent = TestUtil.CreateConfigurationRequirement(isOptional: true); object expected = TestUtil.GetDefaultValidObjectForRequirement(present); Dictionary <IConfigurationRequirement, object> bindings = new Dictionary <IConfigurationRequirement, object>() { [present] = expected }; IRequirementSource configurable = ConfigurationTests.CreateRequirementSource(notPresent); Configuration configuration = new Configuration(configurable, bindings); Assert.AreEqual(fallbackValue, configuration.GetOrDefault(notPresent, () => fallbackValue)); }
public void Configuration_Ctor_RequirementFailsValidation_ThrowsAggregate() { const string exceptionMessage = "BOUNDCONFIGURATIONTESTS_RequirementFailsValidation"; IConfigurationRequirement required = TestUtil.CreateConfigurationRequirement( validator: (x, y, z) => throw new NotImplementedException(exceptionMessage)); IRequirementSource configurable = ConfigurationTests.CreateRequirementSource(required); AggregateException exception = Assert.ThrowsException <AggregateException>(() => new Configuration( configurable, new Dictionary <IConfigurationRequirement, object>() { [required] = "dontCare" })); Assert.AreEqual(1, exception.InnerExceptions.Count); Assert.AreEqual(exceptionMessage, exception.InnerExceptions.Single().Message); }
public void Configuration_Ctor_RequirementDependsOnMissing_ThrowsAggregate() { // dependedUpon needs to be optional or we'll get two errors (because required requirement is missing). IConfigurationRequirement dependedUpon = TestUtil.CreateConfigurationRequirement(isOptional: true); IConfigurationRequirement dependsOn = TestUtil.CreateConfigurationRequirement( type: ConfigurationRequirementType.String, dependsOn: new IConfigurationRequirement[] { dependedUpon }); IRequirementSource configurable = ConfigurationTests.CreateRequirementSource(dependedUpon, dependsOn); AggregateException exception = Assert.ThrowsException <AggregateException>(() => new Configuration( configurable, new Dictionary <IConfigurationRequirement, object>() { [dependsOn] = "Passes validation" })); Assert.AreEqual(1, exception.InnerExceptions.Count); StringAssert.Contains( exception.InnerExceptions.Single().Message, "does not have its dependencies fulfilled."); }
public void Configuration_Ctor_ExclusiveRequirementsSupplied_ThrowsAggregate() { IConfigurationRequirement first = TestUtil.CreateConfigurationRequirement( type: ConfigurationRequirementType.String); IConfigurationRequirement second = TestUtil.CreateConfigurationRequirement( type: ConfigurationRequirementType.String, exclusiveWith: new IConfigurationRequirement[] { first }); IRequirementSource configurable = ConfigurationTests.CreateRequirementSource(first, second); AggregateException exception = Assert.ThrowsException <AggregateException>(() => new Configuration( configurable, new Dictionary <IConfigurationRequirement, object>() { [first] = "Passes validation", [second] = "Also passes validation" })); Assert.AreEqual(1, exception.InnerExceptions.Count); StringAssert.Contains( exception.InnerExceptions.Single().Message, "has conflicting requirements specified."); }
public MockConfigurator(IRequirementSource source, ConfigureFunc configureFunc) { this.source = source; this.configureFunc = configureFunc; }
public Configuration( IRequirementSource requirementSource, IReadOnlyDictionary <IConfigurationRequirement, object> mappings, IConfigurator configurator = null) { if (requirementSource == null) { throw new ArgumentNullException(nameof(requirementSource)); } if (mappings == null) { throw new ArgumentNullException(nameof(mappings)); } if (requirementSource.Requirements == null) { throw new InvalidRequirementsException( Configuration.ConfigurationRequirementsMustNotBeNull, nameof(requirementSource)); } Dictionary <IConfigurationRequirement, object> backingDictionary = mappings.ToDictionary(x => x.Key, x => x.Value); List <Exception> failures = new List <Exception>(); // Check for missing requirements. failures.AddRange(requirementSource .Requirements .Where(x => !x.IsOptional && !backingDictionary.Keys.Contains(x)) .Select(x => new ArgumentException( string.Format( CultureInfo.InvariantCulture, Configuration.MissingRequirement, x.Name)))); // Check for DependsOn failures. IConfigurationRequirement[] dependsOnFailures = backingDictionary .Keys .Where(x => x.DependsOn.Any(y => !mappings.Keys.Contains(y))) .ToArray(); failures.AddRange(dependsOnFailures .Select(x => new ArgumentException( string.Format( CultureInfo.InvariantCulture, Configuration.DependenciesNotSatisfied, x.Name)))); // Check for ExclusiveWith failures. IConfigurationRequirement[] exclusiveConflicts = backingDictionary .Keys .Where(x => x.ExclusiveWith.Any(y => mappings.Keys.Contains(y))) .ToArray(); failures.AddRange(exclusiveConflicts .Select(x => new ArgumentException( string.Format( CultureInfo.InvariantCulture, Configuration.ConflictingRequirementsSpecified, x.Name)))); // Remove ExclusiveWith/DependsOn errors from the set of validations to execute. foreach (IConfigurationRequirement cantValidate in exclusiveConflicts.Concat(dependsOnFailures)) { backingDictionary.Remove(cantValidate); } // Validate the remaining requirements. // We can only reach this after removing all requirements that don't have their prerequisites. // We need to determine what "level" in the heirarchy each requirement is at. // If a requirement has no dependsOns, then it's at level "0" (can be immediately evaluated) // For each requirement which has all dependsOns in the set of previously evaluated requirements, add it // Repeat until all requirements have been added (this can always be reached, because we removed all // requirements which didn't have all their dependsOns satisfied) IList <IConfigurationRequirement> toEvaluate = new List <IConfigurationRequirement>(); void RecursiveAdd( ref IList <IConfigurationRequirement> addTo, IReadOnlyList <IConfigurationRequirement> remaining) { if (!remaining.Any()) { return; } foreach (IConfigurationRequirement toCheck in remaining) { if (!toCheck.DependsOn.Any() || toCheck.DependsOn.All(x => toEvaluate.Contains(x))) { toEvaluate.Add(toCheck); } } } RecursiveAdd(ref toEvaluate, backingDictionary.Keys.ToList()); // We can now iterate through toEvaluate; at each requirement, we set of previously completed requirements // will satisfy all dependsOns. Dictionary <IConfigurationRequirement, object> completed = new Dictionary <IConfigurationRequirement, object>(); foreach (IConfigurationRequirement requirement in toEvaluate) { object value = mappings[requirement]; Exception failure = requirement.Validate( value, new SimpleConfiguration(completed .Where(x => requirement.DependsOn.Contains(x.Key)) .ToDictionary(x => x.Key, x => x.Value))); if (failure == null) { completed.Add(requirement, value); } else { failures.Add(failure); // This isn't really required, but just for future-proofing... backingDictionary.Remove(requirement); } } if (failures.Any()) { throw new AggregateException( Configuration.RequirementsFailedValidation, failures); } this.backingConfiguration = new SimpleConfiguration(completed, configurator); }