diff --git a/DotNetApiDiff.sln b/DotNetApiDiff.sln
index 800275a..739e04a 100644
--- a/DotNetApiDiff.sln
+++ b/DotNetApiDiff.sln
@@ -1,4 +1,4 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
@@ -6,6 +6,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetApiDiff", "src\DotNet
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetApiDiff.Tests", "tests\DotNetApiDiff.Tests\DotNetApiDiff.Tests.csproj", "{B2C3D4E5-F6G7-8901-BCDE-F23456789012}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{9E9A40DF-93D7-4C97-A4B2-EFD48382576D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestAssemblyV1", "tests\TestAssemblies\TestAssemblyV1.csproj", "{5E8D074B-6E7B-4730-BBBE-4E81841DA8D2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestAssemblyV2", "tests\TestAssemblies\TestAssemblyV2.csproj", "{4EAC9034-1137-4E2A-A70B-D8CEB248F107}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -20,5 +26,17 @@ Global
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5E8D074B-6E7B-4730-BBBE-4E81841DA8D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5E8D074B-6E7B-4730-BBBE-4E81841DA8D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5E8D074B-6E7B-4730-BBBE-4E81841DA8D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5E8D074B-6E7B-4730-BBBE-4E81841DA8D2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4EAC9034-1137-4E2A-A70B-D8CEB248F107}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4EAC9034-1137-4E2A-A70B-D8CEB248F107}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4EAC9034-1137-4E2A-A70B-D8CEB248F107}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4EAC9034-1137-4E2A-A70B-D8CEB248F107}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {5E8D074B-6E7B-4730-BBBE-4E81841DA8D2} = {9E9A40DF-93D7-4C97-A4B2-EFD48382576D}
+ {4EAC9034-1137-4E2A-A70B-D8CEB248F107} = {9E9A40DF-93D7-4C97-A4B2-EFD48382576D}
EndGlobalSection
-EndGlobal
\ No newline at end of file
+EndGlobal
diff --git a/Taskfile.yml b/Taskfile.yml
index 0d86e6c..2186927 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -7,6 +7,7 @@ vars:
SOLUTION: DotNetApiDiff.sln
PROJECT: src/DotNetApiDiff/DotNetApiDiff.csproj
TEST_PROJECT: tests/DotNetApiDiff.Tests/DotNetApiDiff.Tests.csproj
+ TEST_ASSEMBLIES_DIR: tests/TestAssemblies
COVERAGE_DIR: coverage-report
TEST_REPORT_DIR: test-report
@@ -21,11 +22,25 @@ tasks:
cmds:
- dotnet build {{.SOLUTION}} --configuration Release
+ build:test-assemblies:
+ desc: Build test assemblies and copy to TestData
+ cmds:
+ - cd {{.TEST_ASSEMBLIES_DIR}} && ./build-test-assemblies.sh
+
+ clean:test-assemblies:
+ desc: Clean test assemblies build outputs
+ cmds:
+ - dotnet clean {{.TEST_ASSEMBLIES_DIR}}/TestAssemblyV1.csproj
+ - dotnet clean {{.TEST_ASSEMBLIES_DIR}}/TestAssemblyV2.csproj
+ - rm -f tests/DotNetApiDiff.Tests/TestData/TestAssemblyV1.dll
+ - rm -f tests/DotNetApiDiff.Tests/TestData/TestAssemblyV2.dll
+
clean:
desc: Clean build outputs
cmds:
- dotnet clean {{.SOLUTION}}
- task: clean:coverage
+ - task: clean:test-assemblies
clean:coverage:
desc: Clean coverage reports
@@ -49,6 +64,7 @@ tasks:
test:integration:
desc: Run integration tests only
+ deps: [build:test-assemblies]
cmds:
- dotnet test {{.TEST_PROJECT}} --filter "Category=Integration" --configuration Release
@@ -213,5 +229,6 @@ tasks:
- task: restore
- task: code:quality
- task: build
+ - task: build:test-assemblies
- task: test
- task: coverage
diff --git a/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerManualTests.cs b/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerManualTests.cs
index 05d59c6..4c46945 100644
--- a/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerManualTests.cs
+++ b/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerManualTests.cs
@@ -1,75 +1,294 @@
-// Copyright DotNet API Diff Project Contributors - SPDX Identifier: MIT
+using System.Reflection;
using DotNetApiDiff.ApiExtraction;
+using DotNetApiDiff.AssemblyLoading;
using DotNetApiDiff.Interfaces;
using DotNetApiDiff.Models;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
+using DotNetApiDiff.Models.Configuration;
using Moq;
using Xunit;
+using Xunit.Abstractions;
-namespace DotNetApiDiff.Tests.ApiExtraction;
-
-public class ApiComparerManualTests
+namespace DotNetApiDiff.Tests.ApiExtraction
{
- [Fact]
- public void CompareMembers_DetectsRemovedMembers_ManualTest()
+ ///
+ /// Integration tests using manually created test assemblies
+ ///
+ public class ApiComparerManualTests
{
- // Arrange
- Console.WriteLine("Starting CompareMembers_DetectsRemovedMembers_ManualTest");
+ private readonly string _testAssemblyV1Path;
+ private readonly string _testAssemblyV2Path;
+ private readonly IAssemblyLoader _assemblyLoader;
+ private readonly IApiExtractor _apiExtractor;
+ private readonly INameMapper _nameMapper;
+ private readonly IDifferenceCalculator _differenceCalculator;
+ private readonly IChangeClassifier _changeClassifier;
+ private readonly IApiComparer _apiComparer;
+ private readonly ITestOutputHelper _output;
+
+ public ApiComparerManualTests(ITestOutputHelper output)
+ {
+ _output = output;
+
+ // For the purpose of this test, we'll use the test assemblies from the TestData directory
+ string testDataDir = Path.Combine(
+ Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty,
+ "TestData");
+
+ _testAssemblyV1Path = Path.Combine(testDataDir, "TestAssemblyV1.dll");
+ _testAssemblyV2Path = Path.Combine(testDataDir, "TestAssemblyV2.dll");
+
+ _output.WriteLine($"Test assembly V1 path: {_testAssemblyV1Path}");
+ _output.WriteLine($"Test assembly V2 path: {_testAssemblyV2Path}");
+
+ // Skip tests if the test assemblies don't exist
+ if (!File.Exists(_testAssemblyV1Path) || !File.Exists(_testAssemblyV2Path))
+ {
+ _output.WriteLine("Test assemblies not found. Skipping tests.");
+ return;
+ }
+
+ // Create the real components for integration testing
+ _assemblyLoader = new AssemblyLoader();
+ _apiExtractor = new ApiExtractor(new TypeAnalyzer(new MemberSignatureBuilder()));
+ _nameMapper = new NameMapper();
+ _differenceCalculator = new DifferenceCalculator();
+ _changeClassifier = new ChangeClassifier();
+ _apiComparer = new ApiComparer(
+ _assemblyLoader,
+ _apiExtractor,
+ _nameMapper,
+ _differenceCalculator,
+ _changeClassifier);
+
+ // Create the real components for integration testing
+ _assemblyLoader = new AssemblyLoader();
+ _apiExtractor = new ApiExtractor(new TypeAnalyzer(new MemberSignatureBuilder()));
+ _nameMapper = new NameMapper();
+ _differenceCalculator = new DifferenceCalculator();
+ _changeClassifier = new ChangeClassifier();
+ _apiComparer = new ApiComparer(
+ _assemblyLoader,
+ _apiExtractor,
+ _nameMapper,
+ _differenceCalculator,
+ _changeClassifier);
+ }
+
+ [Fact]
+ public void Compare_WithTestAssemblies_DetectsBasicChanges()
+ {
+ // Skip test if assemblies don't exist
+ if (!File.Exists(_testAssemblyV1Path) || !File.Exists(_testAssemblyV2Path))
+ {
+ _output.WriteLine("Test assemblies not found. Skipping test.");
+ return;
+ }
+
+ // Arrange
+ var config = new ComparisonConfiguration();
+
+ // Act
+ var result = _apiComparer.Compare(_testAssemblyV1Path, _testAssemblyV2Path, config);
+
+ // Assert
+ Assert.NotNull(result);
+
+ // Verify additions
+ Assert.Contains(result.Additions, change =>
+ change.TargetMember.Name == "NewMethod" &&
+ change.TargetMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.Contains(result.Additions, change =>
+ change.TargetMember.Name == "NewProperty" &&
+ change.TargetMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.Contains(result.Additions, change =>
+ change.TargetMember.Name == "NewField" &&
+ change.TargetMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.Contains(result.Additions, change =>
+ change.TargetMember.Name == "NewEvent" &&
+ change.TargetMember.DeclaringType == "TestAssembly.PublicClass");
+
+ // Verify removals
+ Assert.Contains(result.Removals, change =>
+ change.SourceMember.Name == "RemovedMethod" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.Contains(result.Removals, change =>
+ change.SourceMember.Name == "RemovedProperty" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.Contains(result.Removals, change =>
+ change.SourceMember.Name == "RemovedField" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.Contains(result.Removals, change =>
+ change.SourceMember.Name == "RemovedEvent" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+
+ // Verify modifications
+ Assert.Contains(result.Modifications, change =>
+ change.SourceMember.Name == "ChangedSignatureMethod" &&
+ change.TargetMember.Name == "ChangedSignatureMethod" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.Contains(result.Modifications, change =>
+ change.SourceMember.Name == "ChangedTypeProperty" &&
+ change.TargetMember.Name == "ChangedTypeProperty" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.Contains(result.Modifications, change =>
+ change.SourceMember.Name == "VisibilityChangedMethod" &&
+ change.TargetMember.Name == "VisibilityChangedMethod" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+ }
- // Create test data - use different types to ensure they are treated as distinct objects
- var oldType = typeof(string);
- var newType = typeof(int); // Use a different type than oldType
+ [Fact]
+ public void Compare_WithNamespaceMapping_DetectsRenamedNamespace()
+ {
+ // Skip test if assemblies don't exist
+ if (!File.Exists(_testAssemblyV1Path) || !File.Exists(_testAssemblyV2Path))
+ {
+ _output.WriteLine("Test assemblies not found. Skipping test.");
+ return;
+ }
+
+ // Arrange
+ var config = new ComparisonConfiguration
+ {
+ NamespaceMappings = new Dictionary>
+ {
+ { "TestAssembly.NamespaceToBeRenamed", new List { "TestAssembly.RenamedNamespace" } }
+ }
+ };
+
+ // Act
+ var result = _apiComparer.Compare(_testAssemblyV1Path, _testAssemblyV2Path, config);
+
+ // Assert
+ Assert.NotNull(result);
+
+ // Verify that the class in the renamed namespace is not reported as removed
+ Assert.DoesNotContain(result.Removals, change =>
+ change.SourceMember.DeclaringType == "TestAssembly.NamespaceToBeRenamed.ClassInRenamedNamespace");
+
+ // Verify that the class in the renamed namespace is not reported as added
+ Assert.DoesNotContain(result.Additions, change =>
+ change.TargetMember.DeclaringType == "TestAssembly.RenamedNamespace.ClassInRenamedNamespace");
+
+ // Verify that the members of the class are correctly mapped
+ Assert.DoesNotContain(result.Removals, change =>
+ change.SourceMember.Name == "Method" &&
+ change.SourceMember.DeclaringType == "TestAssembly.NamespaceToBeRenamed.ClassInRenamedNamespace");
- var oldMember = new ApiMember
+ Assert.DoesNotContain(result.Removals, change =>
+ change.SourceMember.Name == "Property" &&
+ change.SourceMember.DeclaringType == "TestAssembly.NamespaceToBeRenamed.ClassInRenamedNamespace");
+ }
+
+ [Fact]
+ public void Compare_WithExclusions_ExcludesSpecifiedMembers()
{
- Name = "OldMethod",
- FullName = "System.String.OldMethod",
- Signature = "public void OldMethod()",
- Type = MemberType.Method
- };
-
- // Create manual mocks
- var mockApiExtractor = new Mock();
- mockApiExtractor.Setup(x => x.ExtractTypeMembers(oldType))
- .Returns(new List { oldMember });
- mockApiExtractor.Setup(x => x.ExtractTypeMembers(newType))
- .Returns(new List());
-
- var expectedDifference = new ApiDifference
+ // Skip test if assemblies don't exist
+ if (!File.Exists(_testAssemblyV1Path) || !File.Exists(_testAssemblyV2Path))
+ {
+ _output.WriteLine("Test assemblies not found. Skipping test.");
+ return;
+ }
+
+ // Arrange
+ var config = new ComparisonConfiguration
+ {
+ ExcludedMembers = new List
+ {
+ "TestAssembly.PublicClass.RemovedMethod",
+ "TestAssembly.PublicClass.RemovedProperty",
+ "TestAssembly.IPublicInterface.RemovedMethod"
+ }
+ };
+
+ // Act
+ var result = _apiComparer.Compare(_testAssemblyV1Path, _testAssemblyV2Path, config);
+
+ // Assert
+ Assert.NotNull(result);
+
+ // Verify that excluded members are not reported as removed
+ Assert.DoesNotContain(result.Removals, change =>
+ change.SourceMember.Name == "RemovedMethod" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.DoesNotContain(result.Removals, change =>
+ change.SourceMember.Name == "RemovedProperty" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.DoesNotContain(result.Removals, change =>
+ change.SourceMember.Name == "RemovedMethod" &&
+ change.SourceMember.DeclaringType == "TestAssembly.IPublicInterface");
+
+ // Verify that excluded members are reported in the Excluded collection
+ Assert.Contains(result.Excluded, change =>
+ change.SourceMember.Name == "RemovedMethod" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.Contains(result.Excluded, change =>
+ change.SourceMember.Name == "RemovedProperty" &&
+ change.SourceMember.DeclaringType == "TestAssembly.PublicClass");
+
+ Assert.Contains(result.Excluded, change =>
+ change.SourceMember.Name == "RemovedMethod" &&
+ change.SourceMember.DeclaringType == "TestAssembly.IPublicInterface");
+ }
+
+ [Fact]
+ public void Compare_WithBreakingChangeRules_ClassifiesBreakingChanges()
{
- ChangeType = ChangeType.Removed,
- ElementType = ApiElementType.Method,
- ElementName = "System.String.OldMethod",
- Description = "Removed method 'System.String.OldMethod'",
- IsBreakingChange = true
- };
-
- var mockDiffCalc = new Mock();
- mockDiffCalc.Setup(x => x.CalculateRemovedMember(It.Is(m => m.Signature == oldMember.Signature)))
- .Returns(expectedDifference);
-
- var logger = new NullLogger();
-
- // Create a mock NameMapper
- var mockNameMapper = new Mock();
-
- // Create a mock ChangeClassifier
- var mockChangeClassifier = new Mock();
- mockChangeClassifier.Setup(x => x.ClassifyChange(It.IsAny()))
- .Returns(diff => diff);
-
- // Create the comparer with our manual mocks
- var apiComparer = new ApiComparer(mockApiExtractor.Object, mockDiffCalc.Object, mockNameMapper.Object, mockChangeClassifier.Object, logger);
-
- // Act
- Console.WriteLine("About to call CompareMembers");
- var result = apiComparer.CompareMembers(oldType, newType).ToList();
- Console.WriteLine($"CompareMembers returned {result.Count} results");
-
- // Assert
- Assert.Single(result);
- Assert.Equal(ChangeType.Removed, result[0].ChangeType);
- Assert.Equal("System.String.OldMethod", result[0].ElementName);
+ // Skip test if assemblies don't exist
+ if (!File.Exists(_testAssemblyV1Path) || !File.Exists(_testAssemblyV2Path))
+ {
+ _output.WriteLine("Test assemblies not found. Skipping test.");
+ return;
+ }
+
+ // Arrange
+ var config = new ComparisonConfiguration
+ {
+ BreakingChangeRules = new BreakingChangeRules
+ {
+ TreatMemberRemovalAsBreaking = true,
+ TreatSignatureChangeAsBreaking = true,
+ TreatVisibilityDecreaseAsBreaking = true,
+ TreatPropertyTypeChangeAsBreaking = true
+ }
+ };
+
+ // Act
+ var result = _apiComparer.Compare(_testAssemblyV1Path, _testAssemblyV2Path, config);
+
+ // Assert
+ Assert.NotNull(result);
+
+ // Verify that removals are classified as breaking changes
+ foreach (var removal in result.Removals)
+ {
+ Assert.True(removal.IsBreakingChange);
+ }
+
+ // Verify that signature changes are classified as breaking changes
+ Assert.Contains(result.Modifications, change =>
+ change.SourceMember.Name == "ChangedSignatureMethod" &&
+ change.IsBreakingChange);
+
+ // Verify that property type changes are classified as breaking changes
+ Assert.Contains(result.Modifications, change =>
+ change.SourceMember.Name == "ChangedTypeProperty" &&
+ change.IsBreakingChange);
+
+ // Verify that visibility increases are not classified as breaking changes
+ Assert.Contains(result.Modifications, change =>
+ change.SourceMember.Name == "VisibilityChangedMethod" &&
+ !change.IsBreakingChange);
+ }
}
}
diff --git a/tests/DotNetApiDiff.Tests/DotNetApiDiff.Tests.csproj b/tests/DotNetApiDiff.Tests/DotNetApiDiff.Tests.csproj
index 84fa524..1b23a43 100644
--- a/tests/DotNetApiDiff.Tests/DotNetApiDiff.Tests.csproj
+++ b/tests/DotNetApiDiff.Tests/DotNetApiDiff.Tests.csproj
@@ -35,4 +35,10 @@
+
+
+
+
+
+
diff --git a/tests/DotNetApiDiff.Tests/Reporting/MarkdownFormatterTests.cs b/tests/DotNetApiDiff.Tests/Reporting/MarkdownFormatterTests.cs
index c54b928..d901203 100644
--- a/tests/DotNetApiDiff.Tests/Reporting/MarkdownFormatterTests.cs
+++ b/tests/DotNetApiDiff.Tests/Reporting/MarkdownFormatterTests.cs
@@ -250,21 +250,24 @@ public void Format_OutputIsValidMarkdown()
var markdown = formatter.Format(result);
// Assert
+ // Normalize line endings for cross-platform compatibility
+ var normalizedMarkdown = markdown.Replace("\r\n", "\n").Replace("\r", "\n");
+
// Check for valid markdown structure
// Headers should start with #
- Assert.Matches(new Regex(@"^# .*$", RegexOptions.Multiline), markdown);
- Assert.Matches(new Regex(@"^## .*$", RegexOptions.Multiline), markdown);
+ Assert.Matches(new Regex(@"^# .*$", RegexOptions.Multiline), normalizedMarkdown);
+ Assert.Matches(new Regex(@"^## .*$", RegexOptions.Multiline), normalizedMarkdown);
// Tables should have header row and separator row
- Assert.Matches(new Regex(@"^\| .* \|$", RegexOptions.Multiline), markdown);
- Assert.Matches(new Regex(@"^\|\-+\|\-+\|", RegexOptions.Multiline), markdown);
+ Assert.Matches(new Regex(@"^\| .* \|$", RegexOptions.Multiline), normalizedMarkdown);
+ Assert.Matches(new Regex(@"^\|\-+\|\-+\|", RegexOptions.Multiline), normalizedMarkdown);
// Code blocks should be properly formatted
- Assert.Matches(new Regex(@"```csharp\s.*\s```", RegexOptions.Singleline), markdown);
+ Assert.Matches(new Regex(@"```csharp\s.*\s```", RegexOptions.Singleline), normalizedMarkdown);
// Details tags should be properly closed
- var detailsOpenCount = Regex.Matches(markdown, @"").Count;
- var detailsCloseCount = Regex.Matches(markdown, @" ").Count;
+ var detailsOpenCount = Regex.Matches(normalizedMarkdown, @"").Count;
+ var detailsCloseCount = Regex.Matches(normalizedMarkdown, @" ").Count;
Assert.Equal(detailsOpenCount, detailsCloseCount);
}
}
diff --git a/tests/DotNetApiDiff.Tests/TestData/README.md b/tests/DotNetApiDiff.Tests/TestData/README.md
new file mode 100644
index 0000000..16877ff
--- /dev/null
+++ b/tests/DotNetApiDiff.Tests/TestData/README.md
@@ -0,0 +1,79 @@
+# Test Assemblies for Integration Testing
+
+This directory contains test assemblies used for integration testing of the DotNetApiDiff tool.
+
+## Test Assembly Structure
+
+### TestAssemblyV1.dll
+
+Contains the following API elements:
+
+- `TestAssembly.PublicClass`
+ - `UnchangedMethod()` - Remains unchanged in V2
+ - `RemovedMethod()` - Removed in V2
+ - `ChangedSignatureMethod(string)` - Signature changed in V2
+ - `UnchangedProperty` - Remains unchanged in V2
+ - `RemovedProperty` - Removed in V2
+ - `ChangedTypeProperty` (string) - Type changed in V2
+ - `UnchangedField` - Remains unchanged in V2
+ - `RemovedField` - Removed in V2
+ - `UnchangedEvent` - Remains unchanged in V2
+ - `RemovedEvent` - Removed in V2
+ - `VisibilityChangedMethod()` (protected) - Visibility changed to public in V2
+
+- `TestAssembly.IPublicInterface`
+ - `UnchangedMethod()` - Remains unchanged in V2
+ - `RemovedMethod()` - Removed in V2
+ - `UnchangedProperty` - Remains unchanged in V2
+ - `RemovedProperty` - Removed in V2
+ - `UnchangedEvent` - Remains unchanged in V2
+ - `RemovedEvent` - Removed in V2
+
+- `TestAssembly.NamespaceToBeRenamed.ClassInRenamedNamespace`
+ - `Method()` - Remains unchanged in V2 (but in renamed namespace)
+ - `Property` - Remains unchanged in V2 (but in renamed namespace)
+
+### TestAssemblyV2.dll
+
+Contains the following API elements:
+
+- `TestAssembly.PublicClass`
+ - `UnchangedMethod()` - Unchanged from V1
+ - `ChangedSignatureMethod(int)` - Signature changed from V1
+ - `NewMethod()` - Added in V2
+ - `UnchangedProperty` - Unchanged from V1
+ - `ChangedTypeProperty` (int) - Type changed from V1
+ - `NewProperty` - Added in V2
+ - `UnchangedField` - Unchanged from V1
+ - `NewField` - Added in V2
+ - `UnchangedEvent` - Unchanged from V1
+ - `NewEvent` - Added in V2
+ - `VisibilityChangedMethod()` (public) - Visibility changed from protected in V1
+
+- `TestAssembly.IPublicInterface`
+ - `UnchangedMethod()` - Unchanged from V1
+ - `NewMethod()` - Added in V2
+ - `UnchangedProperty` - Unchanged from V1
+ - `NewProperty` - Added in V2
+ - `UnchangedEvent` - Unchanged from V1
+ - `NewEvent` - Added in V2
+
+- `TestAssembly.RenamedNamespace.ClassInRenamedNamespace`
+ - `Method()` - Unchanged from V1 (but in renamed namespace)
+ - `Property` - Unchanged from V1 (but in renamed namespace)
+
+## Building the Test Assemblies
+
+The test assemblies need to be built separately and placed in this directory for the integration tests to work.
+
+```bash
+# Build TestAssemblyV1
+dotnet build TestAssemblyV1.csproj -c Release
+
+# Build TestAssemblyV2
+dotnet build TestAssemblyV2.csproj -c Release
+
+# Copy the assemblies to the TestData directory
+cp bin/Release/net8.0/TestAssemblyV1.dll ../DotNetApiDiff.Tests/TestData/
+cp bin/Release/net8.0/TestAssemblyV2.dll ../DotNetApiDiff.Tests/TestData/
+```
diff --git a/tests/DotNetApiDiff.Tests/TestData/TestAssemblyV1.dll b/tests/DotNetApiDiff.Tests/TestData/TestAssemblyV1.dll
new file mode 100644
index 0000000..8e8ef16
Binary files /dev/null and b/tests/DotNetApiDiff.Tests/TestData/TestAssemblyV1.dll differ
diff --git a/tests/DotNetApiDiff.Tests/TestData/TestAssemblyV2.dll b/tests/DotNetApiDiff.Tests/TestData/TestAssemblyV2.dll
new file mode 100644
index 0000000..88b473a
Binary files /dev/null and b/tests/DotNetApiDiff.Tests/TestData/TestAssemblyV2.dll differ
diff --git a/tests/DotNetApiDiff.Tests/TestData/sample-config.json b/tests/DotNetApiDiff.Tests/TestData/sample-config.json
index 5129716..4ddf2fa 100644
--- a/tests/DotNetApiDiff.Tests/TestData/sample-config.json
+++ b/tests/DotNetApiDiff.Tests/TestData/sample-config.json
@@ -1,24 +1,29 @@
{
+ "filters": {
+ "includeNamespaces": ["System.Text", "System.IO"],
+ "excludeNamespaces": ["System.Diagnostics", "System.Internal"],
+ "includeTypes": ["System.Text.*", "System.IO.*"],
+ "excludeTypes": ["*.Internal*", "*.Helper*"],
+ "includeInternals": false,
+ "includeCompilerGenerated": false
+ },
"mappings": {
"namespaceMappings": {
"OldNamespace": ["NewNamespace"],
- "Legacy.Api": ["Modern.Api", "Modern.Api.V2"]
+ "AnotherOldNamespace": ["AnotherNewNamespace"]
},
"typeMappings": {
- "OldNamespace.OldType": "NewNamespace.NewType",
- "Legacy.Api.LegacyClass": "Modern.Api.ModernClass"
+ "OldType": "NewType",
+ "AnotherOldType": "AnotherNewType"
},
"autoMapSameNameTypes": true,
"ignoreCase": true
},
"exclusions": {
- "excludedTypes": ["System.Diagnostics.Debug", "System.Diagnostics.Trace"],
- "excludedTypePatterns": ["*.Internal.*", "*.Private.*"],
- "excludedMembers": [
- "System.Object.Finalize",
- "System.Object.MemberwiseClone"
- ],
- "excludedMemberPatterns": ["*.Obsolete*", "*.Debug*"]
+ "excludedTypes": ["ExcludedType", "AnotherExcludedType"],
+ "excludedMembers": ["SomeClass.RemovedMethod", "SomeClass.RemovedProperty"],
+ "excludedTypePatterns": ["*.Internal*", "*.Helper*"],
+ "excludedMemberPatterns": ["*.get_*", "*.set_*"]
},
"breakingChangeRules": {
"treatTypeRemovalAsBreaking": true,
@@ -26,19 +31,5 @@
"treatAddedTypeAsBreaking": false,
"treatAddedMemberAsBreaking": false,
"treatSignatureChangeAsBreaking": true
- },
- "filters": {
- "includeNamespaces": ["System.Text", "System.IO"],
- "excludeNamespaces": [
- "System.Diagnostics",
- "System.Runtime.CompilerServices"
- ],
- "includeTypes": ["System.Text.*", "System.IO.File*"],
- "excludeTypes": ["*.Internal*", "*.Debug*"],
- "includeInternals": false,
- "includeCompilerGenerated": false
- },
- "outputFormat": "Console",
- "outputPath": null,
- "failOnBreakingChanges": true
+ }
}
diff --git a/tests/TestAssemblies/TestAssemblyV1.csproj b/tests/TestAssemblies/TestAssemblyV1.csproj
new file mode 100644
index 0000000..4c61f0d
--- /dev/null
+++ b/tests/TestAssemblies/TestAssemblyV1.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net8.0
+ enable
+ enable
+ TestAssemblyV1
+ TestAssembly
+ $(MSBuildThisFileDirectory)bin\$(Configuration)\
+ false
+
+ CS0067
+
+
+
+
+
+
+
diff --git a/tests/TestAssemblies/TestAssemblyV2.csproj b/tests/TestAssemblies/TestAssemblyV2.csproj
new file mode 100644
index 0000000..3edc9b1
--- /dev/null
+++ b/tests/TestAssemblies/TestAssemblyV2.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net8.0
+ enable
+ enable
+ TestAssemblyV2
+ TestAssembly
+ $(MSBuildThisFileDirectory)bin\$(Configuration)\
+ false
+
+ CS0067
+
+
+
+
+
+
+
diff --git a/tests/TestAssemblies/V1/NamespaceToBeRenamed.cs b/tests/TestAssemblies/V1/NamespaceToBeRenamed.cs
new file mode 100644
index 0000000..1b9e98a
--- /dev/null
+++ b/tests/TestAssemblies/V1/NamespaceToBeRenamed.cs
@@ -0,0 +1,20 @@
+namespace TestAssembly.NamespaceToBeRenamed
+{
+ ///
+ /// A class in a namespace that will be renamed in V2
+ ///
+ public class ClassInRenamedNamespace
+ {
+ ///
+ /// A method that will remain unchanged
+ ///
+ public void Method()
+ {
+ }
+
+ ///
+ /// A property that will remain unchanged
+ ///
+ public string Property { get; set; } = string.Empty;
+ }
+}
diff --git a/tests/TestAssemblies/V1/PublicClass.cs b/tests/TestAssemblies/V1/PublicClass.cs
new file mode 100644
index 0000000..944f662
--- /dev/null
+++ b/tests/TestAssemblies/V1/PublicClass.cs
@@ -0,0 +1,72 @@
+namespace TestAssembly
+{
+ ///
+ /// A public class that will remain unchanged between versions
+ ///
+ public class PublicClass
+ {
+ ///
+ /// A public method that will remain unchanged
+ ///
+ public void UnchangedMethod()
+ {
+ }
+
+ ///
+ /// A public method that will be removed in V2
+ ///
+ public void RemovedMethod()
+ {
+ }
+
+ ///
+ /// A public method that will have its signature changed in V2
+ ///
+ /// A string parameter
+ public void ChangedSignatureMethod(string value)
+ {
+ }
+
+ ///
+ /// A public property that will remain unchanged
+ ///
+ public string UnchangedProperty { get; set; } = string.Empty;
+
+ ///
+ /// A public property that will be removed in V2
+ ///
+ public int RemovedProperty { get; set; }
+
+ ///
+ /// A public property that will have its type changed in V2
+ ///
+ public string ChangedTypeProperty { get; set; } = string.Empty;
+
+ ///
+ /// A public field that will remain unchanged
+ ///
+ public readonly int UnchangedField = 42;
+
+ ///
+ /// A public field that will be removed in V2
+ ///
+ public readonly bool RemovedField = true;
+
+ ///
+ /// A public event that will remain unchanged
+ ///
+ public event EventHandler? UnchangedEvent;
+
+ ///
+ /// A public event that will be removed in V2
+ ///
+ public event EventHandler? RemovedEvent;
+
+ ///
+ /// A protected method that will have its visibility changed to public in V2
+ ///
+ protected void VisibilityChangedMethod()
+ {
+ }
+ }
+}
diff --git a/tests/TestAssemblies/V1/PublicInterface.cs b/tests/TestAssemblies/V1/PublicInterface.cs
new file mode 100644
index 0000000..7f2fe75
--- /dev/null
+++ b/tests/TestAssemblies/V1/PublicInterface.cs
@@ -0,0 +1,38 @@
+namespace TestAssembly
+{
+ ///
+ /// A public interface that will have members added and removed in V2
+ ///
+ public interface IPublicInterface
+ {
+ ///
+ /// A method that will remain unchanged
+ ///
+ void UnchangedMethod();
+
+ ///
+ /// A method that will be removed in V2
+ ///
+ void RemovedMethod();
+
+ ///
+ /// A property that will remain unchanged
+ ///
+ string UnchangedProperty { get; set; }
+
+ ///
+ /// A property that will be removed in V2
+ ///
+ int RemovedProperty { get; }
+
+ ///
+ /// An event that will remain unchanged
+ ///
+ event EventHandler UnchangedEvent;
+
+ ///
+ /// An event that will be removed in V2
+ ///
+ event EventHandler RemovedEvent;
+ }
+}
diff --git a/tests/TestAssemblies/V2/PublicClass.cs b/tests/TestAssemblies/V2/PublicClass.cs
new file mode 100644
index 0000000..4231153
--- /dev/null
+++ b/tests/TestAssemblies/V2/PublicClass.cs
@@ -0,0 +1,80 @@
+namespace TestAssembly
+{
+ ///
+ /// A public class that remains unchanged between versions
+ ///
+ public class PublicClass
+ {
+ ///
+ /// A public method that remains unchanged
+ ///
+ public void UnchangedMethod()
+ {
+ }
+
+ // RemovedMethod is intentionally removed
+
+ ///
+ /// A public method with a changed signature (parameter type changed)
+ ///
+ /// An integer parameter (was string in V1)
+ public void ChangedSignatureMethod(int value)
+ {
+ }
+
+ ///
+ /// A new method added in V2
+ ///
+ public void NewMethod()
+ {
+ }
+
+ ///
+ /// A public property that remains unchanged
+ ///
+ public string UnchangedProperty { get; set; } = string.Empty;
+
+ // RemovedProperty is intentionally removed
+
+ ///
+ /// A public property with changed type (was string in V1)
+ ///
+ public int ChangedTypeProperty { get; set; }
+
+ ///
+ /// A new property added in V2
+ ///
+ public DateTime NewProperty { get; set; }
+
+ ///
+ /// A public field that remains unchanged
+ ///
+ public readonly int UnchangedField = 42;
+
+ // RemovedField is intentionally removed
+
+ ///
+ /// A new field added in V2
+ ///
+ public readonly double NewField = 3.14;
+
+ ///
+ /// A public event that remains unchanged
+ ///
+ public event EventHandler? UnchangedEvent;
+
+ // RemovedEvent is intentionally removed
+
+ ///
+ /// A new event added in V2
+ ///
+ public event EventHandler? NewEvent;
+
+ ///
+ /// A method that had its visibility changed from protected to public
+ ///
+ public void VisibilityChangedMethod()
+ {
+ }
+ }
+}
diff --git a/tests/TestAssemblies/V2/PublicInterface.cs b/tests/TestAssemblies/V2/PublicInterface.cs
new file mode 100644
index 0000000..e740270
--- /dev/null
+++ b/tests/TestAssemblies/V2/PublicInterface.cs
@@ -0,0 +1,44 @@
+namespace TestAssembly
+{
+ ///
+ /// A public interface with members added and removed
+ ///
+ public interface IPublicInterface
+ {
+ ///
+ /// A method that remains unchanged
+ ///
+ void UnchangedMethod();
+
+ // RemovedMethod is intentionally removed
+
+ ///
+ /// A new method added in V2
+ ///
+ void NewMethod();
+
+ ///
+ /// A property that remains unchanged
+ ///
+ string UnchangedProperty { get; set; }
+
+ // RemovedProperty is intentionally removed
+
+ ///
+ /// A new property added in V2
+ ///
+ DateTime NewProperty { get; }
+
+ ///
+ /// An event that remains unchanged
+ ///
+ event EventHandler UnchangedEvent;
+
+ // RemovedEvent is intentionally removed
+
+ ///
+ /// A new event added in V2
+ ///
+ event EventHandler NewEvent;
+ }
+}
diff --git a/tests/TestAssemblies/V2/RenamedNamespace.cs b/tests/TestAssemblies/V2/RenamedNamespace.cs
new file mode 100644
index 0000000..215a054
--- /dev/null
+++ b/tests/TestAssemblies/V2/RenamedNamespace.cs
@@ -0,0 +1,20 @@
+namespace TestAssembly.RenamedNamespace
+{
+ ///
+ /// A class in a namespace that was renamed from NamespaceToBeRenamed in V1
+ ///
+ public class ClassInRenamedNamespace
+ {
+ ///
+ /// A method that remains unchanged
+ ///
+ public void Method()
+ {
+ }
+
+ ///
+ /// A property that remains unchanged
+ ///
+ public string Property { get; set; } = string.Empty;
+ }
+}
diff --git a/tests/TestAssemblies/build-test-assemblies.sh b/tests/TestAssemblies/build-test-assemblies.sh
new file mode 100755
index 0000000..87bbe1d
--- /dev/null
+++ b/tests/TestAssemblies/build-test-assemblies.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# Build the test assemblies
+echo "Building TestAssemblyV1..."
+dotnet build TestAssemblyV1.csproj -c Release
+
+echo "Building TestAssemblyV2..."
+dotnet build TestAssemblyV2.csproj -c Release
+
+# Copy the assemblies to the TestData directory for integration tests
+echo "Copying assemblies to TestData directory..."
+cp bin/Release/net8.0/TestAssemblyV1.dll ../DotNetApiDiff.Tests/TestData/
+cp bin/Release/net8.0/TestAssemblyV2.dll ../DotNetApiDiff.Tests/TestData/
+
+echo "Done building and copying test assemblies."