Skip to content
This repository was archived by the owner on Apr 8, 2019. It is now read-only.

Commit be0f368

Browse files
committed
Add tests for DeclarePublicAPIAnalyzer
1 parent aeced43 commit be0f368

File tree

6 files changed

+318
-33
lines changed

6 files changed

+318
-33
lines changed
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace PublicApiAnalyzer.Test.ApiDesign
5+
{
6+
using System.Collections.Generic;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
using Microsoft.CodeAnalysis.CodeFixes;
10+
using Microsoft.CodeAnalysis.Diagnostics;
11+
using PublicApiAnalyzer.ApiDesign;
12+
using TestHelper;
13+
using Xunit;
14+
15+
public class DeclarePublicAPIAnalyzerTests : CodeFixVerifier
16+
{
17+
private string shippedText;
18+
private string unshippedText;
19+
20+
[Fact]
21+
public async Task SimpleMissingTypeAsync()
22+
{
23+
var source = @"
24+
public class C
25+
{
26+
}
27+
";
28+
29+
this.shippedText = string.Empty;
30+
this.unshippedText = string.Empty;
31+
32+
var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("C").WithLocation(2, 14);
33+
await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
34+
}
35+
36+
[Fact]
37+
public async Task SimpleMissingMemberAsync()
38+
{
39+
var source = @"
40+
public class C
41+
{
42+
public int Field;
43+
public int Property { get; set; }
44+
public void Method() { }
45+
}
46+
";
47+
48+
this.shippedText = string.Empty;
49+
this.unshippedText = string.Empty;
50+
51+
DiagnosticResult[] expected =
52+
{
53+
// Test0.cs(2,14): error RS0016: Symbol 'C' is not part of the declared API.
54+
this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("C").WithLocation(2, 14),
55+
56+
// Test0.cs(4,16): error RS0016: Symbol 'Field' is not part of the declared API.
57+
this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Field").WithLocation(4, 16),
58+
59+
// Test0.cs(5,27): error RS0016: Symbol 'Property.get' is not part of the declared API.
60+
this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Property.get").WithLocation(5, 27),
61+
62+
// Test0.cs(5,32): error RS0016: Symbol 'Property.set' is not part of the declared API.
63+
this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Property.set").WithLocation(5, 32),
64+
65+
// Test0.cs(6,17): error RS0016: Symbol 'Method' is not part of the declared API.
66+
this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Method").WithLocation(6, 17)
67+
};
68+
69+
await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
70+
}
71+
72+
[Fact]
73+
public async Task SimpleMemberAsync()
74+
{
75+
var source = @"
76+
public class C
77+
{
78+
public int Field;
79+
public int Property { get; set; }
80+
public void Method() { }
81+
}
82+
";
83+
84+
this.shippedText = @"
85+
C
86+
C.Field -> int
87+
C.Property.get -> int
88+
C.Property.set -> void
89+
C.Method() -> void
90+
";
91+
this.unshippedText = string.Empty;
92+
93+
await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
94+
}
95+
96+
[Fact]
97+
public async Task SplitBetweenShippedUnshippedAsync()
98+
{
99+
var source = @"
100+
public class C
101+
{
102+
public int Field;
103+
public int Property { get; set; }
104+
public void Method() { }
105+
}
106+
";
107+
108+
this.shippedText = @"
109+
C
110+
C.Field -> int
111+
C.Property.get -> int
112+
C.Property.set -> void
113+
";
114+
this.unshippedText = @"
115+
C.Method() -> void
116+
";
117+
118+
await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
119+
}
120+
121+
[Fact]
122+
public async Task EnumSplitBetweenFilesAsync()
123+
{
124+
var source = @"
125+
public enum E
126+
{
127+
V1 = 1,
128+
V2 = 2,
129+
V3 = 3,
130+
}
131+
";
132+
133+
this.shippedText = @"
134+
E
135+
E.V1 = 1 -> E
136+
E.V2 = 2 -> E
137+
";
138+
139+
this.unshippedText = @"
140+
E.V3 = 3 -> E
141+
";
142+
143+
await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
144+
}
145+
146+
[Fact]
147+
public async Task SimpleRemovedMemberAsync()
148+
{
149+
var source = @"
150+
public class C
151+
{
152+
public int Field;
153+
public int Property { get; set; }
154+
}
155+
";
156+
157+
this.shippedText = @"
158+
C
159+
C.Field -> int
160+
C.Property.get -> int
161+
C.Property.set -> void
162+
C.Method() -> void
163+
";
164+
165+
this.unshippedText = $@"
166+
{DeclarePublicAPIAnalyzer.RemovedApiPrefix}C.Method() -> void
167+
";
168+
169+
await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
170+
}
171+
172+
[Fact]
173+
public async Task ApiFileShippedWithRemovedAsync()
174+
{
175+
var source = @"
176+
public class C
177+
{
178+
public int Field;
179+
public int Property { get; set; }
180+
}
181+
";
182+
183+
this.shippedText = $@"
184+
C
185+
C.Field -> int
186+
C.Property.get -> int
187+
C.Property.set -> void
188+
{DeclarePublicAPIAnalyzer.RemovedApiPrefix}C.Method() -> void
189+
";
190+
191+
this.unshippedText = string.Empty;
192+
193+
// error RS0024: The contents of the public API files are invalid: The shipped API file can't have removed members
194+
var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.PublicApiFilesInvalid).WithArguments(DeclarePublicAPIAnalyzer.InvalidReasonShippedCantHaveRemoved);
195+
196+
await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
197+
}
198+
199+
[Fact]
200+
public async Task DuplicateSymbolInSameAPIFileAsync()
201+
{
202+
var source = @"
203+
public class C
204+
{
205+
public int Field;
206+
public int Property { get; set; }
207+
}
208+
";
209+
210+
this.shippedText = @"
211+
C
212+
C.Field -> int
213+
C.Property.get -> int
214+
C.Property.set -> void
215+
C.Property.get -> int
216+
";
217+
218+
this.unshippedText = string.Empty;
219+
220+
// Warning RS0025: The symbol 'C.Property.get -> int' appears more than once in the public API files.
221+
var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DuplicateSymbolInApiFiles)
222+
.WithArguments("C.Property.get -> int")
223+
.WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 6, 1)
224+
.WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 4, 1);
225+
226+
await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
227+
}
228+
229+
[Fact]
230+
public async Task DuplicateSymbolInDifferentAPIFilesAsync()
231+
{
232+
var source = @"
233+
public class C
234+
{
235+
public int Field;
236+
public int Property { get; set; }
237+
}
238+
";
239+
240+
this.shippedText = @"
241+
C
242+
C.Field -> int
243+
C.Property.get -> int
244+
C.Property.set -> void
245+
";
246+
247+
this.unshippedText = @"
248+
C.Property.get -> int";
249+
250+
// Warning RS0025: The symbol 'C.Property.get -> int' appears more than once in the public API files.
251+
var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DuplicateSymbolInApiFiles)
252+
.WithArguments("C.Property.get -> int")
253+
.WithLocation(DeclarePublicAPIAnalyzer.UnshippedFileName, 2, 1)
254+
.WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 4, 1);
255+
256+
await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
257+
}
258+
259+
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
260+
{
261+
yield return new DeclarePublicAPIAnalyzer();
262+
}
263+
264+
protected override CodeFixProvider GetCSharpCodeFixProvider()
265+
{
266+
return new DeclarePublicAPIFix();
267+
}
268+
269+
protected override string GetShippedPublicApi()
270+
{
271+
return this.shippedText;
272+
}
273+
274+
protected override string GetUnshippedPublicApi()
275+
{
276+
return this.unshippedText;
277+
}
278+
}
279+
}

