public async Task CalculateChangesWithSyntaxNodesReturnsResultsUsingProvidedOptions()
        {
            var oldNode = await TestNode.Parse(TestNode.ClassProperty).ConfigureAwait(false);

            var oldNodes = new List <SyntaxNode>
            {
                oldNode
            };
            var newNode = await TestNode.Parse(TestNode.Field).ConfigureAwait(false);

            var newNodes = new List <SyntaxNode>
            {
                newNode
            };
            var options  = new ComparerOptions();
            var expected = new ChangeCalculatorResult();

            var calculator = Substitute.For <IChangeCalculator>();

            calculator.CalculateChanges(
                Arg.Is <IEnumerable <ITypeDefinition> >(x => x.OfType <ClassDefinition>().First().Fields.Count == 0),
                Arg.Is <IEnumerable <ITypeDefinition> >(x => x.OfType <ClassDefinition>().First().Fields.Count == 1),
                options).Returns(expected);

            var actual = calculator.CalculateChanges(oldNodes, newNodes, options);

            actual.Should().Be(expected);
        }
예제 #2
0
        public object Clone()
        {
            var cpi = new CompanyProfile
            {
                Id              = Id,
                Name            = Name,
                Street          = Street,
                City            = City,
                State           = State,
                Zip             = Zip,
                Country         = Country,
                TaxCode         = TaxCode,
                VatCode         = VatCode,
                Email           = Email,
                Web             = Web,
                Phone           = Phone,
                Mobile          = Mobile,
                Note            = Note,
                ContactName     = ContactName,
                ContactEmail    = ContactEmail,
                ContactPhone    = ContactPhone,
                ProfileRate     = (CompanyProfileRate)ProfileRate.Clone(),
                ComparerOptions = (ComparerSettings)ComparerOptions.Clone(),
                MetricGroup     = (QualityMetricGroup)MetricGroup.Clone()
            };



            return(cpi);
        }
        public async Task CalculateChangesWithSyntaxNodesReturnsResultsForMultipleDefinitions()
        {
            var oldNode = await TestNode.Parse(TestNode.MultipleClasses).ConfigureAwait(false);

            var oldNodes = new List <SyntaxNode>
            {
                oldNode
            };
            var newNode = await TestNode.Parse(TestNode.MultipleStructs).ConfigureAwait(false);

            var newNodes = new List <SyntaxNode>
            {
                newNode
            };
            var options  = new ComparerOptions();
            var expected = new ChangeCalculatorResult();

            var calculator = Substitute.For <IChangeCalculator>();

            calculator.CalculateChanges(
                Arg.Is <IEnumerable <ITypeDefinition> >(x => x.OfType <ClassDefinition>().Count() == 2),
                Arg.Is <IEnumerable <ITypeDefinition> >(x => x.OfType <StructDefinition>().Count() == 2),
                options).Returns(expected);

            var actual = calculator.CalculateChanges(oldNodes, newNodes, options);

            actual.Should().Be(expected);
        }
예제 #4
0
        /// <inheritdoc/>
        public ComparerResults CompareResults(string basePath, string diffPath, ComparerOptions options)
        {
            using IReader <T> baseReader = CreateReader(basePath);
            using IReader <T> diffReader = CreateReader(diffPath);

            // since entries in the base and diff may be ordered differently,
            // let's store them in a dictionaries
            Dictionary <string, T> baseResults = new();
            Dictionary <string, T> diffResults = new();

            foreach (T allocation in baseReader)
            {
                baseResults[GetItemId(allocation)] = allocation;
            }

            foreach (T allocation in diffReader)
            {
                diffResults[GetItemId(allocation)] = allocation;
            }

            string metric = string.IsNullOrEmpty(options.Metric) ? DefaultMetric : options.Metric;

            ComparerResults results = new()
            {
                MetricName = GetMetricName(metric),
                Results    = GetResults(baseResults, diffResults, metric).ToArray()
            };

            return(results);
        }
