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); }
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); }
/// <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); }
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); } }
/// <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); }
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); }
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); }
private void EvaluateFieldChanges( ItemMatch <IClassDefinition> match, ComparerOptions options, IChangeResultAggregator aggregator) { var changes = _fieldProcessor.CalculateChanges(match.OldItem.Fields, match.NewItem.Fields, options); aggregator.AddResults(changes); }
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); }
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); }
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); }
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); }
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>(); }
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); } }
/// <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(); }
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; }
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(); }
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); }
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); }