PublicApiAnalyzer/PublicApiAnalyzer.Test/ExportCodeFixProviderAttributeNameTest.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ public void TestExportCodeFixProviderAttribute(Type codeFixProvider)
4343

4444
Assert.NotNull(exportCodeFixProviderAttribute);
4545
Assert.Equal(codeFixProvider.Name, exportCodeFixProviderAttribute.Name);
46-
Assert.Equal(1, exportCodeFixProviderAttribute.Languages.Length);
47-
Assert.Equal(LanguageNames.CSharp, exportCodeFixProviderAttribute.Languages[0]);
46+
Assert.Contains(LanguageNames.CSharp, exportCodeFixProviderAttribute.Languages);
4847
}
4948
}
5049
}

PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticVerifier.Helper.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace TestHelper
1414
using Microsoft.CodeAnalysis.Diagnostics;
1515
using Microsoft.CodeAnalysis.Text;
1616
using PublicApiAnalyzer;
17+
using PublicApiAnalyzer.ApiDesign;
1718
using PublicApiAnalyzer.Test.Helpers;
1819

1920
/// <summary>
@@ -58,7 +59,7 @@ protected static async Task<ImmutableArray<Diagnostic>> GetSortedDiagnosticsFrom
5859
var failureDiagnostics = allDiagnostics.Where(diagnostic => diagnostic.Id == "AD0001");
5960
foreach (var diag in diags.Concat(compilerErrors).Concat(failureDiagnostics))
6061
{
61-
if (diag.Location == Location.None || diag.Location.IsInMetadata)
62+
if (diag.Location == Location.None || !diag.Location.IsInSource)
6263
{
6364
diagnostics.Add(diag);
6465
}
@@ -71,6 +72,7 @@ protected static async Task<ImmutableArray<Diagnostic>> GetSortedDiagnosticsFrom
7172
if (tree == diag.Location.SourceTree)
7273
{
7374
diagnostics.Add(diag);
75+
break;
7476
}
7577
}
7678
}
@@ -121,11 +123,18 @@ protected virtual Solution CreateSolution(ProjectId projectId, string language)
121123
.AddMetadataReference(projectId, MetadataReferences.CSharpSymbolsReference)
122124
.AddMetadataReference(projectId, MetadataReferences.CodeAnalysisReference);
123125