예제 #5
0
        private static void EvaluateArgumentDefinitionChanges(
            ItemMatch <IAttributeDefinition> match,
            ComparerOptions options,
            IChangeResultAggregator aggregator)
        {
            if (EvaluateArgumentCount(match, options, aggregator))
            {
                return;
            }

            if (EvaluateOrdinalArgumentCount(match, options, aggregator))
            {
                return;
            }

            // No need to check for a difference in named argument counts because at this point we have the same number of overall arguments and the same number of ordinal arguments
            // The number of named arguments must be the same

            // At this point we have the same number of ordinal and named arguments
            if (EvaluateOrdinalArgument(match, options, aggregator))
            {
                return;
            }

            EvaluateNamedArgument(match, options, aggregator);
        }
        private static void EvaluateDefaultValueChanges(
            ItemMatch <IParameterDefinition> match,
            ComparerOptions options,
            IChangeResultAggregator aggregator)
        {
            var oldItem = match.OldItem;
            var newItem = match.NewItem;

            if (string.IsNullOrWhiteSpace(oldItem.DefaultValue) &&
                string.IsNullOrWhiteSpace(newItem.DefaultValue) == false)
            {
                // A default value has been added, this is technically a feature because the consuming assembly can treat it like an overload
                var args = new FormatArguments(
                    "{DefinitionType} {Identifier} has added the default value {NewValue}",
                    match.NewItem.FullName,
                    null,
                    newItem.DefaultValue);

                aggregator.AddElementChangedResult(SemVerChangeType.Feature, match, options.MessageFormatter, args);
            }
            else if (string.IsNullOrWhiteSpace(oldItem.DefaultValue) == false &&
                     string.IsNullOrWhiteSpace(newItem.DefaultValue))
            {
                // A default value has been removed
                // This will not be a breaking change for existing applications that happen to use a new binary without recompilation
                // however it does cause a breaking change for compiling existing applications against this API
                var args = new FormatArguments(
                    "{DefinitionType} {Identifier} has removed the default value {OldValue}",
                    match.NewItem.FullName,
                    oldItem.DefaultValue,
                    null);

                aggregator.AddElementChangedResult(SemVerChangeType.Breaking, match, options.MessageFormatter, args);
            }
        }
예제 #7
0
        /// <inheritdoc/>
        public ComparerResults CompareResults(string basePath, string diffPath, ComparerOptions options)
        {
            if (options.StatisticalTestThreshold == null || !Threshold.TryParse(options.StatisticalTestThreshold, out var testThreshold))
            {
                throw new Exception($"Invalid statistical test threshold {options.StatisticalTestThreshold}. Examples: 5%, 10ms, 100ns, 1s.");
            }

            if (options.NoiseThreshold == null || !Threshold.TryParse(options.NoiseThreshold, out var noiseThreshold))
            {
                throw new Exception($"Invalid noise threshold {options.NoiseThreshold}. Examples: 0.3ns 1ns.");
            }

            IEnumerable <(string id, Benchmark baseResult, Benchmark diffResult, EquivalenceTestConclusion conclusion)> notSame =
                GetNotSameResults(basePath, diffPath, options, testThreshold, noiseThreshold).ToArray();

            ComparerResults results = new()
            {
                MetricName = "Median (ns)"
            };

            if (!notSame.Any())
            {
                return(results);
            }

            results.Results = notSame.Select(r =>
                                             new ComparerResult {
                Id         = r.id,
                BaseResult = TransformMeasurementResult(r.baseResult),
                DiffResult = TransformMeasurementResult(r.diffResult),
                Conclusion = TransformConclusion(r.conclusion)
            });

            return(results);
        }
