reflex-packager 0.2.0 → 0.3.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/package.yml +310 -0
  3. data/.github/workflows/release-gem.yml +2 -49
  4. data/.github/workflows/tag.yml +11 -28
  5. data/.github/workflows/test.yml +16 -37
  6. data/.gitignore +2 -0
  7. data/ChangeLog.md +28 -0
  8. data/Gemfile.lock +26 -0
  9. data/README.md +232 -27
  10. data/Rakefile +21 -0
  11. data/VERSION +1 -1
  12. data/bin/reflex +2 -32
  13. data/examples/hello/icon.png +0 -0
  14. data/examples/hello/main.rb +18 -0
  15. data/examples/hello/reflex.yml +3 -0
  16. data/lib/reflex/packager/app_store_connect.rb +162 -0
  17. data/lib/reflex/packager/cli.rb +42 -9
  18. data/lib/reflex/packager/config.rb +233 -36
  19. data/lib/reflex/packager/data_compiler.rb +59 -0
  20. data/lib/reflex/packager/data_file.rb +183 -0
  21. data/lib/reflex/packager/data_loader.rb +127 -0
  22. data/lib/reflex/packager/gems.rb +282 -0
  23. data/lib/reflex/packager/library.rb +120 -0
  24. data/lib/reflex/packager/macos.rb +592 -94
  25. data/lib/reflex/packager/platform.rb +289 -10
  26. data/lib/reflex/packager/profile.rb +38 -26
  27. data/lib/reflex/packager/profiles/reflex.rb +43 -0
  28. data/lib/reflex/packager/rake.rb +115 -0
  29. data/lib/reflex/packager/stores/app_store.rb +60 -0
  30. data/lib/reflex/packager/stores/itchio.rb +53 -0
  31. data/lib/reflex/packager/templates/macos/AppStore.entitlements.erb +8 -0
  32. data/lib/reflex/packager/templates/macos/AppStoreExportOptions.plist.erb +14 -0
  33. data/lib/reflex/packager/templates/macos/ExportOptions.plist.erb +20 -0
  34. data/lib/reflex/packager/templates/macos/InfoPlist.strings.erb +5 -0
  35. data/lib/reflex/packager/templates/macos/boot.rb.erb +29 -0
  36. data/lib/reflex/packager/templates/macos/compile.rb.erb +7 -0
  37. data/lib/reflex/packager/templates/macos/main.mm.erb +10 -7
  38. data/lib/reflex/packager/templates/macos/project.yml.erb +60 -3
  39. data/lib/reflex/packager/templates/windows/app.manifest.erb +25 -0
  40. data/lib/reflex/packager/templates/windows/app.rc.erb +38 -0
  41. data/lib/reflex/packager/templates/windows/boot.rb.erb +32 -0
  42. data/lib/reflex/packager/templates/windows/main.cpp.erb +47 -0
  43. data/lib/reflex/packager/templates/windows/runtime.manifest.erb +7 -0
  44. data/lib/reflex/packager/windows.rb +370 -0
  45. data/lib/reflex/packager.rb +18 -1
  46. data/reflex-packager.gemspec +8 -7
  47. data/test/helper.rb +50 -5
  48. data/test/test_packager_app_store_connect.rb +108 -0
  49. data/test/test_packager_cli.rb +38 -3
  50. data/test/test_packager_config.rb +166 -27
  51. data/test/test_packager_data_file.rb +96 -0
  52. data/test/test_packager_data_loader.rb +114 -0
  53. data/test/test_packager_library.rb +81 -0
  54. data/test/test_packager_macos.rb +714 -86
  55. data/test/test_packager_platform.rb +16 -0
  56. data/test/test_packager_rake.rb +165 -0
  57. data/test/test_packager_stores_app_store.rb +66 -0
  58. data/test/test_packager_stores_itchio.rb +38 -0
  59. data/test/test_packager_windows.rb +685 -0
  60. metadata +72 -15
  61. data/.github/workflows/utils.rb +0 -127
  62. data/lib/reflex/packager/templates/macos/Podfile.erb +0 -30
@@ -10,13 +10,15 @@ class TestPackagerConfig < Test::Unit::TestCase
10
10
 
11
11
  MAIN = File.basename(__FILE__)
12
12
 
