dependabot-nuget 0.251.0 → 0.253.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Common.props +1 -0
  3. data/helpers/lib/NuGetUpdater/Directory.Packages.props +26 -0
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +35 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/NuGetUpdater.Cli.csproj +1 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +4 -7
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +251 -0
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +27 -9
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/NuGetUpdater.Cli.Test.csproj +3 -3
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Dependency.cs +56 -1
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyType.cs +1 -1
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DirectoryPackagesPropsDiscovery.cs +69 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DirectoryPackagesPropsDiscoveryResult.cs +11 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +217 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DotNetToolsJsonDiscovery.cs +30 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DotNetToolsJsonDiscoveryResult.cs +10 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/GlobalJsonDiscovery.cs +30 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/GlobalJsonDiscoveryResult.cs +10 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/IDiscoveryResult.cs +14 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscovery.cs +29 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscoveryResult.cs +10 -0
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +13 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +128 -0
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +13 -0
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/EvaluationResult.cs +8 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/EvaluationResultType.cs +9 -0
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/BuildFile.cs +6 -8
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/DotNetToolsJsonBuildFile.cs +4 -7
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/GlobalJsonBuildFile.cs +24 -17
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/JsonBuildFile.cs +2 -2
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +8 -13
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +100 -19
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/XmlBuildFile.cs +2 -2
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +6 -6
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Property.cs +6 -0
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs +23 -36
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs +5 -10
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +59 -26
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +5 -20
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/HashSetExtensions.cs +14 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ImmutableArrayExtensions.cs +18 -0
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +0 -1
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +281 -140
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +27 -4
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +18 -13
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +135 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +91 -0
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +71 -0
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +67 -0
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +405 -0
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +306 -0
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +36 -0
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/DotNetToolsJsonBuildFileTests.cs +1 -2
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/GlobalJsonBuildFileTests.cs +2 -3
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/PackagesConfigBuildFileTests.cs +4 -6
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/ProjectBuildFileTests.cs +6 -5
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/NuGetUpdater.Core.Test.csproj +4 -3
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +38 -6
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestBase.cs +10 -0
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +1 -8
  61. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +13 -41
  62. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +0 -5
  63. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +0 -5
  64. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +0 -5
  65. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +0 -5
  66. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +0 -5
  67. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +30 -23
  68. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/AssertEx.cs +272 -0
  69. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/DiffUtil.cs +266 -0
  70. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +239 -161
  71. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterHelperTests.cs +7 -11
  72. data/lib/dependabot/nuget/discovery/dependency_details.rb +95 -0
  73. data/lib/dependabot/nuget/discovery/dependency_file_discovery.rb +126 -0
  74. data/lib/dependabot/nuget/discovery/directory_packages_props_discovery.rb +43 -0
  75. data/lib/dependabot/nuget/discovery/discovery_json_reader.rb +83 -0
  76. data/lib/dependabot/nuget/discovery/evaluation_details.rb +63 -0
  77. data/lib/dependabot/nuget/discovery/project_discovery.rb +71 -0
  78. data/lib/dependabot/nuget/discovery/property_details.rb +43 -0
  79. data/lib/dependabot/nuget/discovery/workspace_discovery.rb +66 -0
  80. data/lib/dependabot/nuget/file_parser.rb +19 -128
  81. data/lib/dependabot/nuget/file_updater.rb +28 -60
  82. data/lib/dependabot/nuget/native_helpers.rb +55 -0
  83. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +3 -8
  84. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +1 -0
  85. data/lib/dependabot/nuget/update_checker/property_updater.rb +1 -0
  86. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +17 -152
  87. data/lib/dependabot/nuget/update_checker/version_finder.rb +1 -6
  88. data/lib/dependabot/nuget/update_checker.rb +4 -1
  89. metadata +44 -25
  90. data/lib/dependabot/nuget/file_parser/dotnet_tools_json_parser.rb +0 -71
  91. data/lib/dependabot/nuget/file_parser/global_json_parser.rb +0 -68
  92. data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +0 -92
  93. data/lib/dependabot/nuget/file_parser/project_file_parser.rb +0 -620
  94. data/lib/dependabot/nuget/file_parser/property_value_finder.rb +0 -225
  95. data/lib/dependabot/nuget/file_updater/property_value_updater.rb +0 -81
