bitclust-core 1.6.1 → 1.8.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -0
  3. data/data/bitclust/catalog/ja_JP.UTF-8 +4 -2
  4. data/data/bitclust/template/class-index +0 -1
  5. data/data/bitclust/template/function-index +1 -0
  6. data/data/bitclust/template/layout +1 -0
  7. data/data/bitclust/template.epub/class +5 -5
  8. data/data/bitclust/template.epub/class-index +0 -1
  9. data/data/bitclust/template.epub/contents +7 -4
  10. data/data/bitclust/template.epub/layout +4 -4
  11. data/data/bitclust/template.offline/class-index +0 -1
  12. data/data/bitclust/template.offline/function-index +1 -0
  13. data/data/bitclust/template.offline/layout +3 -0
  14. data/lib/bitclust/classentry.rb +1 -1
  15. data/lib/bitclust/docentry.rb +1 -1
  16. data/lib/bitclust/functionentry.rb +1 -1
  17. data/lib/bitclust/generators/epub.rb +69 -5
  18. data/lib/bitclust/irb.rb +74 -0
  19. data/lib/bitclust/markdown_bridge.rb +11 -6
  20. data/lib/bitclust/markdown_exporter.rb +311 -0
  21. data/lib/bitclust/markdown_tree.rb +118 -11
  22. data/lib/bitclust/mdcompiler.rb +15 -1
  23. data/lib/bitclust/mdparser.rb +42 -1
  24. data/lib/bitclust/methoddatabase.rb +17 -4
  25. data/lib/bitclust/methodentry.rb +24 -0
  26. data/lib/bitclust/methodsignature.rb +1 -1
  27. data/lib/bitclust/nameutils.rb +13 -0
  28. data/lib/bitclust/preprocessor.rb +4 -37
  29. data/lib/bitclust/rbs_overload_matcher.rb +179 -0
  30. data/lib/bitclust/rbs_sig_importer.rb +276 -0
  31. data/lib/bitclust/rbs_signatures.rb +125 -0
  32. data/lib/bitclust/rdcompiler.rb +12 -1
  33. data/lib/bitclust/remote.rb +405 -0
  34. data/lib/bitclust/runner.rb +7 -10
  35. data/lib/bitclust/screen.rb +6 -1
  36. data/lib/bitclust/search_index_generator.rb +28 -10
  37. data/lib/bitclust/searcher.rb +54 -25
  38. data/lib/bitclust/subcommands/chm_command.rb +1 -1
  39. data/lib/bitclust/subcommands/epub_command.rb +2 -2
  40. data/lib/bitclust/subcommands/extract_command.rb +7 -0
  41. data/lib/bitclust/subcommands/htmlfile_command.rb +7 -0
  42. data/lib/bitclust/subcommands/methods_command.rb +5 -28
  43. data/lib/bitclust/subcommands/query_command.rb +1 -0
  44. data/lib/bitclust/subcommands/rbssig_command.rb +99 -0
  45. data/lib/bitclust/subcommands/server_command.rb +21 -10
  46. data/lib/bitclust/subcommands/setup_command.rb +32 -11
  47. data/lib/bitclust/subcommands/statichtml_command.rb +59 -0
  48. data/lib/bitclust/syntax_highlighter.rb +65 -4
  49. data/lib/bitclust/user_dirs.rb +62 -0
  50. data/lib/bitclust/version.rb +1 -1
  51. data/lib/bitclust-irb.rb +2 -0
  52. data/test/test_chm_command.rb +22 -0
  53. data/test/test_epub_command.rb +60 -0
  54. data/test/test_epub_generator.rb +330 -0
  55. data/test/test_extract_command.rb +45 -0
  56. data/test/test_irb_plugin.rb +212 -0
  57. data/test/test_link_checker.rb +1 -1
  58. data/test/test_markdown_bridge.rb +35 -0
  59. data/test/test_markdown_exporter.rb +260 -0
  60. data/test/test_markdown_tree.rb +144 -0
  61. data/test/test_mdcompiler.rb +47 -5
  62. data/test/test_mdparser.rb +223 -4
  63. data/test/test_methodentry.rb +111 -0
  64. data/test/test_methods_command.rb +32 -0
  65. data/test/test_methodsignature.rb +3 -1
  66. data/test/test_opensearchdescription_screen.rb +39 -0
  67. data/test/test_preprocessor.rb +26 -0
  68. data/test/test_query_command.rb +30 -0
  69. data/test/test_rbs_overload_matcher.rb +170 -0
  70. data/test/test_rbs_sig_importer.rb +237 -0
  71. data/test/test_rbssig_command.rb +186 -0
  72. data/test/test_rdcompiler.rb +99 -1
  73. data/test/test_remote.rb +374 -0
  74. data/test/test_runner.rb +61 -1
  75. data/test/test_search_index_generator.rb +81 -0
  76. data/test/test_searcher.rb +63 -0
  77. data/test/test_server_command.rb +41 -0
  78. data/test/test_setup_command.rb +164 -0
  79. data/test/test_statichtml_command.rb +137 -0
  80. data/test/test_syntax_highlighter.rb +124 -0
  81. data/test/test_user_dirs.rb +62 -0
  82. data/theme/default/js/run.js +14 -0
  83. data/theme/default/js/search_init.js +3 -1
  84. data/theme/default/js/search_page.js +7 -1
  85. data/theme/default/js/version_switcher.js +147 -0
  86. data/theme/default/search.css +21 -0
  87. data/theme/default/style.css +83 -7
  88. data/theme/lillia/style.css +26 -0
  89. metadata +71 -2
  90. data/lib/bitclust/ridatabase.rb +0 -359