13
- def config(dir: __dir__, main: nil, parent: nil, stderr: nil, **hash)
13
+ def config(dir: __dir__, main: nil, parent: nil, stderr: nil, env: {}, **hash)
14
14
  main ||= MAIN
15
- Config.new TEST_PROFILE, dir, {main: main, **hash}, stderr: stderr
15
+ with_env(ENV_TO_SIGN.merge env) do
16
+ Config.new TEST_PROFILE, dir, {main: main, **hash}, stderr: stderr
17
+ end
16
18
  end
17
19
 
18
20
  def load_config(dir, path = nil)
19
- Config.load TEST_PROFILE, dir, path
21
+ with_env(ENV_TO_SIGN) {Config.load TEST_PROFILE, dir, path}
20
22
  end
21
23
 
22
24
  def stderr()
@@ -73,11 +75,11 @@ class TestPackagerConfig < Test::Unit::TestCase
73
75
  main: app.rb
74
76
  icon: icon.png
75
77
  files: ['*.rb', data]
76
- pods:
77
- reflex: {git: https://example.com/reflex, tag: v1}
78
78
  macos:
79
79
  deployment_target: '12.0'
80
80
  archs: [arm64, x86_64]
81
+ cruby: 4.0.600
82
+ category: public.app-category.games
81
83
  codesign: {identity: Developer ID, team_id: ABCDE12345}
82
84
  YAML
83
85
  touch 'icon.png'
@@ -90,10 +92,11 @@ class TestPackagerConfig < Test::Unit::TestCase
90
92
  assert_equal 'app.rb', c.main
91
93
  assert_equal 'icon.png', c.icon
92
94
  assert_equal %w[*.rb data], c.files
93
- assert_equal %w[1.rb app.rb data], c.app_files
94
- assert_equal({git: 'https://example.com/reflex', tag: 'v1'}, c.pods[:reflex])
95
+ assert_equal %w[1.rb app.rb data/2.json], c.app_files
95
96
  assert_equal '12.0', c.macos.deployment_target
96
97
  assert_equal %w[arm64 x86_64], c.macos.archs
98
+ assert_equal '4.0.600', c.macos.cruby
99
+ assert_equal 'public.app-category.games', c.macos.category
97
100
  assert_equal 'Developer ID', c.macos.codesign_identity
98
101
  assert_equal 'ABCDE12345', c.macos.codesign_team_id
99
102
  end
@@ -103,6 +106,10 @@ class TestPackagerConfig < Test::Unit::TestCase
103
106
  assert_equal 'test', config() .name
104
107
  assert_equal '1', config(name: 1) .name
105
108
  assert_equal 'test', config(name: nil).name
109
+ assert_raise(RP::Error) {config name: '', bundle_id: 'a.b'}
110
+ assert_raise(RP::Error) {config name: ' ', bundle_id: 'a.b'}
111
+ assert_raise(RP::Error) {config name: 'A/B', bundle_id: 'a.b'}
112
+ assert_raise(RP::Error) {config name: 'A:B', bundle_id: 'a.b'}
106
113
  end
107
114
 
108
115
  def test_bundle_id()
@@ -121,7 +128,63 @@ class TestPackagerConfig < Test::Unit::TestCase
121
128
  assert_equal '1', config(version: '1') .version
122
129
  assert_equal '2.3', config(version: '2.3').version
123
130
  assert_equal '0.1.0', config(version: nil) .version
131
+ assert_equal '1', config(version: 1) .version
124
132
  assert_raise(RP::Error) {config version: '4.'}
133
+ assert_raise(RP::Error) {config version: '2.3beta'}
134
+ assert_raise(RP::Error) {config version: 1.10} # read as 1.1
135
+
136
+ # the VERSION file of the app, which the config comes before
137
+ tmpdir do |dir|
138
+ assert_equal '0.1.0', load_config(dir).version
139
+ File.write 'VERSION', "1.2.3\n"
140
+ assert_equal '1.2.3', load_config(dir).version
141
+ File.binwrite 'VERSION', "\xEF\xBB\xBF1.2.4\r\n" # with a BOM
142
+ assert_equal '1.2.4', load_config(dir).version
143
+ File.write 'reflex.yaml', "version: '4.5'"
144
+ assert_equal '4.5', load_config(dir).version
145
+ end
146
+ tmpdir do |dir|
147
+ File.write 'VERSION', '1.2.3beta'
148
+ error = assert_raise(RP::Error) {load_config dir}
149
+ assert_include error.message, 'VERSION file'
150
+ File.write 'reflex.yaml', "version: '4.5'"
151
+ assert_equal '4.5', load_config(dir).version # the file not read
152
+ end
153
+ assert_not_include assert_raise(RP::Error) {config version: '2.3beta'}.message, 'VERSION file'
154
+ end
155
+
156
+ def test_copyright()
157
+ assert_nil config() .copyright
158
+ assert_equal '© 2026 Me', config(copyright: '© 2026 Me').copyright
159
+ assert_equal '2026', config(copyright: 2026) .copyright
160
+ end
161
+
162
+ def test_localizations()
163
+ assert_equal({'en' => {name: 'test', copyright: nil}}, config.localizations)
164
+
165
+ l10n = config(copyright: 'C', localizations: {ja: {name: 'Tesuto'}, 'zh-Hans': nil}).localizations
166
+ assert_equal %w[en ja zh-Hans], l10n.keys
167
+ assert_equal({name: 'test', copyright: 'C'}, l10n['en'])
168
+ assert_equal({name: 'Tesuto', copyright: 'C'}, l10n['ja'])
169
+ assert_equal({name: 'test', copyright: 'C'}, l10n['zh-Hans'])
170
+ assert_equal({name: '1', copyright: nil}, config(localizations: {ja: {name: 1}}).localizations['ja'])
171
+
172
+ assert_raise(RP::Error) {config localizations: 1}
173
+ assert_raise(RP::Error) {config localizations: {ja: 1}}
174
+ assert_raise(RP::Error) {config localizations: {ja: {name: {}}}}
175
+ assert_raise(RP::Error) {config localizations: {en: {name: 'Test'}}} # the values out of it
176
+ assert_raise(RP::Error) {config localizations: {Japanese: {name: 'Tesuto'}}}
177
+ end
178
+
179
+ def test_display_and_build_versions()
180
+ [
181
+ [{version: '1.2.3'}, '1.2.3', '1.2.3'],
182
+ [{version: '1.2.3.4'}, '1.2.3', '1.2.3.4'],
183
+ [{version: '2.3'}, '2.3', '2.3'],
184
+ ].each do |hash, display, build|
185
+ c = config(**hash)
186
+ assert_equal [display, build], [c.display_version, c.build_version], hash.inspect
187
+ end
125
188
  end
126
189
 
127
190
  def test_main()
@@ -142,12 +205,17 @@ class TestPackagerConfig < Test::Unit::TestCase
142
205
  assert_equal %w[a b], config(files: %w[a b]) .files
143
206
  end
144
207
 
145
- def test_pods()
146
- assert_equal({cruby: {}, reflex: {}}, config() .pods)
147
- assert_equal({tag: '6'}, config(pods: {cruby: {tag: '6'}}).pods[:cruby])
148
- assert_equal({branch: '7'}, config(pods: {cruby: {branch: '7'}}).pods[:cruby])
149
- assert_equal({git: '8'}, config(pods: {cruby: {git: '8'}}).pods[:cruby])
150
- assert_equal({path: '9'}, config(pods: {cruby: {path: '9'}}).pods[:cruby])
208
+ def test_excludes()
209
+ assert_nil config() .excludes
210
+ assert_equal %w[helper.rb], config(excludes: 'helper.rb').excludes
211
+ assert_equal %w[a b], config(excludes: %w[a b]) .excludes
212
+ assert_raise(RP::Error) {config excludes: {a: 1}}
213
+ assert_raise(RP::Error) {config excludes: [{a: 1}]}
214
+ assert_raise(RP::Error) {config excludes: [%w[a]]}
215
+ assert_raise(RP::Error) {config excludes: [nil]}
216
+ assert_raise(RP::Error) {config excludes: ['']}
217
+ assert_raise(RP::Error) {config excludes: MAIN}
218
+ assert_raise(RP::Error) {config excludes: "./#{MAIN}"}
151
219
  end
152
220
 
153
221
  def test_app_files()
@@ -162,12 +230,32 @@ class TestPackagerConfig < Test::Unit::TestCase
162
230
  end
163
231
 
164
232
  tmpdir yaml: "files: ['*']" do |dir|
165
- %w[build dist].each {Dir.mkdir _1}
166
- touch 'build/1.rb'
233
+ %w[.build dist].each {Dir.mkdir _1}
234
+ touch '.build/1.rb'
167
235
  touch 'dist/2.rb'
168
236
  assert_equal %w[main.rb], load_config(dir).app_files
169
237
  end
170
238
 
239
+ tmpdir yaml: "files: ['lib/**/*', res]\nexcludes: [lib/a/debug.rb, 'res/*.wav']" do |dir|
240
+ %w[lib/a/b.rb lib/a/debug.rb res/c.png res/d.wav].each {touch _1}
241
+ assert_equal %w[lib/a/b.rb main.rb res/c.png], load_config(dir).app_files
242
+ end
243
+
244
+ tmpdir yaml: "files: [lib, ./res]\nexcludes: [./lib/a/debug.rb, res/d.wav]" do |dir|
245
+ %w[lib/a/b.rb lib/a/debug.rb res/c.png res/d.wav].each {touch _1}
246
+ assert_equal %w[lib/a/b.rb main.rb res/c.png], load_config(dir).app_files
247
+ end
248
+
249
+ tmpdir yaml: "files: ['*']" do |dir|
250
+ touch 'a[1]/b.png'
251
+ assert_equal %w[a[1]/b.png main.rb], load_config(dir).app_files
252
+ end
253
+
254
+ tmpdir yaml: "files: [res, res/.b]" do |dir|
255
+ %w[res/.a res/.b res/.c/d res/e].each {touch _1}
256
+ assert_equal %w[main.rb res/.b res/e], load_config(dir).app_files
257
+ end
258
+
171
259
  tmpdir yaml_path: 'a.yaml', yaml: <<~YAML, main_path: 'b.rb' do |dir|
172
260
  main: b.rb
173
261
  files: '*'
@@ -188,15 +276,35 @@ class TestPackagerConfig < Test::Unit::TestCase
188
276
  end
189
277
 
190
278
  def test_macos_archs()
191
- assert_equal ['arm64'], config() .macos.archs
192
- assert_equal ['arm64'], config(macos: {}) .macos.archs
193
- assert_equal ['1'], config(macos: {archs: 1}) .macos.archs
194
- assert_equal ['2', '3'], config(macos: {archs: [2, 3]}).macos.archs
195
- assert_equal ['arm64'], config(macos: {archs: nil}) .macos.archs
279
+ assert_equal %w[arm64 x86_64], config() .macos.archs
280
+ assert_equal %w[arm64 x86_64], config(macos: {}) .macos.archs
281
+ assert_equal ['arm64'], config(macos: {archs: 'arm64'}).macos.archs
282
+ assert_equal ['1'], config(macos: {archs: 1}) .macos.archs
283
+ assert_equal ['2', '3'], config(macos: {archs: [2, 3]}) .macos.archs
284
+ assert_equal %w[arm64 x86_64], config(macos: {archs: nil}) .macos.archs
196
285
  assert_raise(RP::Error) {config(macos: {archs: []})}
197
286
  assert_raise(RP::Error) {config(macos: {archs: {}})}
198
287
  end
199
288
 
289
+ def test_macos_cruby()
290
+ assert_nil config() .macos.cruby
291
+ assert_nil config(macos: {}) .macos.cruby
292
+ assert_equal '4.0.600', config(macos: {cruby: '4.0.600'}) .macos.cruby
293
+ assert_equal '../cruby', config(macos: {cruby: '../cruby'}) .macos.cruby
294
+ assert_raise(RP::Error) {config(macos: {cruby: {}})}
295
+ end
296
+
297
+ def test_macos_category()
298
+ assert_nil config() .macos.category
299
+ assert_nil config(macos: {}) .macos.category
300
+ assert_nil config(macos: {category: nil}) .macos.category
301
+ assert_equal 'public.app-category.sports-games',
302
+ config(macos: {category: 'public.app-category.sports-games'}).macos.category
303
+ assert_raise(RP::Error) {config(macos: {category: 'games'})}
304
+ assert_raise(RP::Error) {config(macos: {category: 'public.app-category.Games'})}
305
+ assert_raise(RP::Error) {config(macos: {category: {}})}
306
+ end
307
+
200
308
  def test_macos_codesign_identity()
201
309
  assert_equal '-', config() .macos.codesign_identity
202
310
  assert_equal '-', config(macos: {}) .macos.codesign_identity
@@ -206,6 +314,9 @@ class TestPackagerConfig < Test::Unit::TestCase
206
314
  assert_equal '-', config(macos: {codesign: {identity: nil}}).macos.codesign_identity
207
315
  assert_raise(RP::Error) {config(macos: {codesign: 2})}
208
316
  assert_raise(RP::Error) {config(macos: {codesign: {identity: {}}})}
317
+
318
+ assert_equal 'ID', config(macos: {codesign: {identity: 1}}, env: {'APPLE_CODESIGN_IDENTITY' => 'ID'}).macos.codesign_identity
319
+ assert_equal '1', config(macos: {codesign: {identity: 1}}, env: {'APPLE_CODESIGN_IDENTITY' => ''}) .macos.codesign_identity
209
320
  end
210
321
 
211
322
  def test_macos_codesign_team_id()
@@ -217,24 +328,52 @@ class TestPackagerConfig < Test::Unit::TestCase
217
328
  assert_nil config(macos: {codesign: {team_id: nil}}).macos.codesign_team_id
218
329
  assert_raise(RP::Error) {config(macos: {codesign: 2})}
219
330
  assert_raise(RP::Error) {config(macos: {codesign: {team_id: {}}})}
331
+
332
+ assert_equal 'ID', config(macos: {codesign: {team_id: 1}}, env: {'APPLE_TEAM_ID' => 'ID'}).macos.codesign_team_id
333
+ assert_equal '1', config(macos: {codesign: {team_id: 1}}, env: {'APPLE_TEAM_ID' => ''}) .macos.codesign_team_id
334
+ end
335
+
336
+ def test_windows_console()
337
+ assert_equal false, config() .windows.console?
338
+ assert_equal false, config(windows: {}) .windows.console?
339
+ assert_equal false, config(windows: nil) .windows.console?
340
+ assert_equal false, config(windows: {console: nil}) .windows.console?
341
+ assert_equal true, config(windows: {console: true}) .windows.console?
342
+ assert_equal false, config(windows: {console: false}).windows.console?
343
+ assert_raise(RP::Error) {config(windows: 1)}
344
+ assert_raise(RP::Error) {config(windows: {console: 'yes'})}
345
+ assert_raise(RP::Error) {config(windows: {console: {}})}
346
+ end
347
+
348
+ def test_upload_itchio_project()
349
+ assert_nil config() .upload.itchio_project
350
+ assert_nil config(upload: {}) .upload.itchio_project
351
+ assert_nil config(upload: {itchio: {}}) .upload.itchio_project
352
+ assert_equal 'a/b-c_1', config(upload: {itchio: {project: 'a/b-c_1'}}).upload.itchio_project
353
+ assert_raise(RP::Error) {config(upload: {itchio: 'a/b'})}
354
+ assert_raise(RP::Error) {config(upload: {itchio: {project: 'a'}})}
355
+ assert_raise(RP::Error) {config(upload: {itchio: {project: 'a/b/c'}})}
356
+ assert_raise(RP::Error) {config(upload: {itchio: {project: 'A/b'}})} # lower case only
220
357
  end
221
358
 
222
359
  def test_warn_unknown_key()
223
360
  e = stderr
224
361
 
225
- config stderr: e, k1: 1
226
- assert_equal "unknown key '/k1'", e.shift
362
+ config stderr: e, k1: 1
363
+ assert_equal "unknown key '/k1'", e.shift
364
+
365
+ config stderr: e, macos: {k2: 2}
366
+ assert_equal "unknown key '/macos/k2'", e.shift
227
367
 
228
- config stderr: e, macos: {k2: 2}
229
- assert_equal "unknown key '/macos/k2'", e.shift
368
+ config stderr: e, macos: {codesign: {k3: 3}}
369
+ assert_equal "unknown key '/macos/codesign/k3'", e.shift
230
370
 
231
- config stderr: e, macos: {codesign: {k3: 3}}
232
- assert_equal "unknown key '/macos/codesign/k3'", e.shift
371
+ config stderr: e, localizations: {ja: {k4: 4}}
372
+ assert_equal "unknown key '/localizations/ja/k4'", e.shift
233
373
  end
234
374
 
235
375
  def test_invalid_value_type()
236
376
  assert_raise(RP::Error) {config macos: 'arm64'}
237
- assert_raise(RP::Error) {config pods: ['cruby']}
238
377
  end
239
378
 
240
379
  end# TestPackagerConfig
@@ -0,0 +1,96 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestPackagerDataFile < Test::Unit::TestCase
5
+
6
+ RP = Reflex::Packager
7
+ DataFile = RP::DataFile
8
+
9
+ def data_file(files, **options, &block)
10
+ Dir.mktmpdir do |dir|
11
+ path = File.join dir, 'data.bin'
12
+ DataFile.write path, files, **options
13
+ block.call path
14
+ end
15
+ end
16
+
17
+ def test_write_and_read()
18
+ files = {
19
+ 'main.rb' => 'puts :hello',
20
+ 'data/日本語.txt' => 'テキスト',
21
+ 'data/bytes.bin' => (0..255).map(&:chr).join.b,
22
+ 'data/empty.txt' => ''
23
+ }
24
+ data_file files do |path|
25
+ DataFile.open path do |data|
26
+ assert_equal files.keys, data.names
27
+ files.each do |name, bytes|
28
+ assert_equal bytes.b, data.read(name), name
29
+ assert_equal bytes.bytesize, data.size(name)
30
+ assert_include data.names, name
31
+ end
32
+ assert_nil data.read('none.rb')
33
+ assert_nil data.size('none.rb')
34
+ assert_false data.include?('none.rb')
35
+ assert_true data.include?('main.rb')
36
+ end
37
+ raw = File.binread path
38
+ assert_equal 'RXDF', raw[0, 4] # the header as it is
39
+ assert_not_include raw, 'puts :hello'
40
+ assert_not_include raw, 'main.rb'
41
+ end
42
+
43
+ # a random seed for each
44
+ raws = 2.times.map {data_file(files) {File.binread _1}}
45
+ assert_not_equal raws[0], raws[1]
46
+ data_file(files, seed: 1) {|path| DataFile.open(path) {assert_equal 'puts :hello', _1.read('main.rb')}}
47
+
48
+ data_file({}) {|path| DataFile.open(path) {assert_empty _1.names}}
49
+
50
+ assert_raise(ArgumentError) {data_file("a\tb" => '') {}}
51
+ assert_raise(ArgumentError) {data_file("a\nb" => '') {}}
52
+ assert_raise(ArgumentError) {data_file('../a' => '') {}}
53
+ assert_raise(ArgumentError) {data_file('a/../../b' => '') {}}
54
+ assert_raise(ArgumentError) {data_file('/a' => '') {}}
55
+
56
+ Dir.mktmpdir do |dir|
57
+ path = File.join dir, 'data.bin'
58
+ assert_raise(ArgumentError) {DataFile.write path, files, seed: 0}
59
+ assert_false File.exist?(path) # not written at all
60
+ end
61
+ end
62
+
63
+ def test_encode()
64
+ bytes = (0..255).map(&:chr).join.b
65
+ encoded = DataFile.encode bytes, 0x7f4a7c15
66
+ assert_equal bytes, encoded.bytes.sort.pack('C*') # each byte once
67
+ assert_equal bytes, DataFile.decode(encoded, 0x7f4a7c15)
68
+ assert_not_equal bytes, encoded
69
+ assert_not_equal bytes.reverse, encoded # not inverted
70
+ assert_not_equal encoded, DataFile.encode(bytes, 0x7f4a7c16)
71
+ assert_equal '89e68061414636a9', encoded.unpack1('H16') # the same on any Ruby
72
+
73
+ assert_raise(ArgumentError) {DataFile.encode bytes, 0}
74
+ assert_raise(ArgumentError) {DataFile.encode bytes, 0x100000000}
75
+ end
76
+
77
+ def test_not_a_data_file()
78
+ Dir.mktmpdir do |dir|
79
+ path = File.join dir, 'data.bin'
80
+ File.write path, 'not a data file'
81
+ assert_raise(RuntimeError) {DataFile.new path}
82
+ File.write path, ''
83
+ assert_raise(RuntimeError) {DataFile.new path}
84
+ end
85
+ end
86
+
87
+ def test_requires_nothing()
88
+ # a package carries the file to read its data file
89
+ path = File.expand_path '../lib/reflex/packager/data_file.rb', __dir__
90
+ out = IO.popen(
91
+ [RbConfig.ruby, '--disable-gems', '-e', "load #{path.dump}; p Reflex::Packager::DataFile::VERSION"],
92
+ err: [:child, :out], &:read)
93
+ assert_equal "1\n", out
94
+ end
95
+
96
+ end# TestPackagerDataFile
@@ -0,0 +1,114 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestPackagerDataLoader < Test::Unit::TestCase
5
+
6
+ RP = Reflex::Packager
7
+
8
+ LIB_DIR = File.expand_path '../lib/reflex/packager', __dir__
9
+
10
+ APP = {
11
+ 'main.rb' => <<~RUBY,
12
+ $log = []
13
+ $log << require('foo')
14
+ $log << require('foo')
15
+ require_relative 'lib/bar'
16
+ load 'once.rb'
17
+ load 'once.rb'
18
+ $log << require('shellwords') # from the files
19
+ $log << [__FILE__, __dir__]
20
+ $log << $LOADED_FEATURES.grep(/\\/(foo|lib\\/ba[rz])\\.rb\\z/).map {File.basename _1}
21
+ p $log
22
+ RUBY
23
+ 'foo.rb' => '$log << :foo',
24
+ 'lib/bar.rb' => "require_relative 'baz'; $log << [:bar, __FILE__]",
25
+ 'lib/baz.rb' => '$log << :baz',
26
+ 'once.rb' => '$log << :once',
27
+ 'image.png' => "\x89PNG".b
28
+ }
29
+
30
+ # Packs +files+ as an app, and runs the main script of it in another Ruby,
31
+ # as a package does.
32
+ #
33
+ def run_app(files = APP, ruby_version: nil)
34
+ Dir.mktmpdir do |dir|
35
+ src = File.join dir, 'src'
36
+ dir = File.join dir, 'app'
37
+ files.each do |name, content|
38
+ path = File.join src, name
39
+ FileUtils.mkdir_p File.dirname(path)
40
+ File.binwrite path, content
41
+ end
42
+ RP::Platform.pack_app src, dir
43
+ yield src, dir if block_given?
44
+ if ruby_version
45
+ RP::DataFile.open(File.join dir, 'data.bin') do |data|
46
+ RP::DataFile.write data.path, data.names.to_h {[_1, data.read(_1)]}
47
+ .merge('.ruby-version' => ruby_version)
48
+ end
49
+ end
50
+
51
+ script = <<~RUBY
52
+ require #{File.join(LIB_DIR, 'data_loader.rb').dump} # with data_file.rb beside
53
+ Dir.chdir #{dir.dump}
54
+ $LOAD_PATH.unshift Dir.pwd
55
+ Reflex::Packager::DataLoader.setup Dir.pwd
56
+ load 'main.rb'
57
+ RUBY
58
+ IO.popen [RbConfig.ruby, '-e', script], err: [:child, :out], &:read
59
+ end
60
+ end
61
+
62
+ def test_pack_app()
63
+ run_app do |src, dir|
64
+ assert_equal APP.keys.sort, Dir.glob('**/*.*', base: src).sort # as they are
65
+ assert_equal %w[data.bin image.png], Dir.children(dir).sort # the others as they are
66
+ assert_equal "\x89PNG".b, File.binread(File.join dir, 'image.png')
67
+ RP::DataFile.open File.join(dir, 'data.bin') do |data|
68
+ assert_equal %w[.ruby-version foo.rbc lib/bar.rbc lib/baz.rbc main.rbc once.rbc],
69
+ data.names.sort
70
+ assert_equal RUBY_VERSION, data.read('.ruby-version')
71
+ assert_not_include data.read('main.rbc'), '$log << require' # compiled
72
+ end
73
+ end
74
+ end
75
+
76
+ def test_require()
77
+ log = eval run_app
78
+ assert_equal [
79
+ :foo, true, false,
80
+ :baz, [:bar, 'lib/bar.rb'],
81
+ :once, :once,
82
+ true,
83
+ ['main.rb', '.'],
84
+ %w[foo.rb bar.rb baz.rb] # each before it runs, for one it requires to require it back
85
+ ], log
86
+ end
87
+
88
+ def test_files()
89
+ files = {
90
+ 'main.rb' => <<~RUBY,
91
+ r = []
92
+ r << File.read('data/a.txt')
93
+ r << Dir.glob('**/*').sort # with none of the scripts
94
+ r << [File.exist?('main.rb'), File.exist?('sub.rb')]
95
+ r << require_relative('sub')
96
+ p r
97
+ RUBY
98
+ 'sub.rb' => '',
99
+ 'data/a.txt' => 'abc'
100
+ }
101
+ assert_equal [
102
+ 'abc',
103
+ %w[data data.bin data/a.txt],
104
+ [false, false],
105
+ true
106
+ ], eval(run_app files)
107
+ end
108
+
109
+ def test_ruby_version()
110
+ assert_match(/data file is for Ruby 0\.0\.0, not #{Regexp.escape RUBY_VERSION}/,
111
+ run_app(ruby_version: '0.0.0'))
112
+ end
113
+
114
+ end# TestPackagerDataLoader
@@ -0,0 +1,81 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestPackagerLibrary < Test::Unit::TestCase
5
+
6
+ RP = Reflex::Packager
7
+
8
+ # Makes the gems in a directory and puts their lib/ on the load path, as
9
+ # RUBYLIB does for the gems of a repository being worked on.
10
+ #
11
+ # @param gems [Hash] {dir => [gem name, dependencies, files]}
12
+ #
13
+ def fake_gems(gems, &block)
14
+ Dir.mktmpdir do |root|
15
+ gems.each do |dir, (name, deps, files)|
16
+ FileUtils.mkdir_p File.join(root, dir)
17
+ File.write File.join(root, dir, "#{dir}.gemspec"), <<~GEMSPEC
18
+ Gem::Specification.new do |s|
19
+ s.name = '#{name}'
20
+ s.version = '1.0'
21
+ #{deps.map {"s.add_dependency '#{_1}'"}.join "\n "}
22
+ end
23
+ GEMSPEC
24
+ files.each do |file|
25
+ path = File.join root, dir, file
26
+ FileUtils.mkdir_p File.dirname(path)
27
+ FileUtils.touch path
28
+ end
29
+ end
30
+ libs = gems.keys.map {File.join root, _1, 'lib'}
31
+ $LOAD_PATH.unshift(*libs)
32
+ block.call root
33
+ ensure
34
+ $LOAD_PATH.replace $LOAD_PATH - libs.to_a
35
+ end
36
+ end
37
+
38
+ def test_collect()
39
+ gems = {
40
+ 'fakebase' => [
41
+ 'fakebase',
42
+ [],
43
+ %w[lib/fakebase/extension.rb]
44
+ ],
45
+ # the name of the gem differs from the one of the library, as reflexion
46
+ 'fakenative' => [
47
+ 'fakenativeion',
48
+ %w[fakebase],
49
+ %w[lib/fakenative/extension.rb lib/fakenative/ext.rb]
50
+ ],
51
+ # not a library, which has no lib/*/extension.rb as reflex-packager
52
+ 'faketool' => [
53
+ 'faketool',
54
+ %w[fakebase],
55
+ %w[lib/faketool.rb]
56
+ ],
57
+ 'fakeapp' => [
58
+ 'fakeapp',
59
+ %w[faketool fakenativeion no-such-gem],
60
+ %w[lib/fakeapp/extension.rb]
61
+ ]
62
+ }
63
+ fake_gems gems do |root|
64
+ libs = RP::Library.collect File.join(root, 'fakeapp')
65
+ assert_equal %w[fakebase fakenative fakeapp], libs.map(&:name)
66
+ assert_equal [nil, 'fakenative_ext', nil], libs.map(&:extension)
67
+ assert_equal libs.map {File.join root, _1.name}, libs.map(&:root)
68
+
69
+ # leaving out the ones collected already
70
+ libs = RP::Library.collect File.join(root, 'fakeapp'), known: libs.first(1)
71
+ assert_equal %w[fakenative fakeapp], libs.map(&:name)
72
+ end
73
+ end
74
+
75
+ def test_collect_the_libraries_of_the_profiles()
76
+ # this repository, which the helper puts on the load path
77
+ assert_equal %w[xot rucy rays reflex], TEST_PROFILE.libraries.map(&:name)
78
+ assert_equal %w[rays_ext reflex_ext], TEST_PROFILE.extensions
79
+ end
80
+
81
+ end# TestPackagerLibrary