예제 #8
0
        public override IEnumerable <ComparisonResult> CalculateChanges(
            IEnumerable <IAttributeDefinition> oldItems,
            IEnumerable <IAttributeDefinition> newItems,
            ComparerOptions options)
        {
            oldItems = oldItems ?? throw new ArgumentNullException(nameof(oldItems));
            newItems = newItems ?? throw new ArgumentNullException(nameof(newItems));
            options  = options ?? throw new ArgumentNullException(nameof(options));

            if (options.CompareAttributes == AttributeCompareOption.Skip)
            {
                // We are not going to evaluate any attributes
                return(Array.Empty <ComparisonResult>());
            }

            if (options.CompareAttributes == AttributeCompareOption.All)
            {
                return(base.CalculateChanges(oldItems, newItems, options));
            }

            var attributesToMatch = options.AttributeNamesToCompare.ToList();

            // Trim down the items to those where the name matches the compare options
            var oldItemsToCompare = oldItems.Where(x => ShouldCompare(x, attributesToMatch));
            var newItemsToCompare = newItems.Where(x => ShouldCompare(x, attributesToMatch));

            return(base.CalculateChanges(oldItemsToCompare, newItemsToCompare, options));
        }
        public async Task CalculateChangesWithCodeSourceReturnsResultsForMultipleDefinitions()
        {
            var oldCode = new List <CodeSource>
            {
                new(TestNode.MultipleClasses)
            };
            var newCode = new List <CodeSource>
            {
                new(TestNode.MultipleInterfaces)
            };
            var options  = new ComparerOptions();
            var expected = new ChangeCalculatorResult();

            var calculator = Substitute.For <IChangeCalculator>();

            calculator.CalculateChanges(
                Arg.Is <IEnumerable <ITypeDefinition> >(x => x.OfType <ClassDefinition>().Count() == 2),
                Arg.Is <IEnumerable <ITypeDefinition> >(x => x.OfType <InterfaceDefinition>().Count() == 2),
                options).Returns(expected);

            var actual = await calculator.CalculateChanges(oldCode, newCode, options, CancellationToken.None)
                         .ConfigureAwait(false);

            actual.Should().Be(expected);
        }
        public async Task CalculateChangesWithCodeSourceReturnsResultsUsingProvidedOptions()
        {
            var oldCode = new List <CodeSource>
            {
                new(TestNode.ClassProperty)
            };
            var newCode = new List <CodeSource>
            {
                new(TestNode.Field)
            };
            var options  = new ComparerOptions();
            var expected = new ChangeCalculatorResult();

            var calculator = Substitute.For <IChangeCalculator>();

            calculator.CalculateChanges(
                Arg.Is <IEnumerable <ITypeDefinition> >(x => x.OfType <ClassDefinition>().First().Fields.Count == 0),
                Arg.Is <IEnumerable <ITypeDefinition> >(x => x.OfType <ClassDefinition>().First().Fields.Count == 1),
                options).Returns(expected);

            var actual = await calculator.CalculateChanges(oldCode, newCode, options, CancellationToken.None)
                         .ConfigureAwait(false);

            actual.Should().Be(expected);
        }
예제 #11
0
        public IEnumerable <ComparisonResult> CompareMatch(
            ItemMatch <IAccessModifiersElement <T> > match,
            ComparerOptions options)
        {
            var convertedMatch = new ItemMatch <IElementDefinition>(match.OldItem, match.NewItem);

            return(CompareMatch(convertedMatch, match.OldItem.AccessModifiers, match.NewItem.AccessModifiers, options));
        }
        public void AddAttributeNameToCompareReturnsEmptyWhenNoExpressionsAdded()
        {
            var sut = new ComparerOptions();

            var actual = sut.AttributeNamesToCompare;

            actual.Should().BeEmpty();
        }
        public void AddAttributeNameToCompareThrowsExceptionWithNullNameExpression()
        {
            var sut = new ComparerOptions();

            Action action = () => sut.AddAttributeNameToCompare(null !);

            action.Should().Throw <ArgumentNullException>();
        }
        public void CanSetCompareAttributes()
        {
            var sut = new ComparerOptions {
                CompareAttributes = AttributeCompareOption.All
            };

            sut.CompareAttributes.Should().Be(AttributeCompareOption.All);
        }
예제 #15
0
        private void EvaluateFieldChanges(
            ItemMatch <IClassDefinition> match,
            ComparerOptions options,
            IChangeResultAggregator aggregator)
        {
            var changes = _fieldProcessor.CalculateChanges(match.OldItem.Fields, match.NewItem.Fields, options);

            aggregator.AddResults(changes);
        }
예제 #16
0
        protected override void EvaluateSignatureChanges(ItemMatch <T> match, ComparerOptions options,
                                                         IChangeResultAggregator aggregator)
        {
            match   = match ?? throw new ArgumentNullException(nameof(match));
            options = options ?? throw new ArgumentNullException(nameof(options));

            base.EvaluateSignatureChanges(match, options, aggregator);

            RunComparisonStep(EvaluateReturnTypeChanges, match, options, aggregator, true);
        }
