99import java .nio .file .Path ;
1010import java .nio .file .StandardCopyOption ;
1111import java .util .HashMap ;
12+ import java .util .List ;
1213import java .util .Map ;
1314
1415import javax .naming .InvalidNameException ;
1516
17+ import org .junit .jupiter .api .AfterEach ;
1618import org .junit .jupiter .api .BeforeEach ;
1719import org .junit .jupiter .api .Test ;
1820import org .junit .jupiter .api .TestInfo ;
@@ -42,7 +44,25 @@ public void setUp(TestInfo testInfo) throws IOException, InvalidNameException {
4244 this .buildDir .mkdir ();
4345 this .podsDir = new File (this .workingDir , "pods" );
4446 this .podsDir .mkdir ();
45- this .workingDir .deleteOnExit ();
47+ }
48+
49+ @ AfterEach
50+ public void tearDown () throws IOException {
51+ if (this .workingDir != null && this .workingDir .exists ()) {
52+ deleteRecursively (this .workingDir .toPath ());
53+ }
54+ }
55+
56+ private void deleteRecursively (Path path ) throws IOException {
57+ Files .walk (path )
58+ .sorted ((a , b ) -> b .compareTo (a )) // reverse order to delete children first
59+ .forEach (p -> {
60+ try {
61+ Files .delete (p );
62+ } catch (IOException e ) {
63+ // ignore cleanup errors
64+ }
65+ });
4666 }
4767
4868 @ Test
@@ -107,9 +127,9 @@ public void testCompilationFlags() throws IOException, ExtenderException {
107127 public void testNestedPodSpecName () throws IOException , ExtenderException {
108128 String jsonSpec = Files .readString (Path .of ("test-data/pod_specs/YandexMobileMetrica.json" ));
109129 XCConfigParser parser = new XCConfigParser (this .buildDir , this .podsDir , PodUtils .Platform .IPHONEOS , "Debug" , "arm64" );
110- File sentryTargetFolder = Path .of (this .podsDir .toString (), "Target Support Files" , "YandexMobileMetrica" ).toFile ();
111- sentryTargetFolder .mkdirs ();
112- Files .copy (Path .of ("test-data/xcconfigs/YandexMobileMetrica.xcconfig" ), Path .of (sentryTargetFolder .toString (), "YandexMobileMetrica.debug.xcconfig" ), StandardCopyOption .REPLACE_EXISTING );
130+ File metricaTargetFolder = Path .of (this .podsDir .toString (), "Target Support Files" , "YandexMobileMetrica" ).toFile ();
131+ metricaTargetFolder .mkdirs ();
132+ Files .copy (Path .of ("test-data/xcconfigs/YandexMobileMetrica.xcconfig" ), Path .of (metricaTargetFolder .toString (), "YandexMobileMetrica.debug.xcconfig" ), StandardCopyOption .REPLACE_EXISTING );
113133
114134 PodSpec podSpec = PodSpecParser .createPodSpec (PodSpecParser .parseJson (jsonSpec ), PodUtils .Platform .IPHONEOS , null );
115135 CocoaPodsServiceBuildState cocoapodsState = new CocoaPodsServiceBuildState ();
@@ -128,4 +148,190 @@ public void testNestedPodSpecName() throws IOException, ExtenderException {
128148 PodBuildSpec buildSpec = new PodBuildSpec (args , podSpec );
129149 assertEquals (buildSpec .name , "YandexMobileMetrica" );
130150 }
151+
152+ @ Test
153+ public void testPublicHeaderPatternMatch () throws IOException , ExtenderException {
154+ String jsonSpec = Files .readString (Path .of ("test-data/pod_specs/Sentry.json" ));
155+ XCConfigParser parser = new XCConfigParser (this .buildDir , this .podsDir , PodUtils .Platform .IPHONEOS , "Debug" , "arm64" );
156+ File sentryTargetFolder = Path .of (this .podsDir .toString (), "Target Support Files" , "Sentry" ).toFile ();
157+ sentryTargetFolder .mkdirs ();
158+ Files .copy (Path .of ("test-data/xcconfigs/Sentry.xcconfig" ), Path .of (sentryTargetFolder .toString (), "Sentry.debug.xcconfig" ), StandardCopyOption .REPLACE_EXISTING );
159+
160+ // Files matching public_header_files pattern: Sources/Sentry/Public/*.h
161+ List <String > publicHeaderFiles = List .of (
162+ "Sources/Sentry/Public/SentrySDK.h" ,
163+ "Sources/Sentry/Public/SentryClient.h" ,
164+ "Sources/Sentry/Public/SentryOptions.h"
165+ );
166+
167+ // Files NOT matching the pattern (should not be included in publicHeaders)
168+ List <String > otherFiles = List .of (
169+ "Sources/Sentry/Private/SentryPrivate.h" ,
170+ "Sources/Sentry/SentryInternal.h" ,
171+ "Sources/Sentry/Public/SentrySDK.m" ,
172+ "Sources/SentryCrash/SentryCrash.h"
173+ );
174+
175+ // Create all files
176+ Path sentrySourcesDir = Path .of (this .podsDir .toString (), "Sentry" );
177+ for (String filePath : publicHeaderFiles ) {
178+ Path fullPath = sentrySourcesDir .resolve (filePath );
179+ Files .createDirectories (fullPath .getParent ());
180+ Files .createFile (fullPath );
181+ }
182+ for (String filePath : otherFiles ) {
183+ Path fullPath = sentrySourcesDir .resolve (filePath );
184+ Files .createDirectories (fullPath .getParent ());
185+ Files .createFile (fullPath );
186+ }
187+
188+ PodSpec podSpec = PodSpecParser .createPodSpec (PodSpecParser .parseJson (jsonSpec ), PodUtils .Platform .IPHONEOS , null );
189+ CocoaPodsServiceBuildState cocoapodsState = new CocoaPodsServiceBuildState ();
190+ cocoapodsState .workingDir = this .workingDir ;
191+ cocoapodsState .podsDir = this .podsDir ;
192+ cocoapodsState .selectedPlatform = PodUtils .Platform .IPHONEOS ;
193+
194+ CreateBuildSpecArgs args = new CreateBuildSpecArgs .Builder ()
195+ .setConfigParser (parser )
196+ .setCocoapodsBuildState (cocoapodsState )
197+ .setJobContext (this .jobContext )
198+ .build ();
199+ args .buildDir = this .buildDir ;
200+ args .configuration = "Debug" ;
201+
202+ PodBuildSpec buildSpec = new PodBuildSpec (args , podSpec );
203+ buildSpec .addSubSpec (podSpec .getSubspec ("Core" ));
204+
205+ // Verify only the public_header_files pattern matches are included
206+ for (String headerFile : publicHeaderFiles ) {
207+ Path expectedPath = sentrySourcesDir .resolve (headerFile );
208+ assertTrue (
209+ buildSpec .publicHeaders .stream ().anyMatch (h -> h .getAbsolutePath ().equals (expectedPath .toString ())),
210+ "publicHeaders should contain: " + expectedPath
211+ );
212+ }
213+
214+ assertEquals (publicHeaderFiles .size (), buildSpec .publicHeaders .size (),
215+ "publicHeaders should contain exactly " + publicHeaderFiles .size () + " files" );
216+ }
217+
218+ @ Test
219+ public void testNoPublicHeaderPattern () throws IOException , ExtenderException {
220+ String jsonSpec = Files .readString (Path .of ("test-data/pod_specs/AppAuth.json" ));
221+ XCConfigParser parser = new XCConfigParser (this .buildDir , this .podsDir , PodUtils .Platform .IPHONEOS , "Debug" , "arm64" );
222+ File appAuthTargetFolder = Path .of (this .podsDir .toString (), "Target Support Files" , "AppAuth" ).toFile ();
223+ appAuthTargetFolder .mkdirs ();
224+ Files .copy (Path .of ("test-data/xcconfigs/AppAuth.xcconfig" ), Path .of (appAuthTargetFolder .toString (), "AppAuth.debug.xcconfig" ), StandardCopyOption .REPLACE_EXISTING );
225+
226+ List <String > tmpFiles = List .of (
227+ "AppAuth.h" ,
228+ "AppAuth/iOS/OIDAuthState+IOS.h" ,
229+ "AppAuth/iOS/OIDAuthState+IOS.m" ,
230+ "AppAuth/iOS/OIDAuthorizationService+IOS.h" ,
231+ "AppAuth/iOS/OIDAuthorizationService+IOS.m" ,
232+ "AppAuth/iOS/OIDExternalUserAgentCatalyst.h" ,
233+ "AppAuth/iOS/OIDExternalUserAgentCatalyst.m" ,
234+ "AppAuth/iOS/OIDExternalUserAgentIOS.h" ,
235+ "AppAuth/iOS/OIDExternalUserAgentIOS.m" ,
236+ "AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.h" ,
237+ "AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m" ,
238+ "AppAuthCore.h" ,
239+ "AppAuthCore/OIDAuthState.h" ,
240+ "AppAuthCore/OIDAuthState.m" ,
241+ "AppAuthCore/OIDAuthStateChangeDelegate.h" ,
242+ "AppAuthCore/OIDAuthStateErrorDelegate.h" ,
243+ "AppAuthCore/OIDAuthorizationRequest.h" ,
244+ "AppAuthCore/OIDAuthorizationRequest.m" ,
245+ "AppAuthCore/OIDAuthorizationResponse.h" ,
246+ "AppAuthCore/OIDAuthorizationResponse.m" ,
247+ "AppAuthCore/OIDAuthorizationService.h" ,
248+ "AppAuthCore/OIDAuthorizationService.m" ,
249+ "AppAuthCore/OIDClientMetadataParameters.h" ,
250+ "AppAuthCore/OIDClientMetadataParameters.m" ,
251+ "AppAuthCore/OIDDefines.h" ,
252+ "AppAuthCore/OIDEndSessionRequest.h" ,
253+ "AppAuthCore/OIDEndSessionRequest.m" ,
254+ "AppAuthCore/OIDEndSessionResponse.h" ,
255+ "AppAuthCore/OIDEndSessionResponse.m" ,
256+ "AppAuthCore/OIDError.h" ,
257+ "AppAuthCore/OIDError.m" ,
258+ "AppAuthCore/OIDErrorUtilities.h" ,
259+ "AppAuthCore/OIDErrorUtilities.m" ,
260+ "AppAuthCore/OIDExternalUserAgent.h" ,
261+ "AppAuthCore/OIDExternalUserAgentRequest.h" ,
262+ "AppAuthCore/OIDExternalUserAgentSession.h" ,
263+ "AppAuthCore/OIDFieldMapping.h" ,
264+ "AppAuthCore/OIDFieldMapping.m" ,
265+ "AppAuthCore/OIDGrantTypes.h" ,
266+ "AppAuthCore/OIDGrantTypes.m" ,
267+ "AppAuthCore/OIDIDToken.h" ,
268+ "AppAuthCore/OIDIDToken.m" ,
269+ "AppAuthCore/OIDRegistrationRequest.h" ,
270+ "AppAuthCore/OIDRegistrationRequest.m" ,
271+ "AppAuthCore/OIDRegistrationResponse.h" ,
272+ "AppAuthCore/OIDRegistrationResponse.m" ,
273+ "AppAuthCore/OIDResponseTypes.h" ,
274+ "AppAuthCore/OIDResponseTypes.m" ,
275+ "AppAuthCore/OIDScopeUtilities.h" ,
276+ "AppAuthCore/OIDScopeUtilities.m" ,
277+ "AppAuthCore/OIDScopes.h" ,
278+ "AppAuthCore/OIDScopes.m" ,
279+ "AppAuthCore/OIDServiceConfiguration.h" ,
280+ "AppAuthCore/OIDServiceConfiguration.m" ,
281+ "AppAuthCore/OIDServiceDiscovery.h" ,
282+ "AppAuthCore/OIDServiceDiscovery.m" ,
283+ "AppAuthCore/OIDTokenRequest.h" ,
284+ "AppAuthCore/OIDTokenRequest.m" ,
285+ "AppAuthCore/OIDTokenResponse.h" ,
286+ "AppAuthCore/OIDTokenResponse.m" ,
287+ "AppAuthCore/OIDTokenUtilities.h" ,
288+ "AppAuthCore/OIDTokenUtilities.m" ,
289+ "AppAuthCore/OIDURLQueryComponent.h" ,
290+ "AppAuthCore/OIDURLQueryComponent.m" ,
291+ "AppAuthCore/OIDURLSessionProvider.h" ,
292+ "AppAuthCore/OIDURLSessionProvider.m" ,
293+ "AppAuthCore/Resources/PrivacyInfo.xcprivacy"
294+ );
295+
296+ // Create empty files at enumerated paths
297+ Path appAuthSourcesDir = Path .of (this .podsDir .toString (), "AppAuth" , "Sources" );
298+ for (String filePath : tmpFiles ) {
299+ Path fullPath = appAuthSourcesDir .resolve (filePath );
300+ Files .createDirectories (fullPath .getParent ());
301+ Files .createFile (fullPath );
302+ }
303+
304+
305+ PodSpec podSpec = PodSpecParser .createPodSpec (PodSpecParser .parseJson (jsonSpec ), PodUtils .Platform .IPHONEOS , null );
306+ CocoaPodsServiceBuildState cocoapodsState = new CocoaPodsServiceBuildState ();
307+ cocoapodsState .workingDir = this .workingDir ;
308+ cocoapodsState .podsDir = this .podsDir ;
309+ cocoapodsState .selectedPlatform = PodUtils .Platform .IPHONEOS ;
310+
311+ CreateBuildSpecArgs args = new CreateBuildSpecArgs .Builder ()
312+ .setConfigParser (parser )
313+ .setCocoapodsBuildState (cocoapodsState )
314+ .setJobContext (this .jobContext )
315+ .build ();
316+ args .buildDir = this .buildDir ;
317+ args .configuration = "Debug" ;
318+
319+ PodBuildSpec buildSpec = new PodBuildSpec (args , podSpec );
320+ buildSpec .addSubSpec (podSpec .getSubspec ("Core" ));
321+ buildSpec .addSubSpec (podSpec .getSubspec ("ExternalUserAgent" ));
322+ List <String > expectedHeaders = tmpFiles .stream ()
323+ .filter (f -> f .endsWith (".h" ))
324+ .toList ();
325+
326+ for (String headerFile : expectedHeaders ) {
327+ Path expectedPath = appAuthSourcesDir .resolve (headerFile );
328+ assertTrue (
329+ buildSpec .publicHeaders .stream ().anyMatch (h -> h .getAbsolutePath ().equals (expectedPath .toString ())),
330+ "publicHeaders should contain: " + expectedPath
331+ );
332+ }
333+
334+ assertEquals (expectedHeaders .size (), buildSpec .publicHeaders .size (),
335+ "publicHeaders should contain exactly " + expectedHeaders .size () + " files" );
336+ }
131337}
0 commit comments