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+ }
0 commit comments