예제 #17
0
        protected override void EvaluateModifierChanges(ItemMatch <IPropertyAccessorDefinition> match,
                                                        ComparerOptions options, IChangeResultAggregator aggregator)
        {
            match   = match ?? throw new ArgumentNullException(nameof(match));
            options = options ?? throw new ArgumentNullException(nameof(options));

            base.EvaluateModifierChanges(match, options, aggregator);

            RunComparisonStep(EvaluateAccessModifierChanges, match, options, aggregator, true);
        }
        public void CanSetNewMessageFormatter()
        {
            var messageFormatter = Substitute.For <IMessageFormatter>();

            var sut = new ComparerOptions {
                MessageFormatter = messageFormatter
            };

            sut.MessageFormatter.Should().Be(messageFormatter);
        }
예제 #19
0
        private void EvaluateMethodModifierChanges(
            ItemMatch <IMethodDefinition> match,
            ComparerOptions options,
            IChangeResultAggregator aggregator)
        {
            var convertedMatch = new ItemMatch <IModifiersElement <MethodModifiers> >(match.OldItem, match.NewItem);

            var results = _methodModifiersComparer.CompareMatch(convertedMatch, options);

            aggregator.AddResults(results);
        }
예제 #20
0
        private void EvaluateGenericTypeDefinitionChanges(
            ItemMatch <IMethodDefinition> match,
            ComparerOptions options,
            IChangeResultAggregator aggregator)
        {
            var convertedMatch = new ItemMatch <IGenericTypeElement>(match.OldItem, match.NewItem);

            var results = _genericTypeElementComparer.CompareMatch(convertedMatch, options);

            aggregator.AddResults(results);
        }
        public void AddAttributeNameToCompareAddsNewExpression()
        {
            var expression = new Regex("JsonProperty");

            var sut = new ComparerOptions();

            sut.AddAttributeNameToCompare(expression);

            var actual = sut.AttributeNamesToCompare;

            actual.Should().Contain(expression);
        }
예제 #22
0
        private void EvaluateAccessModifierChanges(
            ItemMatch <IPropertyAccessorDefinition> match,
            ComparerOptions options,
            IChangeResultAggregator aggregator)
        {
            var convertedMatch =
                new ItemMatch <IAccessModifiersElement <PropertyAccessorAccessModifiers> >(match.OldItem, match.NewItem);

            var results = _propertyAccessorAccessModifiersComparer.CompareMatch(convertedMatch, options);

            aggregator.AddResults(results);
        }
        public void CompareMatchThrowsExceptionWithNullMatch()
        {
            var options = new ComparerOptions();

            var changeTable = Substitute.For <IAccessModifiersChangeTable>();

            var sut = new Wrapper(changeTable);

            Action action = () =>
                            sut.RunCompareMatch(null !, AccessModifiers.Internal, AccessModifiers.Private, options)
                            .ForceEnumeration();

            action.Should().Throw <ArgumentNullException>();
        }
예제 #24
0
        private void EvaluateParameterChanges(
            ItemMatch <IMethodDefinition> match,
            ComparerOptions options,
            IChangeResultAggregator aggregator)
        {
            var oldParameters = match.OldItem.Parameters.FastToList();
            var newParameters = match.NewItem.Parameters.FastToList();

            if (oldParameters.Count == 0 &&
                newParameters.Count == 0)
            {
                return;
            }

            var parameterShift = oldParameters.Count - newParameters.Count;

            if (parameterShift != 0)
            {
                var changeLabel = parameterShift > 0 ? "removed" : "added";
                var shiftAmount = Math.Abs(parameterShift);

                // One or more generic type parameters have been added or removed
                var suffix = shiftAmount == 1 ? "" : "s";
                var args   = new FormatArguments(
                    $"{{DefinitionType}} {{Identifier}} has {changeLabel} {shiftAmount} parameter{suffix}",
                    match.NewItem.FullName,
                    null,
                    null);

                aggregator.AddElementChangedResult(SemVerChangeType.Breaking, match, options.MessageFormatter, args);

                // No need to look into how the generic type has changed
                return;
            }

            // We have the same number of parameters, compare them
            for (var index = 0; index < oldParameters.Count; index++)
            {
                var oldParameter   = oldParameters[index];
                var newParameter   = newParameters[index];
                var parameterMatch = new ItemMatch <IParameterDefinition>(oldParameter, newParameter);

                var parameterChanges = _parameterComparer.CompareMatch(parameterMatch, options);

                aggregator.AddResults(parameterChanges);
            }
        }