@@ -23,7 +23,7 @@ class TestMDCompiler < Test::Unit::TestCase
23
23
  @rd = BitClust::RDCompiler.new(@u, 1, { :database => @db })
24
24
  end
25
25
 
26
- def compile_method(compiler, src)
26
+ def compile_method(compiler, src, rbs_signature_overloads: nil)
27
27
  method_entry = Object.new
28
28
  mock(method_entry).source { src }
29
29
  mock(method_entry).index_id.any_times { "dummy" }
@@ -32,6 +32,7 @@ class TestMDCompiler < Test::Unit::TestCase
32
32
  mock(method_entry).names.any_times { [] }
33
33
  mock(method_entry).since_map.any_times { {} }
34
34
  mock(method_entry).until_map.any_times { {} }
35
+ mock(method_entry).rbs_signature_overloads.any_times { rbs_signature_overloads }
35
36
  compiler.compile_method(method_entry)
36
37
  end
37
38
 
@@ -58,6 +59,47 @@ class TestMDCompiler < Test::Unit::TestCase
58
59
 
59
60
  # ---- メソッドエントリ ----
60
61
 
62
+ # RBS シグネチャ表示: MDCompiler も entry_chunk をオーバーライドしている
63
+ # ので、rd 経路と同じ位置(説明 <dd> の直前)に同じ <dt> が出ること
64
+ def test_rbs_signatures_dt_matches_rd_output
65
+ rd_src = "--- index(val) -> Integer\n\n説明\n"
66
+ overloads = [{'segments' => [['t', '() -> void']]}]
67
+ md_src = BitClust::RRDToMarkdown.convert(rd_src)
68
+ rd_html = compile_method(@rd, rd_src, rbs_signature_overloads: overloads)
69
+ md_html = compile_method(@md, md_src, rbs_signature_overloads: overloads)
70
+ assert_include md_html,
71
+ '<dt class="rbs-signature"><code>() &rarr; void</code></dt>'
72
+ assert_equal rd_html, md_html
73
+ end
74
+
75
+ # チャンク振り分けは md 側のシグネチャ行(### def ...)でも rd と同じに
76
+ # 動くこと(MDCompiler は行を "--- " に正規化してから照合する)
77
+ def test_rbs_signatures_chunk_assignment_matches_rd_output
78
+ rd_src = <<~RD
79
+ --- index(sub) -> Integer | nil
80
+
81
+ 文字列で探す。
82
+
83
+ --- index(pattern) {|s| ... } -> Integer | nil
84
+
85
+ ブロックで探す。
86
+ RD
87
+ overloads = [
88
+ {'segments' => [['t', '(String sub) -> Integer?']],
89
+ 'params' => ['sub'], 'arity' => [1, 1]},
90
+ {'segments' => [['t', '(Regexp pattern) { (String) -> bool } -> Integer?']],
91
+ 'params' => ['pattern'], 'arity' => [1, 1], 'block' => 'req'},
92
+ ]
93
+ md_src = BitClust::RRDToMarkdown.convert(rd_src)
94
+ rd_html = compile_method(@rd, rd_src, rbs_signature_overloads: overloads)
95
+ md_html = compile_method(@md, md_src, rbs_signature_overloads: overloads)
96
+ assert_match(
97
+ %r!<code>index\(pattern\).*?</dt>\n<dt class="rbs-signature"><code>\(Regexp pattern\) \{ \(String\) &rarr; bool \} &rarr; Integer\?</code></dt>\n<dd!m,
98
+ md_html)
99
+ assert_equal 2, md_html.scan('rbs-signature').size
100
+ assert_equal rd_html, md_html
101
+ end
102
+
61
103
  def test_method_signature_and_paragraph
