Skip to content

Commit c845fb5

Browse files
committed
Add API approvals
1 parent 6ec439e commit c845fb5

File tree

10 files changed

+3848
-1
lines changed

10 files changed

+3848
-1
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,6 @@ pip-log.txt
219219
*.userprefs
220220

221221
QRCoder/PortabilityAnalysis.html
222+
223+
# Unaccepted approval files
224+
*.received.txt

QRCoder.sln

+18
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QRCoderTests", "QRCoderTest
1515
EndProject
1616
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QRCoder.Xaml", "QRCoder.Xaml\QRCoder.Xaml.csproj", "{A7A7E073-2504-4BA2-A63B-87AC34174789}"
1717
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QRCoderApiTests", "QRCoderApiTests\QRCoderApiTests.csproj", "{5FACE5F6-53C9-4B89-91D4-162677893574}"
19+
EndProject
1820
Global
1921
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2022
Debug|Any CPU = Debug|Any CPU
@@ -119,6 +121,22 @@ Global
119121
{A7A7E073-2504-4BA2-A63B-87AC34174789}.Release|x64.Build.0 = Release|Any CPU
120122
{A7A7E073-2504-4BA2-A63B-87AC34174789}.Release|x86.ActiveCfg = Release|Any CPU
121123
{A7A7E073-2504-4BA2-A63B-87AC34174789}.Release|x86.Build.0 = Release|Any CPU
124+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
125+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Debug|Any CPU.Build.0 = Debug|Any CPU
126+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Debug|ARM.ActiveCfg = Debug|Any CPU
127+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Debug|ARM.Build.0 = Debug|Any CPU
128+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Debug|x64.ActiveCfg = Debug|Any CPU
129+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Debug|x64.Build.0 = Debug|Any CPU
130+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Debug|x86.ActiveCfg = Debug|Any CPU
131+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Debug|x86.Build.0 = Debug|Any CPU
132+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Release|Any CPU.ActiveCfg = Release|Any CPU
133+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Release|Any CPU.Build.0 = Release|Any CPU
134+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Release|ARM.ActiveCfg = Release|Any CPU
135+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Release|ARM.Build.0 = Release|Any CPU
136+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Release|x64.ActiveCfg = Release|Any CPU
137+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Release|x64.Build.0 = Release|Any CPU
138+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Release|x86.ActiveCfg = Release|Any CPU
139+
{5FACE5F6-53C9-4B89-91D4-162677893574}.Release|x86.Build.0 = Release|Any CPU
122140
EndGlobalSection
123141
GlobalSection(SolutionProperties) = preSolution
124142
HideSolutionNode = FALSE

QRCoder/ASCIIQRCode.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public AsciiQRCode() { }
1414

1515
public AsciiQRCode(QRCodeData data) : base(data) { }
1616

17-
17+
1818
/// <summary>
1919
/// Returns a strings that contains the resulting QR code as textual representation.
2020
/// </summary>

