@granite-js/screen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/GraniteScreen.podspec +25 -0
  3. package/LICENSE +202 -0
  4. package/android/CMakeLists.txt +62 -0
  5. package/android/build.gradle +63 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/BundleEvaluator.cpp +27 -0
  8. package/android/src/main/cpp/BundleEvaluator.h +17 -0
  9. package/android/src/main/cpp/onLoad.cpp +6 -0
  10. package/android/src/main/kotlin/run/granite/BundleEvaluator.kt +50 -0
  11. package/android/src/main/kotlin/run/granite/BundleLoader.kt +40 -0
  12. package/android/src/main/kotlin/run/granite/DefaultBundleLoader.kt +20 -0
  13. package/android/src/main/kotlin/run/granite/DefaultErrorView.kt +58 -0
  14. package/android/src/main/kotlin/run/granite/DefaultLoadingView.kt +51 -0
  15. package/android/src/main/kotlin/run/granite/GraniteReactDelegate.kt +76 -0
  16. package/android/src/main/kotlin/run/granite/GraniteReactDelegateImpl.kt +448 -0
  17. package/android/src/main/kotlin/run/granite/GraniteReactHost.kt +113 -0
  18. package/android/src/main/kotlin/run/granite/ReactHostFactory.kt +106 -0
  19. package/gradle-plugin/LICENSE +201 -0
  20. package/gradle-plugin/README.md +578 -0
  21. package/gradle-plugin/build.gradle.kts +97 -0
  22. package/gradle-plugin/gradle/libs.versions.toml +17 -0
  23. package/gradle-plugin/gradle/wrapper/gradle-wrapper.jar +0 -0
  24. package/gradle-plugin/gradle/wrapper/gradle-wrapper.properties +7 -0
  25. package/gradle-plugin/gradle.properties +12 -0
  26. package/gradle-plugin/gradlew +248 -0
  27. package/gradle-plugin/gradlew.bat +93 -0
  28. package/gradle-plugin/settings.gradle.kts +1 -0
  29. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteExtension.kt +225 -0
  30. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GranitePlugin.kt +784 -0
  31. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootExtension.kt +107 -0
  32. package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootProjectPlugin.kt +290 -0
  33. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/BuildConfigConfigurator.kt +69 -0
  34. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyConfigurator.kt +232 -0
  35. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyCoordinates.kt +29 -0
  36. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DevServerResourceConfigurator.kt +101 -0
  37. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/JniPackagingConfigurator.kt +160 -0
  38. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/NdkConfigurator.kt +135 -0
  39. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/RepositoryConfigurator.kt +148 -0
  40. package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/ResourceConfigurator.kt +56 -0
  41. package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CMakeGenerator.kt +105 -0
  42. package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CppAutolinkingGenerator.kt +152 -0
  43. package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/EntryPointGenerator.kt +100 -0
  44. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AndroidDependencyConfig.kt +23 -0
  45. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AutolinkingConfig.kt +89 -0
  46. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/CMakeEntry.kt +47 -0
  47. package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/NativeModule.kt +177 -0
  48. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AssetPackagingTask.kt +194 -0
  49. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AutolinkingTask.kt +431 -0
  50. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/BundleTask.kt +275 -0
  51. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenArtifactsTask.kt +218 -0
  52. package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenSchemaTask.kt +186 -0
  53. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/AutolinkingParser.kt +128 -0
  54. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ConflictDetector.kt +121 -0
  55. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/JdkValidator.kt +73 -0
  56. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/NodeExecutableFinder.kt +43 -0
  57. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ReactNativeVersionReader.kt +329 -0
  58. package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/TaskDependencyValidator.kt +198 -0
  59. package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteExtensionTest.kt +191 -0
  60. package/gradle-plugin/src/test/kotlin/run/granite/gradle/GranitePluginTest.kt +156 -0
  61. package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteRootProjectPluginTest.kt +87 -0
  62. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/BuildConfigConfiguratorTest.kt +115 -0
  63. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DependencyConfiguratorTest.kt +338 -0
  64. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DevServerResourceConfiguratorTest.kt +205 -0
  65. package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/ResourceConfiguratorTest.kt +131 -0
  66. package/gradle-plugin/src/test/kotlin/run/granite/gradle/fixtures/NativeModuleFixtures.kt +67 -0
  67. package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CMakeGeneratorTest.kt +71 -0
  68. package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CppAutolinkingGeneratorTest.kt +344 -0
  69. package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/EntryPointGeneratorTest.kt +40 -0
  70. package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/AutolinkingConfigTest.kt +350 -0
  71. package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/CMakeEntryTest.kt +200 -0
  72. package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/NativeModuleTest.kt +562 -0
  73. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AssetPackagingTaskTest.kt +318 -0
  74. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AutolinkingTaskTest.kt +89 -0
  75. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/BundleTaskTest.kt +68 -0
  76. package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/CodegenTasksTest.kt +410 -0
  77. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/AutolinkingParserTest.kt +335 -0
  78. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ConflictDetectorTest.kt +75 -0
  79. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/JdkValidatorTest.kt +88 -0
  80. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ReactNativeVersionReaderTest.kt +585 -0
  81. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskDependencyValidatorTest.kt +123 -0
  82. package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskTestUtils.kt +88 -0
  83. package/gradle-plugin/src/test/resources/fixtures/sample-rn-config.json +45 -0
  84. package/ios/BundleLoader/BundleEvaluator.h +16 -0
  85. package/ios/BundleLoader/BundleEvaluator.mm +76 -0
  86. package/ios/BundleLoader/BundleLoadable.swift +91 -0
  87. package/ios/GraniteBundleLoaderTypes.swift +7 -0
  88. package/ios/GraniteScreen.h +12 -0
  89. package/ios/ReactNativeHosting/DefaultViews.swift +138 -0
  90. package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.h +24 -0
  91. package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.mm +22 -0
  92. package/ios/ReactNativeHosting/GraniteHostingHelper.swift +103 -0
  93. package/ios/ReactNativeHosting/GraniteNativeFactory.swift +35 -0
  94. package/ios/ReactNativeHosting/GraniteNativeFactoryDelegateImpl.swift +30 -0
  95. package/ios/ReactNativeHosting/GraniteNativeFactoryImpl.swift +24 -0
  96. package/ios/ReactNativeHosting/GraniteReactHost.swift +39 -0
  97. package/ios/ReactNativeHosting/GraniteScreen-Bridging-Header.h +12 -0
  98. package/package.json +59 -0
  99. package/react-native.config.js +8 -0