62
104
  assert_equivalent_method <<~RD
63
105
  --- index(val) -> Integer
@@ -752,10 +794,10 @@ class TestMDCompiler < Test::Unit::TestCase
752
794
  def test_gfm_table_alignment
753
795
  md = "# T\n\n| a | b | c |\n|:--|:-:|--:|\n| 1 | 2 | 3 |\n"
754
796
  html = gfm_compiler.compile(md)
755
- assert_include html, '<th align="left">a</th>'
756
- assert_include html, '<th align="center">b</th>'
757
- assert_include html, '<th align="right">c</th>'
758
- assert_include html, '<td align="right">3</td>'
797
+ assert_include html, '<th style="text-align:left">a</th>'
798
+ assert_include html, '<th style="text-align:center">b</th>'
799
+ assert_include html, '<th style="text-align:right">c</th>'
800
+ assert_include html, '<td style="text-align:right">3</td>'
759
801
  end
760
802
 
761
803
  def test_gfm_pipe_line_without_delimiter_is_paragraph
@@ -82,7 +82,7 @@ class TestMDParser < Test::Unit::TestCase
82
82
 
83
83
  ## Class Methods
84
84
 
85
- ### def new -> Comparable
85
+ ### def Comparable.new -> Comparable
86
86
 
87
87
  生成。
88
88
  MD
@@ -181,7 +181,7 @@ class TestMDParser < Test::Unit::TestCase
181
181
  end
182
182
 
183
183
  def test_unsupported_signature_forms_are_rejected
184
- # RBS 形式(MARKUP_SPEC §3.2 で構想)と self. プレフィクスは描画側
184
+ # RBS 形式(MARKUP_SPEC §3.2 で構想)は描画側
185
185
  # (MethodSignature.parse)が受理できないため、パース時に拒否する
186
186
  base = <<~MD
187
187
  ---
@@ -197,12 +197,83 @@ class TestMDParser < Test::Unit::TestCase
197
197
 
198
198
  説明。
199
199
  MD
200
- ['### def each: () { (untyped) -> void } -> self',
201
- '### def self.new(size = 0) -> Foo'].each do |sig|
200
+ ['### def each: () { (untyped) -> void } -> self'].each do |sig|
202
201
  assert_raise(BitClust::ParseError, sig) { parse_md(base % sig) }
203
202
  end
204
203
  end
205
204
 