예제 #25
0
        /// <inheritdoc/>
        public void GenerateReport(ComparerResults results, Stream destination, ComparerOptions options, bool leaveStreamOpen = false)
        {
            ComparerResult[] resultsArray = results.Results.ToArray();

            if (resultsArray.Length == 0)
            {
                return;
            }

            using StreamWriter writer = new(destination, leaveOpen : leaveStreamOpen);
            PrintSummary(resultsArray, writer);
            PrintTable(resultsArray, results.MetricName, ComparisonConclusion.Worse, writer, options);
            PrintTable(resultsArray, results.MetricName, ComparisonConclusion.Better, writer, options);
            PrintTable(resultsArray, results.MetricName, ComparisonConclusion.New, writer, options);
            PrintTable(resultsArray, results.MetricName, ComparisonConclusion.Missing, writer, options);
            writer.Flush();
        }
예제 #26
0
        private static void EvaluateReturnTypeChanges(ItemMatch <T> match, ComparerOptions options,
                                                      IChangeResultAggregator aggregator)
        {
            var oldType = match.OldItem.ReturnType;
            var newType = match.NewItem.ReturnType;

            IGenericTypeElement deepestNewGenericTypeElement;
            IGenericTypeElement deepestOldGenericTypeElement;

            // We need to check whether the element itself can declare generic type parameters
            // If not, the declaring type will be used for generic type parameter mapping
            if (match.OldItem is IGenericTypeElement oldElement &&
                match.NewItem is IGenericTypeElement newElement)
            {
                deepestOldGenericTypeElement = oldElement;
                deepestNewGenericTypeElement = newElement;
            }
예제 #27
0
        public void ComparerReportFilesAndGenerateMarkdownReport(string comparerId, ComparerOptions options, string basePath, string diffPath, string reportPath)
        {
            IResultsComparerProvider comparerProvider = ComparerProviderFactory.CreateDefaultProvider();
            IResultsComparer         comparer         = comparerProvider.GetById(comparerId);

            ComparerResults  results  = comparer.CompareResults(basePath, diffPath, options);
            MarkdownReporter reporter = new();

            using MemoryStream output = new();
            reporter.GenerateReport(results, output, options, leaveStreamOpen: true);

            output.Seek(0, SeekOrigin.Begin);
            string expectedReport = File.ReadAllText(reportPath);
            string actualReport   = new StreamReader(output).ReadToEnd();

            Assert.Equal(expectedReport, actualReport);
        }
        public void CompareMatchReturnsEmptyWhenNoChangeFound()
        {
            var options    = new ComparerOptions();
            var oldElement = new TestPropertyDefinition();
            var newElement = new TestPropertyDefinition();
            var match      = new ItemMatch <IElementDefinition>(oldElement, newElement);

            var changeTable = Substitute.For <IAccessModifiersChangeTable>();

            changeTable.CalculateChange(oldElement.AccessModifiers, newElement.AccessModifiers)
            .Returns(SemVerChangeType.None);

            var sut = new Wrapper(changeTable);

            var actual = sut.RunCompareMatch(match, AccessModifiers.Internal, AccessModifiers.Private, options);

            actual.Should().BeEmpty();
        }
예제 #29
0
        private static void EvaluateMethodNameChanges(
            ItemMatch <IMethodDefinition> match,
            ComparerOptions options,
            IChangeResultAggregator aggregator)
        {
            if (match.OldItem.RawName == match.NewItem.RawName)
            {
                return;
            }

            var args = new FormatArguments(
                "{DefinitionType} {Identifier} has been renamed to {NewValue}",
                match.OldItem.FullName,
                null,
                match.NewItem.Name);

            aggregator.AddElementChangedResult(SemVerChangeType.Breaking, match, options.MessageFormatter, args);
        }
예제 #30
0
        public IEnumerable <ComparisonResult> CompareMatch(ItemMatch <IAttributeDefinition> match,
                                                           ComparerOptions options)
        {
            match   = match ?? throw new ArgumentNullException(nameof(match));
            options = options ?? throw new ArgumentNullException(nameof(options));

            if (match.OldItem.Arguments.Count == 0 &&
                match.NewItem.Arguments.Count == 0)
            {
                // The attributes do not have any arguments
                // These attributes match each other
                return(Array.Empty <ComparisonResult>());
            }

            var aggregator = new ChangeResultAggregator();

            EvaluateArgumentDefinitionChanges(match, options, aggregator);

            return(aggregator.Results);
        }