@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,335 @@
1
+ package run.granite.gradle.utils
2
+
3
+ import org.assertj.core.api.Assertions.assertThat
4
+ import org.assertj.core.api.Assertions.assertThatThrownBy
5
+ import org.junit.jupiter.api.Test
6
+
7
+ /**
8
+ * Unit tests for AutolinkingParser core parsing logic.
9
+ * Tests JSON parsing and error handling.
10
+ */
11
+ class AutolinkingParserTest {
12
+
13
+ @Test
14
+ fun `parse valid JSON with complete configuration`() {
15
+ val json = """
16
+ {
17
+ "project": {
18
+ "name": "test-project",
19
+ "version": "1.0.0",
20
+ "android": {
21
+ "sourceDir": "android",
22
+ "manifestPath": "android/AndroidManifest.xml",
23
+ "packageName": "com.example.test"
24
+ }
25
+ },
26
+ "dependencies": {
27
+ "react-native-webview": {
28
+ "name": "react-native-webview",
29
+ "root": "/path/to/module",
30
+ "platforms": {
31
+ "android": {
32
+ "sourceDir": "android",
33
+ "packageImportPath": "com.example.Package",
34
+ "packageInstance": "new Package()",
35
+ "libraryName": "RNCWebView",
36
+ "componentDescriptors": ["Component1"],
37
+ "cmakeListsPath": "android/CMakeLists.txt"
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ """.trimIndent()
44
+
45
+ val config = AutolinkingParser.parse(json)
46
+
47
+ // Verify project info
48
+ assertThat(config.project.name).isEqualTo("test-project")
49
+ assertThat(config.project.version).isEqualTo("1.0.0")
50
+ assertThat(config.project.android).isNotNull
51
+ assertThat(config.project.android?.packageName).isEqualTo("com.example.test")
52
+
53
+ // Verify dependencies
54
+ assertThat(config.dependencies).hasSize(1)
55
+ assertThat(config.dependencies).containsKey("react-native-webview")
56
+
57
+ // Verify Android dependency config
58
+ val androidModules = config.androidDependencies()
59
+ assertThat(androidModules).hasSize(1)
60
+ assertThat(androidModules[0].name).isEqualTo("react-native-webview")
61
+ assertThat(androidModules[0].packageImportPath).isEqualTo("com.example.Package")
62
+ assertThat(androidModules[0].libraryName).isEqualTo("RNCWebView")
63
+ }
64
+
65
+ @Test
66
+ fun `parse JSON with null fields treats them as absent`() {
67
+ // Null and missing fields treated identically
68
+ val json = """
69
+ {
70
+ "project": {
71
+ "name": "test-project",
72
+ "version": null,
73
+ "android": {
74
+ "sourceDir": "android",
75
+ "manifestPath": null,
76
+ "packageName": "com.example.test"
77
+ }
78
+ },
79
+ "dependencies": {
80
+ "test-module": {
81
+ "name": "test-module",
82
+ "root": "/path",
83
+ "platforms": {
84
+ "android": {
85
+ "sourceDir": "android",
86
+ "packageImportPath": null,
87
+ "packageInstance": null,
88
+ "libraryName": "testlib",
89
+ "componentDescriptors": null
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ """.trimIndent()
96
+
97
+ val config = AutolinkingParser.parse(json)
98
+
99
+ assertThat(config.project.version).isNull()
100
+ assertThat(config.project.android?.manifestPath).isNull()
101
+
102
+ val modules = config.androidDependencies()
103
+ assertThat(modules[0].packageImportPath).isNull()
104
+ assertThat(modules[0].packageInstance).isNull()
105
+ assertThat(modules[0].componentDescriptors).isEmpty()
106
+ }
107
+
108
+ @Test
109
+ fun `parse JSON with missing dependencies field returns empty dependencies`() {
110
+ val json = """
111
+ {
112
+ "project": {
113
+ "name": "test-project"
114
+ },
115
+ "dependencies": {}
116
+ }
117
+ """.trimIndent()
118
+
119
+ val config = AutolinkingParser.parse(json)
120
+
121
+ assertThat(config.dependencies).isEmpty()
122
+ assertThat(config.androidDependencies()).isEmpty()
123
+ }
124
+
125
+ @Test
126
+ fun `parse JSON with JavaScript-only modules excludes them from androidDependencies`() {
127
+ val json = """
128
+ {
129
+ "project": {
130
+ "name": "test-project"
131
+ },
132
+ "dependencies": {
133
+ "js-only-module": {
134
+ "name": "js-only-module",
135
+ "root": "/path",
136
+ "platforms": {}
137
+ },
138
+ "ios-only-module": {
139
+ "name": "ios-only-module",
140
+ "root": "/path",
141
+ "platforms": {
142
+ "ios": {
143
+ "sourceDir": "ios"
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ """.trimIndent()
150
+
151
+ val config = AutolinkingParser.parse(json)
152
+
153
+ assertThat(config.dependencies).hasSize(2)
154
+ assertThat(config.androidDependencies()).isEmpty() // No Android modules
155
+ }
156
+
157
+ @Test
158
+ fun `throws IllegalArgumentException when JSON is malformed`() {
159
+ // Fail-fast with descriptive error
160
+ val malformedJson = "{ invalid json "
161
+
162
+ assertThatThrownBy {
163
+ AutolinkingParser.parse(malformedJson)
164
+ }
165
+ .isInstanceOf(IllegalArgumentException::class.java)
166
+ .hasMessageContaining("react-native config")
167
+ .hasMessageContaining("Failed to parse JSON")
168
+ .hasMessageContaining("syntax error")
169
+ }
170
+
171
+ @Test
172
+ fun `throws IllegalArgumentException when project field missing`() {
173
+ // Fail-fast with descriptive error
174
+ val json = """
175
+ {
176
+ "dependencies": {}
177
+ }
178
+ """.trimIndent()
179
+
180
+ assertThatThrownBy {
181
+ AutolinkingParser.parse(json)
182
+ }
183
+ .isInstanceOf(IllegalArgumentException::class.java)
184
+ .hasMessageContaining("react-native config")
185
+ .hasMessageContaining("missing 'project' field")
186
+ .hasMessageContaining("Ensure react-native CLI is properly configured")
187
+ }
188
+
189
+ @Test
190
+ fun `throws IllegalArgumentException when JSON has unexpected structure`() {
191
+ // Fail-fast with descriptive error
192
+ val json = """
193
+ {
194
+ "project": "invalid-should-be-object",
195
+ "dependencies": {}
196
+ }
197
+ """.trimIndent()
198
+
199
+ assertThatThrownBy {
200
+ AutolinkingParser.parse(json)
201
+ }
202
+ .isInstanceOf(IllegalArgumentException::class.java)
203
+ .hasMessageContaining("react-native config")
204
+ // ClassCastException wrapped in IllegalArgumentException
205
+ }
206
+
207
+ @Test
208
+ fun `parse handles empty project android field`() {
209
+ val json = """
210
+ {
211
+ "project": {
212
+ "name": "test-project",
213
+ "android": null
214
+ },
215
+ "dependencies": {}
216
+ }
217
+ """.trimIndent()
218
+
219
+ val config = AutolinkingParser.parse(json)
220
+
221
+ assertThat(config.project.android).isNull()
222
+ }
223
+
224
+ @Test
225
+ fun `parse handles all AndroidDependencyConfig fields`() {
226
+ val json = """
227
+ {
228
+ "project": {
229
+ "name": "test-project"
230
+ },
231
+ "dependencies": {
232
+ "complex-module": {
233
+ "name": "complex-module",
234
+ "root": "/path",
235
+ "platforms": {
236
+ "android": {
237
+ "sourceDir": "android",
238
+ "packageImportPath": "com.example.Package",
239
+ "packageInstance": "new Package()",
240
+ "buildTypes": ["debug", "release"],
241
+ "libraryName": "ComplexModule",
242
+ "componentDescriptors": ["Comp1", "Comp2"],
243
+ "cmakeListsPath": "android/CMakeLists.txt",
244
+ "cxxModuleCMakeListsPath": "android/cxx/CMakeLists.txt",
245
+ "cxxModuleCMakeListsModuleName": "complex_cxx",
246
+ "cxxModuleHeaderName": "ComplexModule",
247
+ "isPureCxxDependency": false
248
+ }
249
+ }
250
+ }
251
+ }
252
+ }
253
+ """.trimIndent()
254
+
255
+ val config = AutolinkingParser.parse(json)
256
+ val modules = config.androidDependencies()
257
+
258
+ assertThat(modules).hasSize(1)
259
+ val module = modules[0]
260
+
261
+ assertThat(module.packageImportPath).isEqualTo("com.example.Package")
262
+ assertThat(module.packageInstance).isEqualTo("new Package()")
263
+ assertThat(module.libraryName).isEqualTo("ComplexModule")
264
+ assertThat(module.componentDescriptors).containsExactly("Comp1", "Comp2")
265
+ assertThat(module.cmakeListsPath).isEqualTo("android/CMakeLists.txt")
266
+ assertThat(module.cxxModuleCMakeListsPath).isEqualTo("android/cxx/CMakeLists.txt")
267
+ assertThat(module.cxxModuleHeaderName).isEqualTo("ComplexModule")
268
+ assertThat(module.isPureCxxDependency).isFalse
269
+ }
270
+
271
+ @Test
272
+ fun `parse handles module with multiple dependencies`() {
273
+ val json = """
274
+ {
275
+ "project": {
276
+ "name": "test-project"
277
+ },
278
+ "dependencies": {
279
+ "module1": {
280
+ "name": "module1",
281
+ "root": "/path1",
282
+ "platforms": {
283
+ "android": {
284
+ "sourceDir": "android",
285
+ "libraryName": "Module1"
286
+ }
287
+ }
288
+ },
289
+ "module2": {
290
+ "name": "module2",
291
+ "root": "/path2",
292
+ "platforms": {
293
+ "android": {
294
+ "sourceDir": "android",
295
+ "libraryName": "Module2"
296
+ }
297
+ }
298
+ },
299
+ "module3": {
300
+ "name": "module3",
301
+ "root": "/path3",
302
+ "platforms": {
303
+ "android": {
304
+ "sourceDir": "android",
305
+ "libraryName": "Module3"
306
+ }
307
+ }
308
+ }
309
+ }
310
+ }
311
+ """.trimIndent()
312
+
313
+ val config = AutolinkingParser.parse(json)
314
+
315
+ assertThat(config.dependencies).hasSize(3)
316
+ assertThat(config.androidDependencies()).hasSize(3)
317
+ assertThat(config.androidDependencies().map { it.name })
318
+ .containsExactlyInAnyOrder("module1", "module2", "module3")
319
+ }
320
+
321
+ @Test
322
+ fun `parse loads fixture JSON file successfully`() {
323
+ val fixtureJson = javaClass.classLoader
324
+ .getResourceAsStream("fixtures/sample-rn-config.json")!!
325
+ .bufferedReader()
326
+ .use { it.readText() }
327
+
328
+ val config = AutolinkingParser.parse(fixtureJson)
329
+
330
+ assertThat(config.project.name).isEqualTo("test-project")
331
+ assertThat(config.project.android?.packageName).isEqualTo("com.example.testproject")
332
+ assertThat(config.dependencies).hasSize(3)
333
+ assertThat(config.androidDependencies()).hasSize(2) // pure-js-module excluded
334
+ }
335
+ }
@@ -0,0 +1,75 @@
1
+ package run.granite.gradle.utils
2
+
3
+ import org.gradle.testfixtures.ProjectBuilder
4
+ import org.junit.jupiter.api.Test
5
+ import org.junit.jupiter.api.assertThrows
6
+ import kotlin.test.assertFalse
7
+ import kotlin.test.assertTrue
8
+
9
+ /**
10
+ * Unit tests for ConflictDetector.
11
+ */
12
+ class ConflictDetectorTest {
13
+
14
+ @Test
15
+ fun `validateNoConflicts succeeds when no React Native plugin is applied`() {
16
+ // Given
17
+ val project = ProjectBuilder.builder().build()
18
+ project.pluginManager.apply("com.android.library")
19
+
20
+ // When/Then - Should not throw
21
+ ConflictDetector.validateNoConflicts(project)
22
+ }
23
+
24
+ @Test
25
+ fun `validateNoConflicts fails when React Native plugin is applied`() {
26
+ // Given
27
+ val project = ProjectBuilder.builder().build()
28
+
29
+ // Apply a mock plugin ID (can't apply actual React Native plugin in unit tests)
30
+ // This test verifies the logic structure
31
+
32
+ // When/Then
33
+ // Note: In real scenario with actual React Native plugin, this would throw
34
+ // For unit test, we just verify the method exists and is callable
35
+ ConflictDetector.validateNoConflicts(project)
36
+ }
37
+
38
+ @Test
39
+ fun `hasReactNativePlugin returns false when no RN plugin applied`() {
40
+ // Given
41
+ val project = ProjectBuilder.builder().build()
42
+ project.pluginManager.apply("com.android.library")
43
+
44
+ // When
45
+ val hasPlugin = ConflictDetector.hasReactNativePlugin(project)
46
+
47
+ // Then
48
+ assertFalse(hasPlugin)
49
+ }
50
+
51
+ @Test
52
+ fun `detectMultipleGraniteModules warns when multiple modules use plugin`() {
53
+ // Given - Create root project with subprojects
54
+ val rootProject = ProjectBuilder.builder()
55
+ .withName("root")
56
+ .build()
57
+
58
+ val subproject1 = ProjectBuilder.builder()
59
+ .withName("lib1")
60
+ .withParent(rootProject)
61
+ .build()
62
+
63
+ val subproject2 = ProjectBuilder.builder()
64
+ .withName("lib2")
65
+ .withParent(rootProject)
66
+ .build()
67
+
68
+ // Apply Granite plugin to both (simulated)
69
+ // Note: Full multi-module detection requires actual plugin application
70
+ // which is tested in integration tests
71
+
72
+ // When/Then - Should log warning but not fail
73
+ ConflictDetector.validateNoConflicts(subproject1)
74
+ }
75
+ }
@@ -0,0 +1,88 @@
1
+ package run.granite.gradle.utils
2
+
3
+ import org.gradle.api.JavaVersion
4
+ import org.gradle.testfixtures.ProjectBuilder
5
+ import org.junit.jupiter.api.Test
6
+ import org.junit.jupiter.api.assertThrows
7
+ import kotlin.test.assertEquals
8
+ import kotlin.test.assertTrue
9
+
10
+ /**
11
+ * Unit tests for JdkValidator.
12
+ */
13
+ class JdkValidatorTest {
14
+
15
+ @Test
16
+ fun `getCurrentVersion returns current Java version`() {
17
+ // When
18
+ val version = JdkValidator.getCurrentVersion()
19
+
20
+ // Then
21
+ assertEquals(JavaVersion.current(), version)
22
+ }
23
+
24
+ @Test
25
+ fun `getMinimumVersion returns Java 17`() {
26
+ // When
27
+ val version = JdkValidator.getMinimumVersion()
28
+
29
+ // Then
30
+ assertEquals(JavaVersion.VERSION_17, version)
31
+ }
32
+
33
+ @Test
34
+ fun `isCompatible returns true for JDK 17 and above`() {
35
+ // Given - Current JDK is being used
36
+ val currentJdk = JavaVersion.current()
37
+
38
+ // When
39
+ val isCompatible = JdkValidator.isCompatible()
40
+
41
+ // Then
42
+ if (currentJdk >= JavaVersion.VERSION_17) {
43
+ assertTrue(isCompatible)
44
+ }
45
+ }
46
+
47
+ @Test
48
+ fun `validate succeeds when JDK is 17 or higher`() {
49
+ // Given
50
+ val project = ProjectBuilder.builder().build()
51
+ val currentJdk = JavaVersion.current()
52
+
53
+ // When/Then - Should not throw if current JDK >= 17
54
+ if (currentJdk >= JavaVersion.VERSION_17) {
55
+ // Should succeed
56
+ JdkValidator.validate(project)
57
+ } else {
58
+ // Should fail
59
+ assertThrows<IllegalStateException> {
60
+ JdkValidator.validate(project)
61
+ }
62
+ }
63
+ }
64
+
65
+ @Test
66
+ fun `validate provides helpful error message for incompatible JDK`() {
67
+ // Given
68
+ val currentJdk = JavaVersion.current()
69
+
70
+ // Skip test if running on JDK 17+
71
+ if (currentJdk >= JavaVersion.VERSION_17) {
72
+ return
73
+ }
74
+
75
+ // Given
76
+ val project = ProjectBuilder.builder().build()
77
+
78
+ // When/Then
79
+ val exception = assertThrows<IllegalStateException> {
80
+ JdkValidator.validate(project)
81
+ }
82
+
83
+ // Verify error message contains helpful information
84
+ val message = exception.message ?: ""
85
+ assertTrue(message.contains("JDK 17"))
86
+ assertTrue(message.contains("JAVA_HOME") || message.contains("gradle.properties"))
87
+ }
88
+ }