205
+ def test_self_and_class_name_prefixed_signatures
206
+ # クラスメソッドは `def self.name` / `def Klass.name` 形式でも書ける。
207
+ # どちらも singleton method として登録され、名前にプレフィクスは含まない
208
+ md = <<~MD
209
+ ---
210
+ library: _builtin
211
+ ---
212
+ # class Foo < Object
213
+
214
+ テスト。
215
+
216
+ ## Class Methods
217
+
218
+ ### def self.build(x) -> Foo
219
+
220
+ self. 形式。
221
+
222
+ ### def Foo.create(x) -> Foo
223
+
224
+ Klass. 形式。
225
+ MD
226
+ _, lib = parse_md(md)
227
+ entries = lib.classes.first.entries
228
+ assert_equal [["build"], ["create"]], entries.map(&:names)
229
+ assert_equal [:singleton_method, :singleton_method], entries.map(&:type)
230
+ # source には書かれたままの md が入る
231
+ assert_include entries[0].source, "### def self.build(x) -> Foo"
232
+ end
233
+
234
+ def test_main_prefixed_signature_on_object_page
235
+ # object main のページでは main. プレフィクスで特異メソッドを書ける
236
+ # (main は fatal・ARGF.class と同様に文法上クラス名として扱われる)
237
+ md = <<~MD
238
+ ---
239
+ library: _builtin
240
+ ---
241
+ # object main
242
+
243
+ テスト。
244
+
245
+ ## Class Methods
246
+
247
+ ### def main.include(*modules) -> self
248
+
249
+ main. 形式。
250
+ MD
251
+ _, lib = parse_md(md)
252
+ entries = lib.classes.first.entries
253
+ assert_equal [["include"]], entries.map(&:names)
254
+ assert_equal [:singleton_method], entries.map(&:type)
255
+ end
256
+
257
+ def test_self_prefixed_signature_in_instance_methods_section_is_rejected
258
+ # self. は singleton 確定なので Instance Methods セクションとは矛盾する
259
+ md = <<~MD
260
+ ---
261
+ library: _builtin
262
+ ---
263
+ # class Foo < Object
264
+
265
+ テスト。
266
+
267
+ ## Instance Methods
268
+
269
+ ### def self.bar -> nil
270
+
271
+ 矛盾。
272
+ MD
273
+ error = assert_raise(BitClust::ParseError) { parse_md(md) }
274
+ assert_include error.message, "signature crash"
275
+ end
276
+
206
277
  def test_library_file_with_category
207
278
  md = <<~MD
208
279
  ---
@@ -341,6 +412,45 @@ class TestMDParser < Test::Unit::TestCase
341
412
  end
342
413
  end
343
414
 
415
+ def test_update_by_markdowntree_else_and_single_version_memberships
416
+ # bitclust#331: ゲート付き library リストの #@version V(単一版)と #@else。
417
+ # 3.2 だけ onlylib、それ以外は builtin に属する。版の比較は Gem::Version
418
+ # なので 3.2.0 の DB でも 3.2 と同じ版として扱う
419
+ require 'tmpdir'
420
+ Dir.mktmpdir do |root|
421
+ write = ->(rel, s) {
422
+ path = File.join(root, rel)
423
+ FileUtils.mkdir_p(File.dirname(path))
424
+ File.write(path, s)
425
+ }
426
+ write.call('builtin.md', "---\ntype: library\n---\nbuiltin。\n")
427
+ write.call('onlylib.md', "---\ntype: library\n---\nonlylib。\n")
428
+ write.call('onlylib/Shared.md', <<~'MD')
429
+ ---
430
+ library:
431
+ #@version 3.2
432
+ - onlylib
433
+ #@else
434
+ - builtin
435
+ #@end
436
+ ---
437
+ # class Shared < Object
438
+
439
+ 共有クラス。
440
+ MD
441
+
442
+ classes = ->(version) {
443
+ db = BitClust::MethodDatabase.dummy("version" => version)
444
+ db.update_by_markdowntree(root)
445
+ %w[builtin onlylib].map { |lib| db.fetch_library(lib).classes.map(&:name) }
446
+ }
447
+ assert_equal [[], %w[Shared]], classes.call("3.2")
448
+ assert_equal [[], %w[Shared]], classes.call("3.2.0")
449
+ assert_equal [%w[Shared], []], classes.call("3.4")
450
+ assert_equal [%w[Shared], []], classes.call("3.0")
451
+ end
452
+ end
453
+
344
454
  def test_update_by_markdowntree_parse_error_suggests_gem_update