QRCoderApiTests/ApiApprovalTests.cs

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using PublicApiGenerator;
2+
using Shouldly;
3+
using System.Diagnostics;
4+
using System.Reflection;
5+
using System.Xml.Linq;
6+
using Xunit;
7+
8+
namespace QRCoderApiTests;
9+
10+
/// <summary>
11+
/// See more info about API approval tests here <see href="https://github.com/JakeGinnivan/ApiApprover"/>.
12+
/// </summary>
13+
public class ApiApprovalTests
14+
{
15+
[Theory]
16+
[InlineData(typeof(QRCoder.QRCodeData))]
17+
[InlineData(typeof(QRCoder.Xaml.XamlQRCode))]
18+
public void PublicApi(Type type)
19+
{
20+
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
21+
string projectName = type.Assembly.GetName().Name!;
22+
string testDir = Path.Combine(baseDir, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..");
23+
string projectDir = Path.Combine(testDir, "..");
24+
string buildDir = Path.Combine(projectDir, projectName, "bin", "Debug");
25+
Debug.Assert(Directory.Exists(buildDir), $"Directory '{buildDir}' doesn't exist");
26+
string csProject = Path.Combine(projectDir, projectName, projectName + ".csproj");
27+
var project = XDocument.Load(csProject);
28+
string[] tfms = project.Descendants("TargetFrameworks").Union(project.Descendants("TargetFramework")).First().Value.Split(";", StringSplitOptions.RemoveEmptyEntries);
29+
30+
// There may be old stuff from earlier builds like net45, netcoreapp3.0, etc. so filter it out
31+
string[] actualTfmDirs = Directory.GetDirectories(buildDir).Where(dir => tfms.Any(tfm => dir.EndsWith(tfm))).ToArray();
32+
Debug.Assert(actualTfmDirs.Length > 0, $"Directory '{buildDir}' doesn't contain subdirectories matching {string.Join(";", tfms)}");
33+
34+
(string tfm, string content)[] publicApi = actualTfmDirs.Select(tfmDir => (new DirectoryInfo(tfmDir).Name.Replace(".", ""), Assembly.LoadFile(Path.Combine(tfmDir, projectName + ".dll")).GeneratePublicApi(new ApiGeneratorOptions
35+
{
36+
IncludeAssemblyAttributes = false,
37+
//AllowNamespacePrefixes = new[] { "Microsoft.Extensions.DependencyInjection" },
38+
ExcludeAttributes = new[] { "System.Diagnostics.DebuggerDisplayAttribute", "System.Diagnostics.CodeAnalysis.AllowNullAttribute" }
39+
}) + Environment.NewLine)).ToArray();
40+
41+
if (publicApi.DistinctBy(item => item.content).Count() == 1)
42+
{
43+
AutoApproveOrFail(publicApi[0].content, "");
44+
}
45+
else
46+
{
47+
foreach (var item in publicApi.ToLookup(item => item.content))
48+
{
49+
AutoApproveOrFail(item.Key, string.Join("+", item.Select(x => x.tfm).OrderBy(x => x)));
50+
}
51+
}
52+
53+
// Approval test should (re)generate approved.txt files locally if needed.
54+
// Approval test should fail on CI.
55+
// https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
56+
void AutoApproveOrFail(string publicApi, string folder)
57+
{
58+
string file = null!;
59+
60+
try
61+
{
62+
publicApi.ShouldMatchApproved(options => options.SubFolder(folder).NoDiff().WithFilenameGenerator((testMethodInfo, discriminator, fileType, fileExtension) => file = $"{type.Assembly.GetName().Name}.{fileType}.{fileExtension}"));
63+
}
64+
catch (ShouldMatchApprovedException) when (Environment.GetEnvironmentVariable("CI") == null)
65+
{
66+
string? received = Path.Combine(testDir, folder, file);
67+
string? approved = received.Replace(".received.txt", ".approved.txt");
68+
if (File.Exists(received) && File.Exists(approved))
69+
{
70+
File.Copy(received, approved, overwrite: true);
71+
File.Delete(received);
72+
}
73+
else
74+
{
75+
throw;
76+
}
77+
}
78+
}
79+
}
80+
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace QRCoder.Xaml
2+
{
3+
public class XamlQRCode : QRCoder.AbstractQRCode, System.IDisposable
4+
{
5+
public XamlQRCode() { }
6+
public XamlQRCode(QRCoder.QRCodeData data) { }
7+
public System.Windows.Media.DrawingImage GetGraphic(int pixelsPerModule) { }
8+
public System.Windows.Media.DrawingImage GetGraphic(int pixelsPerModule, bool drawQuietZones) { }
9+
public System.Windows.Media.DrawingImage GetGraphic(System.Windows.Size viewBox, bool drawQuietZones = true) { }
10+
public System.Windows.Media.DrawingImage GetGraphic(int pixelsPerModule, string darkColorHex, string lightColorHex, bool drawQuietZones = true) { }
11+
public System.Windows.Media.DrawingImage GetGraphic(System.Windows.Size viewBox, System.Windows.Media.Brush darkBrush, System.Windows.Media.Brush lightBrush, bool drawQuietZones = true) { }
12+
public double GetUnitsPerModule(System.Windows.Size viewBox, bool drawQuietZones = true) { }
13+
}
14+
public static class XamlQRCodeHelper
15+
{
16+
public static System.Windows.Media.DrawingImage GetQRCode(string plainText, int pixelsPerModule, string darkColorHex, string lightColorHex, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1, bool drawQuietZones = true) { }
17+
}
18+
}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<IsTestProject>true</IsTestProject>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\QRCoder.Xaml\QRCoder.Xaml.csproj" />
12+
<ProjectReference Include="..\QRCoder\QRCoder.csproj" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<PackageReference Include="PublicApiGenerator" Version="11.1.0" />
17+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
18+
<PackageReference Include="xunit" Version="2.4.1" />
19+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
20+
<PackageReference Include="shouldly" Version="4.0.3" />
21+
</ItemGroup>
22+
23+
</Project>

0 commit comments

Comments
 (0)