@@ -0,0 +1,585 @@
1
+ package run.granite.gradle.utils
2
+
3
+ import org.junit.jupiter.api.Test
4
+ import org.junit.jupiter.api.assertThrows
5
+ import org.junit.jupiter.api.io.TempDir
6
+ import run.granite.gradle.config.DependencyCoordinates
7
+ import java.io.File
8
+ import kotlin.test.assertEquals
9
+ import kotlin.test.assertTrue
10
+
11
+ /**
12
+ * Unit tests for ReactNativeVersionReader.
13
+ */
14
+ class ReactNativeVersionReaderTest {
15
+
16
+ @TempDir
17
+ lateinit var tempDir: File
18
+
19
+ @Test
20
+ fun `readVersion reads version from package json`() {
21
+ // Given
22
+ val reactNativeDir = File(tempDir, "react-native")
23
+ reactNativeDir.mkdirs()
24
+
25
+ val packageJson = File(reactNativeDir, "package.json")
26
+ packageJson.writeText("""{"version":"0.81.6"}""")
27
+
28
+ // When
29
+ val version = ReactNativeVersionReader.readVersion(reactNativeDir)
30
+
31
+ // Then
32
+ assertEquals("0.81.6", version)
33
+ }
34
+
35
+ @Test
36
+ fun `readVersion handles version with pre-release suffix`() {
37
+ // Given
38
+ val reactNativeDir = File(tempDir, "react-native")
39
+ reactNativeDir.mkdirs()
40
+
41
+ val packageJson = File(reactNativeDir, "package.json")
42
+ packageJson.writeText("""{"version":"0.82.0-rc.0"}""")
43
+
44
+ // When
45
+ val version = ReactNativeVersionReader.readVersion(reactNativeDir)
46
+
47
+ // Then
48
+ assertEquals("0.82.0-rc.0", version)
49
+ }
50
+
51
+ @Test
52
+ fun `readVersion throws error when package json not found`() {
53
+ // Given
54
+ val reactNativeDir = File(tempDir, "nonexistent")
55
+
56
+ // When/Then
57
+ val exception = assertThrows<IllegalStateException> {
58
+ ReactNativeVersionReader.readVersion(reactNativeDir)
59
+ }
60
+
61
+ assertTrue(exception.message?.contains("package.json not found") == true)
62
+ }
63
+
64
+ @Test
65
+ fun `readVersion throws error when version field missing`() {
66
+ // Given
67
+ val reactNativeDir = File(tempDir, "react-native")
68
+ reactNativeDir.mkdirs()
69
+
70
+ val packageJson = File(reactNativeDir, "package.json")
71
+ packageJson.writeText("""{"name":"react-native"}""")
72
+
73
+ // When/Then
74
+ val exception = assertThrows<IllegalStateException> {
75
+ ReactNativeVersionReader.readVersion(reactNativeDir)
76
+ }
77
+
78
+ assertTrue(exception.message?.contains("version") == true)
79
+ }
80
+
81
+ @Test
82
+ fun `validateVersion succeeds for supported versions`() {
83
+ // Given
84
+ val supportedVersions = listOf("0.81.0", "0.81.6", "0.82.0", "1.0.0")
85
+
86
+ // When/Then - Should not throw
87
+ for (version in supportedVersions) {
88
+ ReactNativeVersionReader.validateVersion(version)
89
+ }
90
+ }
91
+
92
+ @Test
93
+ fun `validateVersion fails for unsupported versions`() {
94
+ // Given
95
+ val unsupportedVersions = listOf("0.80.9", "0.70.0", "0.60.0")
96
+
97
+ // When/Then
98
+ for (version in unsupportedVersions) {
99
+ val exception = assertThrows<IllegalStateException> {
100
+ ReactNativeVersionReader.validateVersion(version)
101
+ }
102
+
103
+ assertTrue(exception.message?.contains("not supported") == true)
104
+ assertTrue(exception.message?.contains("0.81.0") == true)
105
+ }
106
+ }
107
+
108
+ @Test
109
+ fun `getMinimumVersion returns correct minimum`() {
110
+ // When
111
+ val minVersion = ReactNativeVersionReader.getMinimumVersion()
112
+
113
+ // Then
114
+ assertEquals("0.81.0", minVersion)
115
+ }
116
+
117
+ @Test
118
+ fun `version comparison handles major version correctly`() {
119
+ // Test that 1.0.0 > 0.81.0
120
+ ReactNativeVersionReader.validateVersion("1.0.0")
121
+ }
122
+
123
+ @Test
124
+ fun `version comparison handles minor version correctly`() {
125
+ // Test that 0.82.0 > 0.81.0
126
+ ReactNativeVersionReader.validateVersion("0.82.0")
127
+ }
128
+
129
+ @Test
130
+ fun `version comparison handles patch version correctly`() {
131
+ // Test that 0.81.1 > 0.81.0
132
+ ReactNativeVersionReader.validateVersion("0.81.1")
133
+ }
134
+
135
+ @Test
136
+ fun `version comparison handles exact minimum version`() {
137
+ // Test that 0.81.0 == 0.81.0
138
+ ReactNativeVersionReader.validateVersion("0.81.0")
139
+ }
140
+
141
+ @Test
142
+ fun `readVersion handles malformed JSON gracefully`() {
143
+ // Given
144
+ val reactNativeDir = File(tempDir, "react-native")
145
+ reactNativeDir.mkdirs()
146
+
147
+ val packageJson = File(reactNativeDir, "package.json")
148
+ packageJson.writeText("""not valid json""")
149
+
150
+ // When/Then
151
+ assertThrows<IllegalStateException> {
152
+ ReactNativeVersionReader.readVersion(reactNativeDir)
153
+ }
154
+ }
155
+
156
+ @Test
157
+ fun `readVersionAndGroup reads from gradle properties`() {
158
+ // Given
159
+ val reactNativeDir = File(tempDir, "react-native")
160
+ reactNativeDir.mkdirs()
161
+
162
+ val reactAndroidDir = File(reactNativeDir, "ReactAndroid")
163
+ reactAndroidDir.mkdirs()
164
+
165
+ val gradleProperties = File(reactAndroidDir, "gradle.properties")
166
+ gradleProperties.writeText(
167
+ """
168
+ VERSION_NAME=0.81.1
169
+ react.internal.publishingGroup=com.facebook.react
170
+ """.trimIndent(),
171
+ )
172
+
173
+ val packageJson = File(reactNativeDir, "package.json")
174
+ packageJson.writeText("""{"version":"0.81.1"}""")
175
+
176
+ // When
177
+ val (version, group) = ReactNativeVersionReader.readVersionAndGroup(reactNativeDir)
178
+
179
+ // Then
180
+ assertEquals("0.81.1", version)
181
+ assertEquals("com.facebook.react", group)
182
+ }
183
+
184
+ @Test
185
+ fun `readVersionAndGroup returns default group when publishingGroup is missing`() {
186
+ // Given
187
+ val reactNativeDir = File(tempDir, "react-native")
188
+ reactNativeDir.mkdirs()
189
+
190
+ val reactAndroidDir = File(reactNativeDir, "ReactAndroid")
191
+ reactAndroidDir.mkdirs()
192
+
193
+ val gradleProperties = File(reactAndroidDir, "gradle.properties")
194
+ gradleProperties.writeText(
195
+ """
196
+ VERSION_NAME=0.81.1
197
+ """.trimIndent(),
198
+ )
199
+
200
+ val packageJson = File(reactNativeDir, "package.json")
201
+ packageJson.writeText("""{"version":"0.81.1"}""")
202
+
203
+ // When
204
+ val (version, group) = ReactNativeVersionReader.readVersionAndGroup(reactNativeDir)
205
+
206
+ // Then
207
+ assertEquals("0.81.1", version)
208
+ assertEquals("com.facebook.react", group)
209
+ }
210
+
211
+ @Test
212
+ fun `readVersionAndGroup handles custom publishingGroup`() {
213
+ // Given
214
+ val reactNativeDir = File(tempDir, "react-native")
215
+ reactNativeDir.mkdirs()
216
+
217
+ val reactAndroidDir = File(reactNativeDir, "ReactAndroid")
218
+ reactAndroidDir.mkdirs()
219
+
220
+ val gradleProperties = File(reactAndroidDir, "gradle.properties")
221
+ gradleProperties.writeText(
222
+ """
223
+ VERSION_NAME=0.81.1-nightly-20250123
224
+ react.internal.publishingGroup=com.custom.react
225
+ """.trimIndent(),
226
+ )
227
+
228
+ val packageJson = File(reactNativeDir, "package.json")
229
+ packageJson.writeText("""{"version":"0.81.1-nightly-20250123"}""")
230
+
231
+ // When
232
+ val (version, group) = ReactNativeVersionReader.readVersionAndGroup(reactNativeDir)
233
+
234
+ // Then
235
+ assertEquals("0.81.1-nightly-20250123-SNAPSHOT", version)
236
+ assertEquals("com.custom.react", group)
237
+ }
238
+
239
+ @Test
240
+ fun `readVersionAndGroup adds SNAPSHOT suffix for nightly versions starting with 0_0_0`() {
241
+ // Given
242
+ val reactNativeDir = File(tempDir, "react-native")
243
+ reactNativeDir.mkdirs()
244
+
245
+ val reactAndroidDir = File(reactNativeDir, "ReactAndroid")
246
+ reactAndroidDir.mkdirs()
247
+
248
+ val gradleProperties = File(reactAndroidDir, "gradle.properties")
249
+ gradleProperties.writeText(
250
+ """
251
+ VERSION_NAME=0.0.0-20250123-1234-abc123
252
+ react.internal.publishingGroup=com.facebook.react
253
+ """.trimIndent(),
254
+ )
255
+
256
+ val packageJson = File(reactNativeDir, "package.json")
257
+ packageJson.writeText("""{"version":"0.0.0-20250123-1234-abc123"}""")
258
+
259
+ // When
260
+ val (version, _) = ReactNativeVersionReader.readVersionAndGroup(reactNativeDir)
261
+
262
+ // Then
263
+ assertEquals("0.0.0-20250123-1234-abc123-SNAPSHOT", version)
264
+ }
265
+
266
+ @Test
267
+ fun `readVersionAndGroup adds SNAPSHOT suffix for nightly versions containing nightly`() {
268
+ // Given
269
+ val reactNativeDir = File(tempDir, "react-native")
270
+ reactNativeDir.mkdirs()
271
+
272
+ val reactAndroidDir = File(reactNativeDir, "ReactAndroid")
273
+ reactAndroidDir.mkdirs()
274
+
275
+ val gradleProperties = File(reactAndroidDir, "gradle.properties")
276
+ gradleProperties.writeText(
277
+ """
278
+ VERSION_NAME=0.81.0-nightly-20250123-abc123
279
+ react.internal.publishingGroup=com.facebook.react
280
+ """.trimIndent(),
281
+ )
282
+
283
+ val packageJson = File(reactNativeDir, "package.json")
284
+ packageJson.writeText("""{"version":"0.81.0-nightly-20250123-abc123"}""")
285
+
286
+ // When
287
+ val (version, _) = ReactNativeVersionReader.readVersionAndGroup(reactNativeDir)
288
+
289
+ // Then
290
+ assertEquals("0.81.0-nightly-20250123-abc123-SNAPSHOT", version)
291
+ }
292
+
293
+ @Test
294
+ fun `readVersionAndGroup does NOT add SNAPSHOT for stable releases`() {
295
+ // Given
296
+ val reactNativeDir = File(tempDir, "react-native")
297
+ reactNativeDir.mkdirs()
298
+
299
+ val reactAndroidDir = File(reactNativeDir, "ReactAndroid")
300
+ reactAndroidDir.mkdirs()
301
+
302
+ val gradleProperties = File(reactAndroidDir, "gradle.properties")
303
+ gradleProperties.writeText(
304
+ """
305
+ VERSION_NAME=0.81.6
306
+ react.internal.publishingGroup=com.facebook.react
307
+ """.trimIndent(),
308
+ )
309
+
310
+ val packageJson = File(reactNativeDir, "package.json")
311
+ packageJson.writeText("""{"version":"0.81.6"}""")
312
+
313
+ // When
314
+ val (version, _) = ReactNativeVersionReader.readVersionAndGroup(reactNativeDir)
315
+
316
+ // Then
317
+ assertEquals("0.81.6", version)
318
+ }
319
+
320
+ @Test
321
+ fun `readVersionAndGroup does NOT add SNAPSHOT for rc versions`() {
322
+ // Given
323
+ val reactNativeDir = File(tempDir, "react-native")
324
+ reactNativeDir.mkdirs()
325
+
326
+ val reactAndroidDir = File(reactNativeDir, "ReactAndroid")
327
+ reactAndroidDir.mkdirs()
328
+
329
+ val gradleProperties = File(reactAndroidDir, "gradle.properties")
330
+ gradleProperties.writeText(
331
+ """
332
+ VERSION_NAME=0.82.0-rc.0
333
+ react.internal.publishingGroup=com.facebook.react
334
+ """.trimIndent(),
335
+ )
336
+
337
+ val packageJson = File(reactNativeDir, "package.json")
338
+ packageJson.writeText("""{"version":"0.82.0-rc.0"}""")
339
+
340
+ // When
341
+ val (version, _) = ReactNativeVersionReader.readVersionAndGroup(reactNativeDir)
342
+
343
+ // Then
344
+ assertEquals("0.82.0-rc.0", version)
345
+ }
346
+
347
+ @Test
348
+ fun `readVersionAndGroup falls back to package json when gradle properties missing`() {
349
+ // Given
350
+ val reactNativeDir = File(tempDir, "react-native")
351
+ reactNativeDir.mkdirs()
352
+ // No ReactAndroid/gradle.properties - fallback to package.json
353
+
354
+ val packageJson = File(reactNativeDir, "package.json")
355
+ packageJson.writeText("""{"version":"0.81.6"}""")
356
+
357
+ // When
358
+ val (version, group) = ReactNativeVersionReader.readVersionAndGroup(reactNativeDir)
359
+
360
+ // Then
361
+ assertEquals("0.81.6", version)
362
+ assertEquals("com.facebook.react", group)
363
+ }
364
+
365
+ @Test
366
+ fun `readVersionAndGroup throws error when VERSION_NAME empty and no package json`() {
367
+ // Given
368
+ val reactNativeDir = File(tempDir, "react-native")
369
+ reactNativeDir.mkdirs()
370
+
371
+ val reactAndroidDir = File(reactNativeDir, "ReactAndroid")
372
+ reactAndroidDir.mkdirs()
373
+
374
+ val gradleProperties = File(reactAndroidDir, "gradle.properties")
375
+ gradleProperties.writeText(
376
+ """
377
+ react.internal.publishingGroup=com.custom.react
378
+ """.trimIndent(),
379
+ )
380
+ // No VERSION_NAME - error expected
381
+
382
+ // When/Then
383
+ val exception = assertThrows<IllegalStateException> {
384
+ ReactNativeVersionReader.readVersionAndGroup(reactNativeDir)
385
+ }
386
+
387
+ assertTrue(exception.message?.contains("VERSION_NAME") == true)
388
+ }
389
+
390
+ // === readCoordinates tests ===
391
+
392
+ @Test
393
+ fun `readCoordinates reads all version info from both files`() {
394
+ // Given
395
+ val reactNativeDir = setupReactNativeDirForCoordinates(
396
+ gradleProperties = """
397
+ VERSION_NAME=0.84.0-rc.4
398
+ react.internal.publishingGroup=com.facebook.react
399
+ react.internal.hermesPublishingGroup=com.facebook.hermes
400
+ """.trimIndent(),
401
+ hermesVersionProperties = """
402
+ HERMES_VERSION_NAME=0.15.0
403
+ HERMES_V1_VERSION_NAME=250829098.0.6
404
+ """.trimIndent(),
405
+ )
406
+
407
+ // When
408
+ val coordinates = ReactNativeVersionReader.readCoordinates(reactNativeDir)
409
+
410
+ // Then
411
+ assertEquals("0.84.0-rc.4", coordinates.reactVersion)
412
+ assertEquals("0.15.0", coordinates.hermesVersion)
413
+ assertEquals("250829098.0.6", coordinates.hermesV1Version)
414
+ assertEquals("com.facebook.react", coordinates.reactGroup)
415
+ assertEquals("com.facebook.hermes", coordinates.hermesGroup)
416
+ }
417
+
418
+ @Test
419
+ fun `readCoordinates uses default hermes group when not specified`() {
420
+ // Given
421
+ val reactNativeDir = setupReactNativeDirForCoordinates(
422
+ gradleProperties = """
423
+ VERSION_NAME=0.84.0
424
+ react.internal.publishingGroup=com.facebook.react
425
+ """.trimIndent(),
426
+ hermesVersionProperties = """
427
+ HERMES_VERSION_NAME=0.15.0
428
+ HERMES_V1_VERSION_NAME=250829098.0.6
429
+ """.trimIndent(),
430
+ )
431
+
432
+ // When
433
+ val coordinates = ReactNativeVersionReader.readCoordinates(reactNativeDir)
434
+
435
+ // Then
436
+ assertEquals("com.facebook.hermes", coordinates.hermesGroup)
437
+ }
438
+
439
+ @Test
440
+ fun `readCoordinates falls back to RN version when hermes version file missing`() {
441
+ // Given - RN 0.83 or lower scenario
442
+ val reactNativeDir = setupReactNativeDirForCoordinates(
443
+ gradleProperties = """
444
+ VERSION_NAME=0.83.0
445
+ react.internal.publishingGroup=com.facebook.react
446
+ """.trimIndent(),
447
+ hermesVersionProperties = null, // File doesn't exist
448
+ )
449
+
450
+ // When
451
+ val coordinates = ReactNativeVersionReader.readCoordinates(reactNativeDir)
452
+
453
+ // Then
454
+ assertEquals("0.83.0", coordinates.reactVersion)
455
+ assertEquals("0.83.0", coordinates.hermesVersion)
456
+ assertEquals("0.83.0", coordinates.hermesV1Version)
457
+ }
458
+
459
+ @Test
460
+ fun `readCoordinates handles nightly hermes version with SNAPSHOT suffix`() {
461
+ // Given
462
+ val reactNativeDir = setupReactNativeDirForCoordinates(
463
+ gradleProperties = """
464
+ VERSION_NAME=0.84.0
465
+ """.trimIndent(),
466
+ hermesVersionProperties = """
467
+ HERMES_VERSION_NAME=0.0.0-commitly-20250123-abc123
468
+ HERMES_V1_VERSION_NAME=250829098.0.0
469
+ """.trimIndent(),
470
+ )
471
+
472
+ // When
473
+ val coordinates = ReactNativeVersionReader.readCoordinates(reactNativeDir)
474
+
475
+ // Then
476
+ assertEquals("0.0.0-commitly-20250123-abc123-SNAPSHOT", coordinates.hermesVersion)
477
+ }
478
+
479
+ @Test
480
+ fun `readCoordinates returns V1 version from getEffectiveHermesVersion`() {
481
+ // Given
482
+ val reactNativeDir = setupReactNativeDirForCoordinates(
483
+ gradleProperties = """
484
+ VERSION_NAME=0.84.0
485
+ """.trimIndent(),
486
+ hermesVersionProperties = """
487
+ HERMES_VERSION_NAME=0.15.0
488
+ HERMES_V1_VERSION_NAME=250829098.0.6
489
+ """.trimIndent(),
490
+ )
491
+
492
+ // When
493
+ val coordinates = ReactNativeVersionReader.readCoordinates(reactNativeDir)
494
+
495
+ // Then
496
+ assertEquals("250829098.0.6", coordinates.getEffectiveHermesVersion())
497
+ }
498
+
499
+ @Test
500
+ fun `readCoordinates returns classic version when V1 empty`() {
501
+ // Given
502
+ val reactNativeDir = setupReactNativeDirForCoordinates(
503
+ gradleProperties = """
504
+ VERSION_NAME=0.84.0
505
+ """.trimIndent(),
506
+ hermesVersionProperties = """
507
+ HERMES_VERSION_NAME=0.15.0
508
+ HERMES_V1_VERSION_NAME=
509
+ """.trimIndent(),
510
+ )
511
+
512
+ // When
513
+ val coordinates = ReactNativeVersionReader.readCoordinates(reactNativeDir)
514
+
515
+ // Then
516
+ assertEquals("0.15.0", coordinates.getEffectiveHermesVersion())
517
+ }
518
+
519
+ @Test
520
+ fun `readCoordinates handles empty hermes version file gracefully`() {
521
+ // Given - Hermes version file exists but is empty
522
+ val reactNativeDir = setupReactNativeDirForCoordinates(
523
+ gradleProperties = """
524
+ VERSION_NAME=0.84.0
525
+ """.trimIndent(),
526
+ hermesVersionProperties = "", // Empty file
527
+ )
528
+
529
+ // When
530
+ val coordinates = ReactNativeVersionReader.readCoordinates(reactNativeDir)
531
+
532
+ // Then - Fallback to RN version
533
+ assertEquals("0.84.0", coordinates.hermesVersion)
534
+ assertEquals("0.84.0", coordinates.hermesV1Version)
535
+ }
536
+
537
+ @Test
538
+ fun `readCoordinates handles malformed hermes version file gracefully`() {
539
+ // Given - Hermes version file has invalid format
540
+ val reactNativeDir = setupReactNativeDirForCoordinates(
541
+ gradleProperties = """
542
+ VERSION_NAME=0.84.0
543
+ """.trimIndent(),
544
+ hermesVersionProperties = """
545
+ INVALID_KEY=some_value
546
+ ANOTHER_INVALID=123
547
+ """.trimIndent(),
548
+ )
549
+
550
+ // When
551
+ val coordinates = ReactNativeVersionReader.readCoordinates(reactNativeDir)
552
+
553
+ // Then - Fallback to RN version
554
+ assertEquals("0.84.0", coordinates.hermesVersion)
555
+ assertEquals("0.84.0", coordinates.hermesV1Version)
556
+ }
557
+
558
+ // === Helper for readCoordinates tests ===
559
+
560
+ private fun setupReactNativeDirForCoordinates(
561
+ gradleProperties: String,
562
+ hermesVersionProperties: String?,
563
+ packageJsonVersion: String = "0.84.0",
564
+ ): File {
565
+ val reactNativeDir = File(tempDir, "rn-coords-${System.nanoTime()}")
566
+ reactNativeDir.mkdirs()
567
+
568
+ // package.json (for fallback)
569
+ File(reactNativeDir, "package.json").writeText("""{"version":"$packageJsonVersion"}""")
570
+
571
+ // ReactAndroid/gradle.properties
572
+ val reactAndroidDir = File(reactNativeDir, "ReactAndroid")
573
+ reactAndroidDir.mkdirs()
574
+ File(reactAndroidDir, "gradle.properties").writeText(gradleProperties)
575
+
576
+ // sdks/hermes-engine/version.properties
577
+ if (hermesVersionProperties != null) {
578
+ val hermesDir = File(reactNativeDir, "sdks/hermes-engine")
579
+ hermesDir.mkdirs()
580
+ File(hermesDir, "version.properties").writeText(hermesVersionProperties)
581
+ }
582
+
583
+ return reactNativeDir
584
+ }
585
+ }
@@ -0,0 +1,123 @@
1
+ package run.granite.gradle.utils
2
+
3
+ import org.gradle.api.DefaultTask
4
+ import org.gradle.testfixtures.ProjectBuilder
5
+ import org.junit.jupiter.api.BeforeEach
6
+ import org.junit.jupiter.api.Test
7
+ import kotlin.test.assertEquals
8
+ import kotlin.test.assertTrue
9
+
10
+ /**
11
+ * Unit tests for TaskDependencyValidator.
12
+ */
13
+ class TaskDependencyValidatorTest {
14
+
15
+ private lateinit var project: org.gradle.api.Project
16
+
17
+ @BeforeEach
18
+ fun setup() {
19
+ project = ProjectBuilder.builder().build()
20
+ }
21
+
22
+ @Test
23
+ fun `validateRequiredTasks returns empty list when all tasks exist`() {
24
+ // Given
25
+ project.tasks.register("task1", DefaultTask::class.java)
26
+ project.tasks.register("task2", DefaultTask::class.java)
27
+
28
+ val requiredTasks = listOf("task1", "task2")
29
+
30
+ // When
31
+ val missingTasks = TaskDependencyValidator.validateRequiredTasks(project, requiredTasks)
32
+
33
+ // Then
34
+ assertTrue(missingTasks.isEmpty())
35
+ }
36
+
37
+ @Test
38
+ fun `validateRequiredTasks returns missing task names`() {
39
+ // Given
40
+ project.tasks.register("task1", DefaultTask::class.java)
41
+ // task2 not registered
42
+
43
+ val requiredTasks = listOf("task1", "task2", "task3")
44
+
45
+ // When
46
+ val missingTasks = TaskDependencyValidator.validateRequiredTasks(project, requiredTasks)
47
+
48
+ // Then
49
+ assertEquals(2, missingTasks.size)
50
+ assertTrue(missingTasks.contains("task2"))
51
+ assertTrue(missingTasks.contains("task3"))
52
+ }
53
+
54
+ @Test
55
+ fun `validateRequiredTasks handles empty required tasks list`() {
56
+ // Given
57
+ val requiredTasks = emptyList<String>()
58
+
59
+ // When
60
+ val missingTasks = TaskDependencyValidator.validateRequiredTasks(project, requiredTasks)
61
+
62
+ // Then
63
+ assertTrue(missingTasks.isEmpty())
64
+ }
65
+
66
+ @Test
67
+ fun `validateTaskInputsOutputs warns for tasks without inputs or outputs`() {
68
+ // Given
69
+ val task = project.tasks.register("testTask", DefaultTask::class.java).get()
70
+
71
+ // When/Then - Should not throw, just log warning
72
+ TaskDependencyValidator.validateTaskInputsOutputs(project, task)
73
+ }
74
+
75
+ @Test
76
+ fun `validateAutolinkingBeforeCodegen validates task dependency`() {
77
+ // Given
78
+ val autolinkingTask = project.tasks.register("autolinking", DefaultTask::class.java).get()
79
+ val codegenTask = project.tasks.register("codegen", DefaultTask::class.java).get()
80
+
81
+ // When/Then - Should not throw
82
+ TaskDependencyValidator.validateAutolinkingBeforeCodegen(project, autolinkingTask, codegenTask)
83
+ }
84
+
85
+ @Test
86
+ fun `validateCodegenBeforeCompilation validates task dependency`() {
87
+ // Given
88
+ val codegenTask = project.tasks.register("codegen", DefaultTask::class.java).get()
89
+ val compileTask = project.tasks.register("compile", DefaultTask::class.java).get()
90
+
91
+ // When/Then - Should not throw
92
+ TaskDependencyValidator.validateCodegenBeforeCompilation(project, codegenTask, compileTask)
93
+ }
94
+
95
+ @Test
96
+ fun `validateBundleBeforePackaging validates task dependency`() {
97
+ // Given
98
+ val bundleTask = project.tasks.register("bundle", DefaultTask::class.java).get()
99
+ val packageTask = project.tasks.register("package", DefaultTask::class.java).get()
100
+
101
+ // When/Then - Should not throw
102
+ TaskDependencyValidator.validateBundleBeforePackaging(project, bundleTask, packageTask)
103
+ }
104
+
105
+ @Test
106
+ fun `validateInfrastructureBeforeVariant validates task dependency`() {
107
+ // Given
108
+ val infrastructureTask = project.tasks.register("infrastructure", DefaultTask::class.java).get()
109
+ val variantTask = project.tasks.register("variant", DefaultTask::class.java).get()
110
+
111
+ // When/Then - Should not throw
112
+ TaskDependencyValidator.validateInfrastructureBeforeVariant(project, infrastructureTask, variantTask)
113
+ }
114
+
115
+ @Test
116
+ fun `logTaskDependencies logs task information`() {
117
+ // Given
118
+ val task = project.tasks.register("testTask", DefaultTask::class.java).get()
119
+
120
+ // When/Then - Should not throw
121
+ TaskDependencyValidator.logTaskDependencies(project, task)
122
+ }
123
+ }