345
455
  # manual/ が新しい bitclust を必要とする記法を含むとき、古い gem では
346
456
  # パースエラーになる。原因へたどり着けるよう gem 更新の案内を添える
@@ -570,4 +680,113 @@ class TestMDParser < Test::Unit::TestCase
570
680
  assert_equal('2.0.0', entry.since_of('fuga'))
571
681
  assert_equal('3.0', entry.since_of('fuga2'))
572
682
  end
683
+
684
+ def test_entry_style_bare_def_in_singleton_section_is_rejected
685
+ # MARKUP_SPEC §3.1: 特異メソッドはプレフィクス必須(def 行だけで
686
+ # メソッド種別が分かるようにする)
687
+ md = <<~MD
688
+ ---
689
+ library: _builtin
690
+ ---
691
+ # class Foo < Object
692
+
693
+ テスト。
694
+
695
+ ## Class Methods
696
+
697
+ ### def build(x) -> Foo
698
+
699
+ プレフィクスなし。
700
+ MD
701
+ error = assert_raise(BitClust::ParseError) { parse_md(md) }
702
+ assert_include error.message, "singleton method entry must be written as `def Foo.name' or `def self.name'"
703
+ end
704
+
705
+ def test_entry_style_keyword_section_mismatch_is_rejected
706
+ # rurema/doctree#3291 型の分類間違い: キーワードとセクションの不一致
707
+ base = <<~MD
708
+ ---
709
+ library: _builtin
710
+ ---
711
+ # class Foo < Object
712
+
713
+ テスト。
714
+
715
+ ## %s
716
+
717
+ ### %s
718
+
719
+ 不一致。
720
+ MD
721
+ [
722
+ # Class Methods 配下の const(#3291 の実例)
723
+ ['Class Methods', 'const CURRENT -> Foo', "entry keyword `const' does not match this section (expected `def')"],
724
+ # Constants 配下の gvar
725
+ ['Constants', 'gvar $foo -> Foo', "entry keyword `gvar' does not match this section (expected `const')"],
726
+ # Instance Methods 配下の const
727
+ ['Instance Methods', 'const BAR -> Integer', "entry keyword `const' does not match this section (expected `def')"],
728
+ ].each do |section, sig, message|
729
+ error = assert_raise(BitClust::ParseError, "#{section} / #{sig}") do
730
+ parse_md(base % [section, sig])
731
+ end
732
+ assert_include error.message, message
733
+ end
734
+ end
735
+
736
+ def test_entry_style_module_functions_require_keyword
737
+ md = <<~MD
738
+ ---
739
+ library: _builtin
740
+ ---
741
+ # module Foo
742
+
743
+ テスト。
744
+
745
+ ## Module Functions
746
+
747
+ ### def bar -> nil
748
+
749
+ module_function キーワードなし。
750
+ MD
751
+ error = assert_raise(BitClust::ParseError) { parse_md(md) }
752
+ assert_include error.message, "entry keyword `def' does not match this section (expected `module_function def')"
753
+ end
754
+
755
+ def test_entry_style_accepts_consistent_forms
756
+ md = <<~MD
757
+ ---
758
+ library: _builtin
759
+ ---
760
+ # class Foo < Object
761
+
762
+ テスト。
763
+
764
+ ## Class Methods
765
+
766
+ ### def Foo.build(x) -> Foo
767
+
768
+ Klass 形式。
769
+
770
+ ### def self.create(x) -> Foo
771
+
772
+ self 形式。
773
+
774
+ ## Instance Methods
775
+
776
+ ### def each {|x| ... } -> self
777
+
778
+ 素の def。
779
+
780
+ ## Constants
781
+
782
+ ### const BAR -> Integer
783
+
784
+ 定数。
785
+ MD
786
+ _, lib = parse_md(md)
787
+ entries = lib.classes.first.entries
788
+ assert_equal [["build"], ["create"], ["each"], ["BAR"]], entries.map(&:names)
789
+ assert_equal [:singleton_method, :singleton_method, :instance_method, :constant],
790
+ entries.map(&:type)
791
+ end
573
792
  end
