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