@@ -8,11 +8,6 @@ public partial class UpdateWorkerTests
8
8
  {
9
9
  public class DotNetTools : UpdateWorkerTestBase
10
10
  {
11
- public DotNetTools()
12
- {
13
- MSBuildHelper.RegisterMSBuild();
14
- }
15
-
16
11
  [Fact]
17
12
  public async Task NoChangeWhenDotNetToolsJsonNotFound()
18
13
  {
@@ -8,11 +8,6 @@ public partial class UpdateWorkerTests
8
8
  {
9
9
  public class GlobalJson : UpdateWorkerTestBase
10
10
  {
11
- public GlobalJson()
12
- {
13
- MSBuildHelper.RegisterMSBuild();
14
- }
15
-
16
11
  [Fact]
17
12
  public async Task NoChangeWhenGlobalJsonNotFound()
18
13
  {
@@ -8,11 +8,6 @@ public partial class UpdateWorkerTests
8
8
  {
9
9
  public class Mixed : UpdateWorkerTestBase
10
10
  {
11
- public Mixed()
12
- {
13
- MSBuildHelper.RegisterMSBuild();
14
- }
15
-
16
11
  [Fact]
17
12
  public async Task ForPackagesProject_UpdatePackageReference_InBuildProps()
18
13
  {
@@ -9,11 +9,6 @@ public partial class UpdateWorkerTests
9
9
  {
10
10
  public class PackagesConfig : UpdateWorkerTestBase
11
11
  {
12
- public PackagesConfig()
13
- {
14
- MSBuildHelper.RegisterMSBuild();
15
- }
16
-
17
12
  [Fact]
18
13
  public async Task UpdateSingleDependencyInPackagesConfig()
19
14
  {
@@ -8,11 +8,6 @@ public partial class UpdateWorkerTests
8
8
  {
9
9
  public class Sdk : UpdateWorkerTestBase
10
10
  {
11
- public Sdk()
12
- {
13
- MSBuildHelper.RegisterMSBuild();
14
- }
15
-
16
11
  [Theory]
17
12
  [InlineData("net472")]
18
13
  [InlineData("netstandard2.0")]
@@ -175,24 +170,6 @@ public partial class UpdateWorkerTests
175
170
  ]);
176
171
  }
177
172
 
178
- [Fact]
179
- public async Task NoChange_WhenPackageHasVersionConstraint()
180
- {
181
- // Dependency package has version constraint
182
- await TestNoChangeforProject("AWSSDK.Core", "3.3.21.19", "3.7.300.20",
183
- projectContents: $"""
184
- <Project Sdk="Microsoft.NET.Sdk">
185
- <PropertyGroup>
186
- <TargetFramework>netstandard2.0</TargetFramework>
187
- </PropertyGroup>
188
- <ItemGroup>
189
- <PackageReference Include="AWSSDK.S3" Version="3.3.17.3" />
190
- <PackageReference Include="AWSSDK.Core" Version="3.3.21.19" />
191
- </ItemGroup>
192
- </Project>
193
- """);
194
- }
195
-
196
173
  [Fact]
197
174
  public async Task UpdateVersionAttribute_InProjectFile_ForPackageReferenceInclude_Windows()
198
175
  {
@@ -2558,5 +2535,35 @@ public partial class UpdateWorkerTests
2558
2535
  """
2559
2536
  );
2560
2537
  }
2538
+
2539
+ [Fact]
2540
+ public async Task UpdatingPackageAlsoUpdatesAnythingWithADependencyOnTheUpdatedPackage()
2541
+ {
2542
+ // updating SpecFlow from 3.3.30 requires that SpecFlow.Tools.MsBuild.Generation also be updated
2543
+ await TestUpdateForProject("SpecFlow", "3.3.30", "3.4.3",
2544
+ projectContents: """
2545
+ <Project Sdk="Microsoft.NET.Sdk">
2546
+ <PropertyGroup>
2547
+ <TargetFramework>netstandard2.0</TargetFramework>
2548
+ </PropertyGroup>
2549
+ <ItemGroup>
2550
+ <PackageReference Include="SpecFlow" Version="3.3.30" />
2551
+ <PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.3.30" />
2552
+ </ItemGroup>
2553
+ </Project>
2554
+ """,
2555
+ expectedProjectContents: """
2556
+ <Project Sdk="Microsoft.NET.Sdk">
2557
+ <PropertyGroup>
2558
+ <TargetFramework>netstandard2.0</TargetFramework>
2559
+ </PropertyGroup>
2560
+ <ItemGroup>
2561
+ <PackageReference Include="SpecFlow" Version="3.4.3" />
2562
+ <PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.4.3" />
2563
+ </ItemGroup>
2564
+ </Project>
2565
+ """
2566
+ );
2567
+ }
2561
2568
  }
2562
2569
  }
@@ -0,0 +1,272 @@
1
+ using System.Collections;
2
+ using System.Collections.Immutable;
3
+ using System.Reflection;
4
+ using System.Text;
5
+
6
+ using Xunit;
7
+
8
+ namespace NuGetUpdater.Core.Test.Utilities;
9
+
10
+ /// <summary>
11
+ /// Assert style type to deal with the lack of features in xUnit's Assert type
12
+ /// </summary>
13
+ public static class AssertEx
14
+ {
15
+ public static void Equal<T>(
16
+ ImmutableArray<T>? expected,
17
+ ImmutableArray<T>? actual,
18
+ IEqualityComparer<T>? comparer = null,
19
+ string? message = null)
20
+ {
21
+ if (actual is null || actual.Value.IsDefault)
22
+ {
23
+ Assert.True(expected is null || expected.Value.IsDefault, message);
24
+ }
25
+ else
26
+ {
27
+ Equal(expected, (IEnumerable<T>)actual.Value, comparer, message);
28
+ }
29
+ }
30
+
31
+ public static void Equal<T>(
32
+ ImmutableArray<T> expected,
33
+ ImmutableArray<T> actual,
34
+ IEqualityComparer<T>? comparer = null,
35
+ string? message = null)
36
+ {
37
+ if (actual.IsDefault)
38
+ {
39
+ Assert.True(expected.IsDefault, message);
40
+ }
41
+ else
42
+ {
43
+ Equal(expected, (IEnumerable<T>)actual, comparer, message);
44
+ }
45
+ }
46
+
47
+ public static void Equal<T>(
48
+ ImmutableArray<T>? expected,
49
+ IEnumerable<T>? actual,
50
+ IEqualityComparer<T>? comparer = null,
51
+ string? message = null)
52
+ {
53
+ if (expected is null || expected.Value.IsDefault)
54
+ {
55
+ Assert.True(actual is null, message);
56
+ }
57
+ else
58
+ {
59
+ Equal((IEnumerable<T>?)expected, actual, comparer, message);
60
+ }
61
+ }
62
+
63
+ public static void Equal<T>(
64
+ IEnumerable<T>? expected,
65
+ ImmutableArray<T>? actual,
66
+ IEqualityComparer<T>? comparer = null,
67
+ string? message = null)
68
+ {
69
+ if (actual is null || actual.Value.IsDefault)
70
+ {
71
+ Assert.True(expected is null, message);
72
+ }
73
+ else
74
+ {
75
+ Equal(expected, (IEnumerable<T>)actual, comparer, message);
76
+ }
77
+ }
78
+
79
+ public static void Equal<T>(
80
+ IEnumerable<T>? expected,
81
+ IEnumerable<T>? actual,
82
+ IEqualityComparer<T>? comparer = null,
83
+ string? message = null)
84
+ {
85
+ if (expected == null)
86
+ {
87
+ Assert.True(actual is null, message);
88
+ return;
89
+ }
90
+ else
91
+ {
92
+ Assert.True(actual is not null, message);
93
+ }
94
+
95
+ if (SequenceEqual(expected, actual, comparer))
96
+ {
97
+ return;
98
+ }
99
+
100
+ Assert.True(false, GetAssertMessage(expected, actual, comparer, message));
101
+ }
102
+
103
+ private static bool SequenceEqual<T>(
104
+ IEnumerable<T> expected,
105
+ IEnumerable<T> actual,
106
+ IEqualityComparer<T>? comparer = null)
107
+ {
108
+ if (ReferenceEquals(expected, actual))
109
+ {
110
+ return true;
111
+ }
112
+
113
+ var enumerator1 = expected.GetEnumerator();
114
+ var enumerator2 = actual.GetEnumerator();
115
+
116
+ while (true)
117
+ {
118
+ var hasNext1 = enumerator1.MoveNext();
119
+ var hasNext2 = enumerator2.MoveNext();
120
+
121
+ if (hasNext1 != hasNext2)
122
+ {
123
+ return false;
124
+ }
125
+
126
+ if (!hasNext1)
127
+ {
128
+ break;
129
+ }
130
+
131
+ var value1 = enumerator1.Current;
132
+ var value2 = enumerator2.Current;
133
+
134
+ var areEqual = comparer != null
135
+ ? comparer.Equals(value1, value2)
136
+ : AssertEqualityComparer<T>.Equals(value1, value2);
137
+ if (!areEqual)
138
+ {
139
+ return false;
140
+ }
141
+ }
142
+
143
+ return true;
144
+ }
145
+
146
+ public static string GetAssertMessage<T>(
147
+ IEnumerable<T> expected,
148
+ IEnumerable<T> actual,
149
+ IEqualityComparer<T>? comparer = null,
150
+ string? prefix = null)
151
+ {
152
+ Func<T, string> itemInspector = typeof(T) == typeof(byte)
153
+ ? b => $"0x{b:X2}"
154
+ : new Func<T, string>(obj => obj?.ToString() ?? "<null>");
155
+
156
+ var itemSeparator = typeof(T) == typeof(byte)
157
+ ? ", "
158
+ : "," + Environment.NewLine;
159
+
160
+ var expectedString = string.Join(itemSeparator, expected.Take(10).Select(itemInspector));
161
+ var actualString = string.Join(itemSeparator, actual.Select(itemInspector));
162
+
163
+ var message = new StringBuilder();
164
+
165
+ if (!string.IsNullOrEmpty(prefix))
166
+ {
167
+ message.AppendLine(prefix);
168
+ message.AppendLine();
169
+ }
170
+
171
+ message.AppendLine("Expected:");
172
+ message.AppendLine(expectedString);
173
+ if (expected.Count() > 10)
174
+ {
175
+ message.AppendLine("... truncated ...");
176
+ }
177
+
178
+ message.AppendLine("Actual:");
179
+ message.AppendLine(actualString);
180
+ message.AppendLine("Differences:");
181
+ message.AppendLine(DiffUtil.DiffReport(expected, actual, itemSeparator, comparer, itemInspector));
182
+
183
+ return message.ToString();
184
+ }
185
+
186
+ private class AssertEqualityComparer<T> : IEqualityComparer<T>
187
+ {
188
+ public static readonly IEqualityComparer<T> Instance = new AssertEqualityComparer<T>();
189
+
190
+ private static bool CanBeNull()
191
+ {
192
+ var type = typeof(T);
193
+ return !type.GetTypeInfo().IsValueType ||
194
+ (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
195
+ }
196
+
197
+ public static bool IsNull(T @object)
198
+ {
199
+ if (!CanBeNull())
200
+ {
201
+ return false;
202
+ }
203
+
204
+ return object.Equals(@object, default(T));
205
+ }
206
+
207
+ public static bool Equals(T left, T right)
208
+ {
209
+ return Instance.Equals(left, right);
210
+ }
211
+
212
+ bool IEqualityComparer<T>.Equals(T? x, T? y)
213
+ {
214
+ if (CanBeNull())
215
+ {
216
+ if (object.Equals(x, default(T)))
217
+ {
218
+ return object.Equals(y, default(T));
219
+ }
220
+
221
+ if (object.Equals(y, default(T)))
222
+ {
223
+ return false;
224
+ }
225
+ }
226
+
227
+ if (x is IEquatable<T> equatable)
228
+ {
229
+ return equatable.Equals(y);
230
+ }
231
+
232
+ if (x is IComparable<T> comparableT)
233
+ {
234
+ return comparableT.CompareTo(y) == 0;
235
+ }
236
+
237
+ if (x is IComparable comparable)
238
+ {
239
+ return comparable.CompareTo(y) == 0;
240
+ }
241
+
242
+ if (x is IEnumerable enumerableX && y is IEnumerable enumerableY)
243
+ {
244
+ var enumeratorX = enumerableX.GetEnumerator();
245
+ var enumeratorY = enumerableY.GetEnumerator();
246
+
247
+ while (true)
248
+ {
249
+ bool hasNextX = enumeratorX.MoveNext();
250
+ bool hasNextY = enumeratorY.MoveNext();
251
+
252
+ if (!hasNextX || !hasNextY)
253
+ {
254
+ return hasNextX == hasNextY;
255
+ }
256
+
257
+ if (!Equals(enumeratorX.Current, enumeratorY.Current))
258
+ {
259
+ return false;
260
+ }
261
+ }
262
+ }
263
+
264
+ return object.Equals(x, y);
265
+ }
266
+
267
+ int IEqualityComparer<T>.GetHashCode(T obj)
268
+ {
269
+ throw new NotImplementedException();
270
+ }
271
+ }
272
+ }
@@ -0,0 +1,266 @@
1
+ #nullable disable
2
+
3
+ using System.Diagnostics;
4
+
5
+ namespace NuGetUpdater.Core.Test.Utilities;
6
+
7
+ public class DiffUtil
8
+ {
9
+ private enum EditKind
10
+ {
11
+ /// <summary>
12
+ /// No change.
13
+ /// </summary>
14
+ None = 0,
15
+
16
+ /// <summary>
17
+ /// Node value was updated.
18
+ /// </summary>
19
+ Update = 1,
20
+
21
+ /// <summary>
22
+ /// Node was inserted.
23
+ /// </summary>
24
+ Insert = 2,
25
+
26
+ /// <summary>
27
+ /// Node was deleted.
28
+ /// </summary>
29
+ Delete = 3,
30
+ }
31
+
32
+ private class LCS<T> : LongestCommonSubsequence<IList<T>>
33
+ {
34
+ public static readonly LCS<T> Default = new LCS<T>(EqualityComparer<T>.Default);
35
+
36
+ private readonly IEqualityComparer<T> _comparer;
37
+
38
+ public LCS(IEqualityComparer<T> comparer)
39
+ {
40
+ _comparer = comparer;
41
+ }
42
+
43
+ protected override bool ItemsEqual(IList<T> sequenceA, int indexA, IList<T> sequenceB, int indexB)
44
+ {
45
+ return _comparer.Equals(sequenceA[indexA], sequenceB[indexB]);
46
+ }
47
+
48
+ public IEnumerable<string> CalculateDiff(IList<T> sequenceA, IList<T> sequenceB, Func<T, string> toString)
49
+ {
50
+ foreach (var edit in GetEdits(sequenceA, sequenceA.Count, sequenceB, sequenceB.Count).Reverse())
51
+ {
52
+ switch (edit.Kind)
53
+ {
54
+ case EditKind.Delete:
55
+ yield return "--> " + toString(sequenceA[edit.IndexA]);
56
+ break;
57
+
58
+ case EditKind.Insert:
59
+ yield return "++> " + toString(sequenceB[edit.IndexB]);
60
+ break;
61
+
62
+ case EditKind.Update:
63
+ yield return " " + toString(sequenceB[edit.IndexB]);
64
+ break;
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ public static string DiffReport<T>(IEnumerable<T> expected, IEnumerable<T> actual, string separator, IEqualityComparer<T> comparer = null, Func<T, string> toString = null)
71
+ {
72
+ var lcs = (comparer != null) ? new LCS<T>(comparer) : LCS<T>.Default;
73
+ toString = toString ?? new Func<T, string>(obj => obj.ToString());
74
+
75
+ IList<T> expectedList = expected as IList<T> ?? new List<T>(expected);
76
+ IList<T> actualList = actual as IList<T> ?? new List<T>(actual);
77
+
78
+ return string.Join(separator, lcs.CalculateDiff(expectedList, actualList, toString));
79
+ }
80
+
81
+ private static readonly char[] s_lineSplitChars = new[] { '\r', '\n' };
82
+
83
+ public static string[] Lines(string s)
84
+ {
85
+ return s.Split(s_lineSplitChars, StringSplitOptions.RemoveEmptyEntries);
86
+ }
87
+
88
+ public static string DiffReport(string expected, string actual)
89
+ {
90
+ var exlines = Lines(expected);
91
+ var aclines = Lines(actual);
92
+ return DiffReport(exlines, aclines, separator: Environment.NewLine);
93
+ }
94
+
95
+ /// <summary>
96
+ /// Calculates Longest Common Subsequence.
97
+ /// </summary>
98
+ private abstract class LongestCommonSubsequence<TSequence>
99
+ {
100
+ protected readonly struct Edit
101
+ {
102
+ public readonly EditKind Kind;
103
+ public readonly int IndexA;
104
+ public readonly int IndexB;
105
+
106
+ internal Edit(EditKind kind, int indexA, int indexB)
107
+ {
108
+ this.Kind = kind;
109
+ this.IndexA = indexA;
110
+ this.IndexB = indexB;
111
+ }
112
+ }
113
+
114
+ private const int DeleteCost = 1;
115
+ private const int InsertCost = 1;
116
+ private const int UpdateCost = 2;
117
+
118
+ protected abstract bool ItemsEqual(TSequence sequenceA, int indexA, TSequence sequenceB, int indexB);
119
+
120
+ protected IEnumerable<KeyValuePair<int, int>> GetMatchingPairs(TSequence sequenceA, int lengthA, TSequence sequenceB, int lengthB)
121
+ {
122
+ int[,] d = ComputeCostMatrix(sequenceA, lengthA, sequenceB, lengthB);
123
+ int i = lengthA;
124
+ int j = lengthB;
125
+
126
+ while (i != 0 && j != 0)
127
+ {
128
+ if (d[i, j] == d[i - 1, j] + DeleteCost)
129
+ {
130
+ i--;
131
+ }
132
+ else if (d[i, j] == d[i, j - 1] + InsertCost)
133
+ {
134
+ j--;
135
+ }
136
+ else
137
+ {
138
+ i--;
139
+ j--;
140
+ yield return new KeyValuePair<int, int>(i, j);
141
+ }
142
+ }
143
+ }
144
+
145
+ protected IEnumerable<Edit> GetEdits(TSequence sequenceA, int lengthA, TSequence sequenceB, int lengthB)
146
+ {
147
+ int[,] d = ComputeCostMatrix(sequenceA, lengthA, sequenceB, lengthB);
148
+ int i = lengthA;
149
+ int j = lengthB;
150
+
151
+ while (i != 0 && j != 0)
152
+ {
153
+ if (d[i, j] == d[i - 1, j] + DeleteCost)
154
+ {
155
+ i--;
156
+ yield return new Edit(EditKind.Delete, i, -1);
157
+ }
158
+ else if (d[i, j] == d[i, j - 1] + InsertCost)
159
+ {
160
+ j--;
161
+ yield return new Edit(EditKind.Insert, -1, j);
162
+ }
163
+ else
164
+ {
165
+ i--;
166
+ j--;
167
+ yield return new Edit(EditKind.Update, i, j);
168
+ }
169
+ }
170
+
171
+ while (i > 0)
172
+ {
173
+ i--;
174
+ yield return new Edit(EditKind.Delete, i, -1);
175
+ }
176
+
177
+ while (j > 0)
178
+ {
179
+ j--;
180
+ yield return new Edit(EditKind.Insert, -1, j);
181
+ }
182
+ }
183
+
184
+ /// <summary>
185
+ /// Returns a distance [0..1] of the specified sequences.
186
+ /// The smaller distance the more of their elements match.
187
+ /// </summary>
188
+ /// <summary>
189
+ /// Returns a distance [0..1] of the specified sequences.
190
+ /// The smaller distance the more of their elements match.
191
+ /// </summary>
192
+ protected double ComputeDistance(TSequence sequenceA, int lengthA, TSequence sequenceB, int lengthB)
193
+ {
194
+ Debug.Assert(lengthA >= 0 && lengthB >= 0);
195
+
196
+ if (lengthA == 0 || lengthB == 0)
197
+ {
198
+ return (lengthA == lengthB) ? 0.0 : 1.0;
199
+ }
200
+
201
+ int lcsLength = 0;
202
+ foreach (var pair in GetMatchingPairs(sequenceA, lengthA, sequenceB, lengthB))
203
+ {
204
+ lcsLength++;
205
+ }
206
+
207
+ int max = Math.Max(lengthA, lengthB);
208
+ Debug.Assert(lcsLength <= max);
209
+ return 1.0 - (double)lcsLength / (double)max;
210
+ }
211
+
212
+ /// <summary>
213
+ /// Calculates costs of all paths in an edit graph starting from vertex (0,0) and ending in vertex (lengthA, lengthB).
214
+ /// </summary>
215
+ /// <remarks>
216
+ /// The edit graph for A and B has a vertex at each point in the grid (i,j), i in [0, lengthA] and j in [0, lengthB].
217
+ ///
218
+ /// The vertices of the edit graph are connected by horizontal, vertical, and diagonal directed edges to form a directed acyclic graph.
219
+ /// Horizontal edges connect each vertex to its right neighbor.
220
+ /// Vertical edges connect each vertex to the neighbor below it.
221
+ /// Diagonal edges connect vertex (i,j) to vertex (i-1,j-1) if <see cref="ItemsEqual"/>(sequenceA[i-1],sequenceB[j-1]) is true.
222
+ ///
223
+ /// Editing starts with S = [].
224
+ /// Move along horizontal edge (i-1,j)-(i,j) represents the fact that sequenceA[i-1] is not added to S.
225
+ /// Move along vertical edge (i,j-1)-(i,j) represents an insert of sequenceB[j-1] to S.
226
+ /// Move along diagonal edge (i-1,j-1)-(i,j) represents an addition of sequenceB[j-1] to S via an acceptable
227
+ /// change of sequenceA[i-1] to sequenceB[j-1].
228
+ ///
229
+ /// In every vertex the cheapest outgoing edge is selected.
230
+ /// The number of diagonal edges on the path from (0,0) to (lengthA, lengthB) is the length of the longest common subsequence.
231
+ /// </remarks>
232
+ private int[,] ComputeCostMatrix(TSequence sequenceA, int lengthA, TSequence sequenceB, int lengthB)
233
+ {
234
+ var la = lengthA + 1;
235
+ var lb = lengthB + 1;
236
+
237
+ // TODO: Optimization possible: O(ND) time, O(N) space
238
+ // EUGENE W. MYERS: An O(ND) Difference Algorithm and Its Variations
239
+ var d = new int[la, lb];
240
+
241
+ d[0, 0] = 0;
242
+ for (int i = 1; i <= lengthA; i++)
243
+ {
244
+ d[i, 0] = d[i - 1, 0] + DeleteCost;
245
+ }
246
+
247
+ for (int j = 1; j <= lengthB; j++)
248
+ {
249
+ d[0, j] = d[0, j - 1] + InsertCost;
250
+ }
251
+
252
+ for (int i = 1; i <= lengthA; i++)
253
+ {
254
+ for (int j = 1; j <= lengthB; j++)
255
+ {
256
+ int m1 = d[i - 1, j - 1] + (ItemsEqual(sequenceA, i - 1, sequenceB, j - 1) ? 0 : UpdateCost);
257
+ int m2 = d[i - 1, j] + DeleteCost;
258
+ int m3 = d[i, j - 1] + InsertCost;
259
+ d[i, j] = Math.Min(Math.Min(m1, m2), m3);
260
+ }
261
+ }
262
+
263
+ return d;
264
+ }
265
+ }
266
+ }