@@ -3,6 +3,7 @@ require 'bitclust'
3
3
  require 'bitclust/methoddatabase'
4
4
  require 'tmpdir'
5
5
  require 'fileutils'
6
+ require 'json'
6
7
 
7
8
  class TestMethodEntryTitleLabels < Test::Unit::TestCase
8
9
  def test_title_labels_matches_label_when_no_alias
@@ -326,3 +327,113 @@ class TestMethodEntrySinceUntil < Test::Unit::TestCase
326
327
  File.write(path, lines.join)
327
328
  end
328
329
  end
330
+
331
+ # rbs_sig property(RBS シグネチャ表示)。オーバーロード配列
332
+ # ({"segments" => 行, "params"/"arity"/"block" => メタ情報} の配列)を
333
+ # {"overloads": [...]} の JSON 1 行で 'String' 型 property に格納する。
334
+ # JSON は改行を含まず、値中の '=' や ',' も property ファイル
335
+ # (key=value 1 行形式)で安全。
336
+ #
337
+ # テストリスト:
338
+ # [x] 既定は nil で rbs_signature_overloads も nil
339
+ # [x] JSON を入れて save → reload で round-trip する
340
+ # [x] 旧形式(セグメント行列そのものの配列)は segments だけの
341
+ # オーバーロード列に読み替える(#322 以前の DB)
342
+ # [x] property 行が無い古い DB でも nil(後方互換)
343
+ # [x] 空文字列(シグネチャ無しで save された値)も nil 扱い
344
+ # [x] 壊れた JSON は nil 扱い(描画を落とさない)
345
+ class TestMethodEntryRbsSig < Test::Unit::TestCase
346
+ OVERLOADS = [
347
+ {'segments' => [['t', '('], ['c', 'Integer'], ['t', ') -> '], ['c', 'String']],
348
+ 'params' => ['base'], 'arity' => [1, 1]},
349
+ {'segments' => [['t', '() -> void']],
350
+ 'params' => [], 'arity' => [0, 0], 'block' => 'req'},
351
+ ]
352
+
353
+ def setup
354
+ @dir = Dir.mktmpdir
355
+ @db = BitClust::MethodDatabase.new(@dir)
356
+ @db.init
357
+ @db.transaction do
358
+ @db.propset('version', '4.0')
359
+ @db.propset('encoding', 'utf-8')
360
+ end
361
+ end
362
+
363
+ def teardown
364
+ FileUtils.rm_rf(@dir)
365
+ end
366
+
367
+ # 未保存エントリの String 型 property は "(uninitialized)" センチネル
368
+ # なので、rbs_sig そのものではなく overloads が nil であることを見る
369
+ def test_default_has_no_overloads
370
+ m = build_entry('sig')
371
+ assert_nil m.rbs_signature_overloads
372
+ end
373
+
374
+ def test_roundtrip
375
+ m = build_entry('sig')
376
+ m.rbs_sig = JSON.generate({'overloads' => OVERLOADS})
377
+ m.save
378
+
379
+ reloaded = reload_entry(m.id)
380
+ assert_equal(OVERLOADS, reloaded.rbs_signature_overloads)
381
+ end
382
+
383
+ def test_old_array_format_is_read_as_segments_only
384
+ m = build_entry('sig')
385
+ m.rbs_sig = JSON.generate(OVERLOADS.map {|o| o['segments'] })
386
+ m.save
387
+
388
+ reloaded = reload_entry(m.id)
389
+ assert_equal(OVERLOADS.map {|o| {'segments' => o['segments']} },
390
+ reloaded.rbs_signature_overloads)
391
+ end
392
+
393
+ def test_missing_property_is_backward_compatible
394
+ m = build_entry('sig')
395
+ m.save
396
+ strip_property_line(m.id, 'rbs_sig')
397
+
398
+ reloaded = reload_entry(m.id)
399
+ assert_nil reloaded.rbs_sig
400
+ assert_nil reloaded.rbs_signature_overloads
401
+ end
402
+
403
+ def test_saved_without_signature_is_treated_as_absent
404
+ m = build_entry('sig')
405
+ m.save
406
+
407
+ reloaded = reload_entry(m.id)
408
+ assert_nil reloaded.rbs_signature_overloads
409
+ end
410
+
411
+ def test_broken_json_is_treated_as_absent
412
+ m = build_entry('sig')
413
+ m.rbs_sig = '{broken'
414
+ assert_nil m.rbs_signature_overloads
415
+ end
416
+
417
+ private
418
+
419
+ def build_entry(name)
420
+ id = BitClust::NameUtils.build_method_id('_builtin', 'Foo', :instance_method, name)
421
+ m = BitClust::MethodEntry.new(@db, id)
422
+ m.names = [name]
423
+ m.visibility = :public
424
+ m.kind = :defined
425
+ m.source = "説明\n"
426
+ m
427
+ end
428
+
429
+ def reload_entry(id)
430
+ fresh_db = BitClust::MethodDatabase.new(@dir)
431
+ BitClust::MethodEntry.new(fresh_db, id)
432
+ end
433
+
434
+ def strip_property_line(id, key)
435
+ path = File.join(@dir, BitClust::NameUtils.encodeid("method/#{id}"))
436
+ lines = File.readlines(path).reject {|l| l.start_with?("#{key}=") }
437
+ File.write(path, lines.join)
438
+ end
439
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require 'test/unit'
3
+ require 'bitclust'
4
+ require 'bitclust/subcommands/methods_command'
5
+
6
+ class TestMethodsCommand < Test::Unit::TestCase
7
+ def setup
8
+ @cmd = BitClust::Subcommands::MethodsCommand.new
9
+ end
10
+
11
+ def test_needs_no_database
12
+ assert_false(@cmd.needs_database?)
13
+ end
14
+
15
+ def test_content_option_is_not_accepted
16
+ assert_raise(OptionParser::InvalidOption) do
17
+ @cmd.parse(['-c', 'Object'])
18
+ end
19
+ end
20
+
21
+ def test_ri_database_option_is_not_accepted
22
+ assert_raise(OptionParser::InvalidOption) do
23
+ @cmd.parse(['--ri-database=/tmp/ri', 'Object'])
24
+ end
25
+ end
26
+
27
+ def test_diff_and_ruby_options_are_kept
28
+ assert_nothing_raised do
29
+ @cmd.parse(['--ruby=3.4.8', '--diff=refm/api/src/_builtin/Object', 'Object'])
30
+ end
31
+ end
32
+ end
@@ -6,7 +6,9 @@ class TestMethodSignature < Test::Unit::TestCase
6
6
  data("special var" => ["$_ -> String | nil", "--- $_ -> String | nil"],
7
7
  "backquote" => ["`command` -> String", "--- `(command) -> String"],
8
8
  "aset" => ["self[key] = value", "--- []=(key, value)"],
9
- "aset splat" => ["[]=(*idxary)", "--- []=(*idxary)"])
9
+ "aset splat" => ["[]=(*idxary)", "--- []=(*idxary)"],
10
+ "self prefix" => ["build(x) -> Foo", "--- self.build(x) -> Foo"],
11
+ "class prefix" => ["build(x) -> Foo", "--- Foo.build(x) -> Foo"])
10
12
  def test_friendlyname(data)
