@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.
- package/CHANGELOG.md +7 -0
- package/GraniteScreen.podspec +25 -0
- package/LICENSE +202 -0
- package/android/CMakeLists.txt +62 -0
- package/android/build.gradle +63 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/BundleEvaluator.cpp +27 -0
- package/android/src/main/cpp/BundleEvaluator.h +17 -0
- package/android/src/main/cpp/onLoad.cpp +6 -0
- package/android/src/main/kotlin/run/granite/BundleEvaluator.kt +50 -0
- package/android/src/main/kotlin/run/granite/BundleLoader.kt +40 -0
- package/android/src/main/kotlin/run/granite/DefaultBundleLoader.kt +20 -0
- package/android/src/main/kotlin/run/granite/DefaultErrorView.kt +58 -0
- package/android/src/main/kotlin/run/granite/DefaultLoadingView.kt +51 -0
- package/android/src/main/kotlin/run/granite/GraniteReactDelegate.kt +76 -0
- package/android/src/main/kotlin/run/granite/GraniteReactDelegateImpl.kt +448 -0
- package/android/src/main/kotlin/run/granite/GraniteReactHost.kt +113 -0
- package/android/src/main/kotlin/run/granite/ReactHostFactory.kt +106 -0
- package/gradle-plugin/LICENSE +201 -0
- package/gradle-plugin/README.md +578 -0
- package/gradle-plugin/build.gradle.kts +97 -0
- package/gradle-plugin/gradle/libs.versions.toml +17 -0
- package/gradle-plugin/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle-plugin/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/gradle-plugin/gradle.properties +12 -0
- package/gradle-plugin/gradlew +248 -0
- package/gradle-plugin/gradlew.bat +93 -0
- package/gradle-plugin/settings.gradle.kts +1 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteExtension.kt +225 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GranitePlugin.kt +784 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootExtension.kt +107 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootProjectPlugin.kt +290 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/BuildConfigConfigurator.kt +69 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyConfigurator.kt +232 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyCoordinates.kt +29 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DevServerResourceConfigurator.kt +101 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/JniPackagingConfigurator.kt +160 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/NdkConfigurator.kt +135 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/RepositoryConfigurator.kt +148 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/ResourceConfigurator.kt +56 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CMakeGenerator.kt +105 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CppAutolinkingGenerator.kt +152 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/EntryPointGenerator.kt +100 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AndroidDependencyConfig.kt +23 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AutolinkingConfig.kt +89 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/CMakeEntry.kt +47 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/NativeModule.kt +177 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AssetPackagingTask.kt +194 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AutolinkingTask.kt +431 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/BundleTask.kt +275 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenArtifactsTask.kt +218 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenSchemaTask.kt +186 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/AutolinkingParser.kt +128 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ConflictDetector.kt +121 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/JdkValidator.kt +73 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/NodeExecutableFinder.kt +43 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ReactNativeVersionReader.kt +329 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/TaskDependencyValidator.kt +198 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteExtensionTest.kt +191 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GranitePluginTest.kt +156 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteRootProjectPluginTest.kt +87 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/BuildConfigConfiguratorTest.kt +115 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DependencyConfiguratorTest.kt +338 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DevServerResourceConfiguratorTest.kt +205 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/ResourceConfiguratorTest.kt +131 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/fixtures/NativeModuleFixtures.kt +67 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CMakeGeneratorTest.kt +71 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CppAutolinkingGeneratorTest.kt +344 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/EntryPointGeneratorTest.kt +40 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/AutolinkingConfigTest.kt +350 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/CMakeEntryTest.kt +200 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/NativeModuleTest.kt +562 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AssetPackagingTaskTest.kt +318 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AutolinkingTaskTest.kt +89 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/BundleTaskTest.kt +68 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/CodegenTasksTest.kt +410 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/AutolinkingParserTest.kt +335 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ConflictDetectorTest.kt +75 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/JdkValidatorTest.kt +88 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ReactNativeVersionReaderTest.kt +585 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskDependencyValidatorTest.kt +123 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskTestUtils.kt +88 -0
- package/gradle-plugin/src/test/resources/fixtures/sample-rn-config.json +45 -0
- package/ios/BundleLoader/BundleEvaluator.h +16 -0
- package/ios/BundleLoader/BundleEvaluator.mm +76 -0
- package/ios/BundleLoader/BundleLoadable.swift +91 -0
- package/ios/GraniteBundleLoaderTypes.swift +7 -0
- package/ios/GraniteScreen.h +12 -0
- package/ios/ReactNativeHosting/DefaultViews.swift +138 -0
- package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.h +24 -0
- package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.mm +22 -0
- package/ios/ReactNativeHosting/GraniteHostingHelper.swift +103 -0
- package/ios/ReactNativeHosting/GraniteNativeFactory.swift +35 -0
- package/ios/ReactNativeHosting/GraniteNativeFactoryDelegateImpl.swift +30 -0
- package/ios/ReactNativeHosting/GraniteNativeFactoryImpl.swift +24 -0
- package/ios/ReactNativeHosting/GraniteReactHost.swift +39 -0
- package/ios/ReactNativeHosting/GraniteScreen-Bridging-Header.h +12 -0
- package/package.json +59 -0
- package/react-native.config.js +8 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
package run.granite.gradle.tasks
|
|
2
|
+
|
|
3
|
+
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
+
import org.junit.jupiter.api.Test
|
|
5
|
+
import org.junit.jupiter.api.io.TempDir
|
|
6
|
+
import run.granite.gradle.utils.createTestFile
|
|
7
|
+
import run.granite.gradle.utils.createTestTask
|
|
8
|
+
import run.granite.gradle.utils.readFileContent
|
|
9
|
+
import java.io.File
|
|
10
|
+
import java.util.zip.GZIPInputStream
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Unit tests for AssetPackagingTask.
|
|
14
|
+
*
|
|
15
|
+
* Tests asset packaging and compression logic by executing the actual task.
|
|
16
|
+
*/
|
|
17
|
+
class AssetPackagingTaskTest {
|
|
18
|
+
|
|
19
|
+
@TempDir
|
|
20
|
+
lateinit var tempDir: File
|
|
21
|
+
|
|
22
|
+
@Test
|
|
23
|
+
fun `task is cacheable`() {
|
|
24
|
+
val annotations = AssetPackagingTask::class.annotations
|
|
25
|
+
assertThat(annotations)
|
|
26
|
+
.anyMatch { it.annotationClass.simpleName == "CacheableTask" }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Test
|
|
30
|
+
fun `task has correct group and description`() {
|
|
31
|
+
val task = createTestTask<AssetPackagingTask>()
|
|
32
|
+
|
|
33
|
+
assertThat(task.group).isEqualTo("granite")
|
|
34
|
+
assertThat(task.description).isEqualTo("Packages React Native assets and bundles")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Test
|
|
38
|
+
fun `packageBundle copies uncompressed bundle when compression disabled`() {
|
|
39
|
+
// Create test bundle file
|
|
40
|
+
val bundleContent = "// React Native bundle\nvar __BUNDLE_START_TIME__=Date.now();"
|
|
41
|
+
val bundleFile = createTestFile(tempDir, "index.android.bundle", bundleContent)
|
|
42
|
+
|
|
43
|
+
val outputDir = File(tempDir, "output/assets")
|
|
44
|
+
val outputFile = File(outputDir, "index.android.bundle")
|
|
45
|
+
|
|
46
|
+
val task = createTestTask<AssetPackagingTask> {
|
|
47
|
+
it.bundleFile.set(bundleFile)
|
|
48
|
+
it.outputAssetsDir.set(outputDir)
|
|
49
|
+
it.bundleAssetName.set("index.android.bundle")
|
|
50
|
+
it.compressionEnabled.set(false)
|
|
51
|
+
it.variantName.set("release")
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
task.execute()
|
|
55
|
+
|
|
56
|
+
// Verify uncompressed bundle was copied
|
|
57
|
+
assertThat(outputFile).exists()
|
|
58
|
+
assertThat(readFileContent(outputFile)).isEqualTo(bundleContent)
|
|
59
|
+
|
|
60
|
+
// Verify no gzipped file was created
|
|
61
|
+
assertThat(File(outputDir, "index.android.bundle.gz")).doesNotExist()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Test
|
|
65
|
+
fun `packageBundle creates gzipped bundle when compression enabled`() {
|
|
66
|
+
// Create test bundle file
|
|
67
|
+
val bundleContent = "// React Native bundle\nvar __BUNDLE_START_TIME__=Date.now();"
|
|
68
|
+
val bundleFile = createTestFile(tempDir, "index.android.hbc", bundleContent)
|
|
69
|
+
|
|
70
|
+
val outputDir = File(tempDir, "output/assets")
|
|
71
|
+
val compressedFile = File(outputDir, "index.android.hbc.gz")
|
|
72
|
+
|
|
73
|
+
val task = createTestTask<AssetPackagingTask> {
|
|
74
|
+
it.bundleFile.set(bundleFile)
|
|
75
|
+
it.outputAssetsDir.set(outputDir)
|
|
76
|
+
it.bundleAssetName.set("index.android.hbc")
|
|
77
|
+
it.compressionEnabled.set(true)
|
|
78
|
+
it.variantName.set("release")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
task.execute()
|
|
82
|
+
|
|
83
|
+
// Verify gzipped bundle was created
|
|
84
|
+
assertThat(compressedFile).exists()
|
|
85
|
+
|
|
86
|
+
// Verify content can be decompressed
|
|
87
|
+
val decompressed = GZIPInputStream(compressedFile.inputStream()).use {
|
|
88
|
+
it.readBytes().toString(Charsets.UTF_8)
|
|
89
|
+
}
|
|
90
|
+
assertThat(decompressed).isEqualTo(bundleContent)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@Test
|
|
94
|
+
fun `packageBundle handles Hermes bytecode files`() {
|
|
95
|
+
// Create test Hermes bytecode file (simulated)
|
|
96
|
+
val hbcContent = ByteArray(100) { it.toByte() } // Fake bytecode
|
|
97
|
+
val bundleFile = File(tempDir, "index.android.hbc")
|
|
98
|
+
bundleFile.writeBytes(hbcContent)
|
|
99
|
+
|
|
100
|
+
val outputDir = File(tempDir, "output/assets")
|
|
101
|
+
val outputFile = File(outputDir, "index.android.hbc")
|
|
102
|
+
|
|
103
|
+
val task = createTestTask<AssetPackagingTask> {
|
|
104
|
+
it.bundleFile.set(bundleFile)
|
|
105
|
+
it.outputAssetsDir.set(outputDir)
|
|
106
|
+
it.bundleAssetName.set("index.android.hbc")
|
|
107
|
+
it.compressionEnabled.set(false)
|
|
108
|
+
it.variantName.set("release")
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
task.execute()
|
|
112
|
+
|
|
113
|
+
assertThat(outputFile).exists()
|
|
114
|
+
assertThat(outputFile.readBytes()).isEqualTo(hbcContent)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@Test
|
|
118
|
+
fun `packageDrawableAssets copies single drawable directory`() {
|
|
119
|
+
// Create drawable assets
|
|
120
|
+
val assetsDir = File(tempDir, "assets")
|
|
121
|
+
val drawableMdpi = File(assetsDir, "drawable-mdpi")
|
|
122
|
+
createTestFile(drawableMdpi, "icon.png", "fake png content")
|
|
123
|
+
|
|
124
|
+
val outputResDir = File(tempDir, "output/res")
|
|
125
|
+
val bundleFile = createTestFile(tempDir, "bundle.js", "fake")
|
|
126
|
+
|
|
127
|
+
val task = createTestTask<AssetPackagingTask> {
|
|
128
|
+
it.bundleFile.set(bundleFile)
|
|
129
|
+
it.assetsDir.set(assetsDir)
|
|
130
|
+
it.outputAssetsDir.set(File(tempDir, "output/assets"))
|
|
131
|
+
it.outputResDir.set(outputResDir)
|
|
132
|
+
it.bundleAssetName.set("index.android.bundle")
|
|
133
|
+
it.compressionEnabled.set(false)
|
|
134
|
+
it.variantName.set("release")
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
task.execute()
|
|
138
|
+
|
|
139
|
+
// Verify drawable was copied
|
|
140
|
+
val copiedDrawable = File(outputResDir, "drawable-mdpi/icon.png")
|
|
141
|
+
assertThat(copiedDrawable).exists()
|
|
142
|
+
assertThat(readFileContent(copiedDrawable)).isEqualTo("fake png content")
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@Test
|
|
146
|
+
fun `packageDrawableAssets copies multiple drawable densities`() {
|
|
147
|
+
// Create multiple drawable density directories
|
|
148
|
+
val assetsDir = File(tempDir, "assets")
|
|
149
|
+
val densities = listOf("mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi")
|
|
150
|
+
|
|
151
|
+
for (density in densities) {
|
|
152
|
+
val drawableDir = File(assetsDir, "drawable-$density")
|
|
153
|
+
createTestFile(drawableDir, "icon.png", "icon for $density")
|
|
154
|
+
createTestFile(drawableDir, "logo.png", "logo for $density")
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
val outputResDir = File(tempDir, "output/res")
|
|
158
|
+
val bundleFile = createTestFile(tempDir, "bundle.js", "fake")
|
|
159
|
+
|
|
160
|
+
val task = createTestTask<AssetPackagingTask> {
|
|
161
|
+
it.bundleFile.set(bundleFile)
|
|
162
|
+
it.assetsDir.set(assetsDir)
|
|
163
|
+
it.outputAssetsDir.set(File(tempDir, "output/assets"))
|
|
164
|
+
it.outputResDir.set(outputResDir)
|
|
165
|
+
it.bundleAssetName.set("index.android.bundle")
|
|
166
|
+
it.compressionEnabled.set(false)
|
|
167
|
+
it.variantName.set("release")
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
task.execute()
|
|
171
|
+
|
|
172
|
+
// Verify all densities were copied
|
|
173
|
+
for (density in densities) {
|
|
174
|
+
val iconFile = File(outputResDir, "drawable-$density/icon.png")
|
|
175
|
+
val logoFile = File(outputResDir, "drawable-$density/logo.png")
|
|
176
|
+
|
|
177
|
+
assertThat(iconFile).exists()
|
|
178
|
+
assertThat(logoFile).exists()
|
|
179
|
+
assertThat(readFileContent(iconFile)).isEqualTo("icon for $density")
|
|
180
|
+
assertThat(readFileContent(logoFile)).isEqualTo("logo for $density")
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@Test
|
|
185
|
+
fun `packageDrawableAssets handles nested subdirectories`() {
|
|
186
|
+
// Create drawable with nested structure
|
|
187
|
+
val assetsDir = File(tempDir, "assets")
|
|
188
|
+
val drawableXhdpi = File(assetsDir, "drawable-xhdpi")
|
|
189
|
+
createTestFile(File(drawableXhdpi, "icons"), "app_icon.png", "nested icon")
|
|
190
|
+
|
|
191
|
+
val outputResDir = File(tempDir, "output/res")
|
|
192
|
+
val bundleFile = createTestFile(tempDir, "bundle.js", "fake")
|
|
193
|
+
|
|
194
|
+
val task = createTestTask<AssetPackagingTask> {
|
|
195
|
+
it.bundleFile.set(bundleFile)
|
|
196
|
+
it.assetsDir.set(assetsDir)
|
|
197
|
+
it.outputAssetsDir.set(File(tempDir, "output/assets"))
|
|
198
|
+
it.outputResDir.set(outputResDir)
|
|
199
|
+
it.bundleAssetName.set("index.android.bundle")
|
|
200
|
+
it.compressionEnabled.set(false)
|
|
201
|
+
it.variantName.set("release")
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
task.execute()
|
|
205
|
+
|
|
206
|
+
// Verify nested file was copied with same structure
|
|
207
|
+
val copiedNestedFile = File(outputResDir, "drawable-xhdpi/icons/app_icon.png")
|
|
208
|
+
assertThat(copiedNestedFile).exists()
|
|
209
|
+
assertThat(readFileContent(copiedNestedFile)).isEqualTo("nested icon")
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@Test
|
|
213
|
+
fun `packageDrawableAssets handles no assets directory`() {
|
|
214
|
+
// Don't set assetsDir (optional)
|
|
215
|
+
val outputAssetsDir = File(tempDir, "output/assets")
|
|
216
|
+
val bundleFile = createTestFile(tempDir, "bundle.js", "fake")
|
|
217
|
+
|
|
218
|
+
val task = createTestTask<AssetPackagingTask> {
|
|
219
|
+
it.bundleFile.set(bundleFile)
|
|
220
|
+
// assetsDir not set
|
|
221
|
+
it.outputAssetsDir.set(outputAssetsDir)
|
|
222
|
+
// outputResDir not set
|
|
223
|
+
it.bundleAssetName.set("index.android.bundle")
|
|
224
|
+
it.compressionEnabled.set(false)
|
|
225
|
+
it.variantName.set("release")
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Should not throw
|
|
229
|
+
task.execute()
|
|
230
|
+
|
|
231
|
+
// Only bundle should be packaged
|
|
232
|
+
assertThat(File(outputAssetsDir, "index.android.bundle")).exists()
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@Test
|
|
236
|
+
fun `packageDrawableAssets handles empty assets directory`() {
|
|
237
|
+
// Create empty assets directory
|
|
238
|
+
val assetsDir = File(tempDir, "assets")
|
|
239
|
+
assetsDir.mkdirs()
|
|
240
|
+
|
|
241
|
+
val outputResDir = File(tempDir, "output/res")
|
|
242
|
+
val bundleFile = createTestFile(tempDir, "bundle.js", "fake")
|
|
243
|
+
|
|
244
|
+
val task = createTestTask<AssetPackagingTask> {
|
|
245
|
+
it.bundleFile.set(bundleFile)
|
|
246
|
+
it.assetsDir.set(assetsDir)
|
|
247
|
+
it.outputAssetsDir.set(File(tempDir, "output/assets"))
|
|
248
|
+
it.outputResDir.set(outputResDir)
|
|
249
|
+
it.bundleAssetName.set("index.android.bundle")
|
|
250
|
+
it.compressionEnabled.set(false)
|
|
251
|
+
it.variantName.set("release")
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Should not throw
|
|
255
|
+
task.execute()
|
|
256
|
+
|
|
257
|
+
// res directory may be created but empty
|
|
258
|
+
if (outputResDir.exists()) {
|
|
259
|
+
assertThat(outputResDir.listFiles()).isEmpty()
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
@Test
|
|
264
|
+
fun `task packages both bundle and drawables together`() {
|
|
265
|
+
// Create bundle and drawables
|
|
266
|
+
val bundleContent = "// React Native bundle"
|
|
267
|
+
val bundleFile = createTestFile(tempDir, "index.android.bundle", bundleContent)
|
|
268
|
+
|
|
269
|
+
val assetsDir = File(tempDir, "assets")
|
|
270
|
+
createTestFile(File(assetsDir, "drawable-mdpi"), "icon.png", "mdpi icon")
|
|
271
|
+
createTestFile(File(assetsDir, "drawable-hdpi"), "icon.png", "hdpi icon")
|
|
272
|
+
|
|
273
|
+
val outputAssetsDir = File(tempDir, "output/assets")
|
|
274
|
+
val outputResDir = File(tempDir, "output/res")
|
|
275
|
+
|
|
276
|
+
val task = createTestTask<AssetPackagingTask> {
|
|
277
|
+
it.bundleFile.set(bundleFile)
|
|
278
|
+
it.assetsDir.set(assetsDir)
|
|
279
|
+
it.outputAssetsDir.set(outputAssetsDir)
|
|
280
|
+
it.outputResDir.set(outputResDir)
|
|
281
|
+
it.bundleAssetName.set("index.android.bundle")
|
|
282
|
+
it.compressionEnabled.set(false)
|
|
283
|
+
it.variantName.set("release")
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
task.execute()
|
|
287
|
+
|
|
288
|
+
// Verify both bundle and drawables were packaged
|
|
289
|
+
assertThat(File(outputAssetsDir, "index.android.bundle")).exists()
|
|
290
|
+
assertThat(File(outputResDir, "drawable-mdpi/icon.png")).exists()
|
|
291
|
+
assertThat(File(outputResDir, "drawable-hdpi/icon.png")).exists()
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
@Test
|
|
295
|
+
fun `gzip compression actually reduces file size for text bundles`() {
|
|
296
|
+
// Create large repetitive text bundle (compresses well)
|
|
297
|
+
val bundleContent = "var x = 'test';\n".repeat(1000)
|
|
298
|
+
val bundleFile = createTestFile(tempDir, "large.bundle", bundleContent)
|
|
299
|
+
|
|
300
|
+
val outputDir = File(tempDir, "output/assets")
|
|
301
|
+
|
|
302
|
+
val task = createTestTask<AssetPackagingTask> {
|
|
303
|
+
it.bundleFile.set(bundleFile)
|
|
304
|
+
it.outputAssetsDir.set(outputDir)
|
|
305
|
+
it.bundleAssetName.set("large.bundle")
|
|
306
|
+
it.compressionEnabled.set(true)
|
|
307
|
+
it.variantName.set("release")
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
task.execute()
|
|
311
|
+
|
|
312
|
+
val compressedFile = File(outputDir, "large.bundle.gz")
|
|
313
|
+
assertThat(compressedFile).exists()
|
|
314
|
+
|
|
315
|
+
// Compressed file should be smaller than original
|
|
316
|
+
assertThat(compressedFile.length()).isLessThan(bundleFile.length())
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
package run.granite.gradle.tasks
|
|
2
|
+
|
|
3
|
+
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
+
import org.junit.jupiter.api.Test
|
|
5
|
+
import run.granite.gradle.fixtures.NativeModuleFixtures
|
|
6
|
+
import run.granite.gradle.generators.CppAutolinkingGenerator
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Unit tests for AutolinkingTask.
|
|
10
|
+
*
|
|
11
|
+
* Tests PackageList.kt generation logic and configuration.
|
|
12
|
+
*
|
|
13
|
+
* Note: Full integration testing (actual CLI execution) requires Node.js and
|
|
14
|
+
* React Native CLI, so is done separately. These tests focus on task structure validation.
|
|
15
|
+
*/
|
|
16
|
+
class AutolinkingTaskTest {
|
|
17
|
+
|
|
18
|
+
@Test
|
|
19
|
+
fun `task is cacheable`() {
|
|
20
|
+
val annotations = AutolinkingTask::class.annotations
|
|
21
|
+
assertThat(annotations)
|
|
22
|
+
.anyMatch { it.annotationClass.simpleName == "CacheableTask" }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Test
|
|
26
|
+
fun `task has all required properties`() {
|
|
27
|
+
val propertyNames = AutolinkingTask::class.members
|
|
28
|
+
.filter {
|
|
29
|
+
it.name.endsWith("File") || it.name.endsWith("Dir") ||
|
|
30
|
+
it.name == "packageName"
|
|
31
|
+
}
|
|
32
|
+
.map { it.name }
|
|
33
|
+
.toSet()
|
|
34
|
+
|
|
35
|
+
assertThat(propertyNames).contains("reactNativeDir")
|
|
36
|
+
assertThat(propertyNames).contains("nodeModulesDir")
|
|
37
|
+
assertThat(propertyNames).contains("projectDir")
|
|
38
|
+
assertThat(propertyNames).contains("outputDir")
|
|
39
|
+
assertThat(propertyNames).contains("packageListFile")
|
|
40
|
+
assertThat(propertyNames).contains("packageName")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Test
|
|
44
|
+
fun `task has execute method`() {
|
|
45
|
+
val executeMethods = AutolinkingTask::class.members
|
|
46
|
+
.filter { it.name == "execute" }
|
|
47
|
+
|
|
48
|
+
assertThat(executeMethods).isNotEmpty
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun `task class is abstract`() {
|
|
53
|
+
assertThat(AutolinkingTask::class.isAbstract).isTrue
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Test
|
|
57
|
+
fun `autolinking filters modules by includesGeneratedCode flag`() {
|
|
58
|
+
// Create mixed modules: one standard, one with custom codegen
|
|
59
|
+
val modules = NativeModuleFixtures.createMixedModules()
|
|
60
|
+
|
|
61
|
+
// Simulate the filtering logic from AutolinkingTask
|
|
62
|
+
val filteredModules = modules.filter { !it.includesGeneratedCode }
|
|
63
|
+
|
|
64
|
+
// Standard module should be included
|
|
65
|
+
assertThat(filteredModules).anyMatch { it.name == "react-native-example" }
|
|
66
|
+
|
|
67
|
+
// Custom codegen module should be excluded
|
|
68
|
+
assertThat(filteredModules).noneMatch { it.name == "custom-codegen-module" }
|
|
69
|
+
|
|
70
|
+
assertThat(filteredModules).hasSize(1)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@Test
|
|
74
|
+
fun `generated autolinking excludes modules with includesGeneratedCode flag`() {
|
|
75
|
+
// Filtering by includesGeneratedCode is the caller's responsibility (AutolinkingTask),
|
|
76
|
+
// not the generator's. Test that pre-filtered input produces correct output.
|
|
77
|
+
val allModules = NativeModuleFixtures.createMixedModules()
|
|
78
|
+
val filteredModules = allModules.filter { !it.includesGeneratedCode }
|
|
79
|
+
|
|
80
|
+
val generatedCpp = CppAutolinkingGenerator.generate(filteredModules)
|
|
81
|
+
|
|
82
|
+
// Custom codegen module should be excluded after filtering
|
|
83
|
+
assertThat(generatedCpp).doesNotContain("CustomCodegenSpec_ModuleProvider")
|
|
84
|
+
assertThat(generatedCpp).doesNotContain("custom_codegen_module")
|
|
85
|
+
|
|
86
|
+
// Standard module should still be included
|
|
87
|
+
assertThat(generatedCpp).contains("ExampleSpec_ModuleProvider")
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
package run.granite.gradle.tasks
|
|
2
|
+
|
|
3
|
+
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
+
import org.junit.jupiter.api.Test
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Unit tests for BundleTask.
|
|
8
|
+
*
|
|
9
|
+
* Tests JavaScript bundling and Hermes compilation logic structure.
|
|
10
|
+
*
|
|
11
|
+
* Note: Full integration testing (actual Metro/Hermes execution) requires
|
|
12
|
+
* Node.js, React Native CLI, and Hermes compiler, so is done separately.
|
|
13
|
+
* These tests focus on task structure validation.
|
|
14
|
+
*/
|
|
15
|
+
class BundleTaskTest {
|
|
16
|
+
|
|
17
|
+
@Test
|
|
18
|
+
fun `task is cacheable`() {
|
|
19
|
+
val annotations = BundleTask::class.annotations
|
|
20
|
+
assertThat(annotations)
|
|
21
|
+
.anyMatch { it.annotationClass.simpleName == "CacheableTask" }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Test
|
|
25
|
+
fun `task has all required properties`() {
|
|
26
|
+
val propertyNames = BundleTask::class.members
|
|
27
|
+
.filter {
|
|
28
|
+
it.name.endsWith("File") || it.name.endsWith("Dir") ||
|
|
29
|
+
it.name == "devMode" ||
|
|
30
|
+
it.name == "bundleAssetName" || it.name == "variantName"
|
|
31
|
+
}
|
|
32
|
+
.map { it.name }
|
|
33
|
+
.toSet()
|
|
34
|
+
|
|
35
|
+
assertThat(propertyNames).contains("entryFile")
|
|
36
|
+
assertThat(propertyNames).contains("reactNativeDir")
|
|
37
|
+
assertThat(propertyNames).contains("nodeModulesDir")
|
|
38
|
+
assertThat(propertyNames).contains("projectDir")
|
|
39
|
+
assertThat(propertyNames).contains("bundleFile")
|
|
40
|
+
assertThat(propertyNames).contains("sourceMapFile")
|
|
41
|
+
assertThat(propertyNames).contains("bundleAssetName")
|
|
42
|
+
assertThat(propertyNames).contains("devMode")
|
|
43
|
+
assertThat(propertyNames).contains("variantName")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Test
|
|
47
|
+
fun `task has execute method`() {
|
|
48
|
+
val executeMethods = BundleTask::class.members
|
|
49
|
+
.filter { it.name == "execute" }
|
|
50
|
+
|
|
51
|
+
assertThat(executeMethods).isNotEmpty
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Test
|
|
55
|
+
fun `task class is abstract`() {
|
|
56
|
+
assertThat(BundleTask::class.isAbstract).isTrue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@Test
|
|
60
|
+
fun `task has private helper methods`() {
|
|
61
|
+
val privateMembers = BundleTask::class.members
|
|
62
|
+
.map { it.name }
|
|
63
|
+
.toSet()
|
|
64
|
+
|
|
65
|
+
// Execute method orchestrates the bundling process
|
|
66
|
+
assertThat(privateMembers).contains("execute")
|
|
67
|
+
}
|
|
68
|
+
}
|