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.");
        }
Example #12
0
 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);
        }