11
13
  friendly_string, method_signature = data
12
14
  assert_equal(friendly_string, BitClust::MethodSignature.parse(method_signature).friendly_string)
@@ -0,0 +1,39 @@
1
+ require 'test/unit'
2
+ require 'uri'
3
+ require 'bitclust'
4
+ require 'bitclust/screen'
5
+
6
+ class TestOpenSearchDescriptionScreen < Test::Unit::TestCase
7
+ SRC = <<'HERE'
8
+ = class Foo
9
+ description
10
+ HERE
11
+
12
+ def screen
13
+ _lib, db = BitClust::RRDParser.parse(SRC, 'testlib', {'version' => '3.4'})
14
+ datadir = File.expand_path('../data/bitclust', __dir__)
15
+ manager = BitClust::ScreenManager.new(
16
+ :templatedir => "#{datadir}/template",
17
+ :catalogdir => "#{datadir}/catalog",
18
+ :encoding => 'utf-8',
19
+ :default_encoding => 'utf-8',
20
+ :base_url => '',
21
+ :cgi_url => '/view',
22
+ :target_version => '3.4'
23
+ )
24
+ manager.opensearchdescription_screen(URI.parse('http://example.com/view/opensearchdescription'),
25
+ :database => db)
26
+ end
27
+
28
+ def test_body_is_bare_xml_without_html_layout
29
+ body = screen.body
30
+ assert_match(/\A<\?xml/, body)
31
+ assert_not_include(body, '<html')
32
+ assert_match(%r{\A<\?xml[^>]*\?>\s*<OpenSearchDescription\b.*</OpenSearchDescription>\s*\z}m, body)
33
+ assert_include(body, 'http://example.com/view/search')
34
+ end
35
+
36
+ def test_content_type_is_opensearch_xml
37
+ assert_equal 'application/opensearchdescription+xml; charset=utf-8', screen.content_type
38
+ end
39
+ end
@@ -516,6 +516,32 @@ HERE
516
516
  assert_equal "", process(src, '4.0')