124-
var settings = this.GetSettings();
125-
if (!string.IsNullOrEmpty(settings))
126+
var publicApi = this.GetUnshippedPublicApi();
127+
if (publicApi != null)
126128
{
127129
var documentId = DocumentId.CreateNewId(projectId);
128-
solution = solution.AddAdditionalDocument(documentId, SettingsHelper.PublicApiFileName, settings);
130+
solution = solution.AddAdditionalDocument(documentId, DeclarePublicAPIAnalyzer.UnshippedFileName, publicApi);
131+
}
132+
133+
publicApi = this.GetShippedPublicApi();
134+
if (publicApi != null)
135+
{
136+
var documentId = DocumentId.CreateNewId(projectId);
137+
solution = solution.AddAdditionalDocument(documentId, DeclarePublicAPIAnalyzer.ShippedFileName, publicApi);
129138
}
130139

131140
ParseOptions parseOptions = solution.GetProject(projectId).ParseOptions;
@@ -145,7 +154,16 @@ protected virtual IEnumerable<string> GetDisabledDiagnostics()
145154
/// Gets the content of the settings file to use.
146155
/// </summary>
147156
/// <returns>The contents of the settings file to use.</returns>
148-
protected virtual string GetSettings()
157+
protected virtual string GetUnshippedPublicApi()
158+
{
159+
return null;
160+
}
161+
162+
/// <summary>
163+
/// Gets the content of the settings file to use.
164+
/// </summary>
165+
/// <returns>The contents of the settings file to use.</returns>
166+
protected virtual string GetShippedPublicApi()
149167
{
150168
return null;
151169
}

PublicApiAnalyzer/PublicApiAnalyzer.Test/PublicApiAnalyzer.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
</Reference>
110110
</ItemGroup>
111111
<ItemGroup>
112+
<Compile Include="ApiDesign\DeclarePublicAPIAnalyzerTests.cs" />
112113
<Compile Include="AttributeTests.cs" />
113114
<Compile Include="ExclusionTests.cs" />
114115
<Compile Include="ExportCodeFixProviderAttributeNameTest.cs" />

0 commit comments

Comments
 (0)