517
517
  end
518
518
 
519
+ def test_single_version
520
+ # 単一版のみの省略記法(#%if (version == "V") と同義)
521
+ src = gated('#%version 3.1')
522
+ assert_equal "out\n", process(src, '3.0')
523
+ assert_equal "in\n", process(src, '3.1')
524
+ assert_equal "out\n", process(src, '3.2')
525
+ end
526
+
527
+ def test_single_version_quoted
528
+ src = gated('#%version "3.1"')
529
+ assert_equal "in\n", process(src, '3.1')
530
+ assert_equal "out\n", process(src, '3.2')
531
+ end
532
+
533
+ def test_single_version_old_prefix
534
+ src = gated("\#@version 3.1")
535
+ assert_equal "in\n", process(src, '3.1')
536
+ end
537
+
538
+ def test_single_version_teeny
539
+ # 版名は文字列一致(teeny 付き版名は teeny まで一致したときのみ)
540
+ src = gated('#%version 2.7.0')
541
+ assert_equal "in\n", process(src, '2.7.0')
542
+ assert_equal "out\n", process(src, '2.7')
543
+ end
544
+
519
545
  def test_two_dot_range_is_error
520
546
  # .. (終端を含む Range)は誤解を招くため受け付けない(#285)
521
547
  assert_raise(BitClust::ParseError) { process(gated('#%version 3.1..3.4'), '3.2') }
@@ -0,0 +1,30 @@
1
+ require 'test/unit'
2
+ require 'stringio'
3
+ require 'tmpdir'
4
+ require 'bitclust'
5
+ require 'bitclust/subcommands/query_command'
6
+
7
+ class TestQueryCommand < Test::Unit::TestCase
8
+ def run_query(script, capi: false)
9
+ Dir.mktmpdir do |dir|
10
+ cmd = BitClust::Subcommands::QueryCommand.new
11
+ cmd.parse([script])
12
+ out = StringIO.new
13
+ orig, $stdout = $stdout, out
14
+ begin
15
+ cmd.exec([script], {:prefix => dir, :capi => capi})
16
+ ensure
17
+ $stdout = orig
18
+ end
19
+ out.string
20
+ end
21
+ end
22
+
23
+ def test_db_is_available_to_the_script
24
+ assert_equal %Q("BitClust::MethodDatabase"\n), run_query('@db.class.name')
25
+ end
26
+
27
+ def test_capi_uses_function_database
28
+ assert_equal %Q("BitClust::FunctionDatabase"\n), run_query('@db.class.name', capi: true)
29
+ end
30
+ end