lumitrace 0.4.2 → 0.6.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.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/docs/spec.md +119 -2
- data/docs/supported_syntax.md +7 -0
- data/docs/tutorial.ja.md +67 -31
- data/docs/tutorial.md +67 -0
- data/lib/lumitrace/generate_resulted_html.rb +327 -393
- data/lib/lumitrace/generate_resulted_html_renderer.js +774 -0
- data/lib/lumitrace/record_instrument.rb +127 -27
- data/lib/lumitrace/version.rb +1 -1
- data/lib/lumitrace.rb +36 -7
- data/runv/index.html +1234 -427
- data/runv/sync_inline.rb +13 -1
- data/test/test_lumitrace.rb +295 -0
- metadata +2 -1
data/runv/sync_inline.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
index_path = File.expand_path("index.html", __dir__)
|
|
4
4
|
record_path = File.expand_path("../lib/lumitrace/record_instrument.rb", __dir__)
|
|
5
5
|
html_path = File.expand_path("../lib/lumitrace/generate_resulted_html.rb", __dir__)
|
|
6
|
+
renderer_js_path = File.expand_path("../lib/lumitrace/generate_resulted_html_renderer.js", __dir__)
|
|
6
7
|
version_path = File.expand_path("../lib/lumitrace/version.rb", __dir__)
|
|
7
8
|
|
|
8
9
|
index = File.read(index_path)
|
|
@@ -15,6 +16,17 @@ record_code = File.read(record_path).rstrip
|
|
|
15
16
|
html_code = File.read(html_path).lines.reject { |line|
|
|
16
17
|
line.match?(/^\s*require_relative\s+["']record_instrument["']\s*$/)
|
|
17
18
|
}.join.rstrip
|
|
19
|
+
renderer_js = File.read(renderer_js_path).rstrip
|
|
20
|
+
renderer_js_literal = renderer_js.gsub(/\\/, "\\\\\\\\").gsub(/'/, "\\\\'")
|
|
21
|
+
|
|
22
|
+
renderer_method_src = <<~RUBY.rstrip
|
|
23
|
+
def self.html_renderer_js
|
|
24
|
+
@html_renderer_js ||= '#{renderer_js_literal}'
|
|
25
|
+
end
|
|
26
|
+
RUBY
|
|
27
|
+
html_code_after = html_code.sub(/def self\.html_renderer_js\s*\n.*?\n\s*end/m) { renderer_method_src }
|
|
28
|
+
abort "failed to inline renderer JS into GenerateResultedHtml" if html_code_after == html_code
|
|
29
|
+
html_code = html_code_after
|
|
18
30
|
html_code = html_code.gsub("</script>", "\#{'</scr' + 'ipt>'}")
|
|
19
31
|
|
|
20
32
|
generated = +" # LUMITRACE_INLINE_BEGIN\n"
|
|
@@ -30,7 +42,7 @@ unless index.match?(pattern)
|
|
|
30
42
|
abort "inline block markers not found in #{index_path}"
|
|
31
43
|
end
|
|
32
44
|
|
|
33
|
-
index.sub!(pattern
|
|
45
|
+
index.sub!(pattern) { generated }
|
|
34
46
|
index.gsub!(/lumitrace \d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?\./, "lumitrace #{version}.")
|
|
35
47
|
File.write(index_path, index)
|
|
36
48
|
puts "updated #{index_path}"
|
data/test/test_lumitrace.rb
CHANGED
|
@@ -64,6 +64,43 @@ class LumiTraceTest < Minitest::Test
|
|
|
64
64
|
assert_operator out.scan(/Lumitrace::R\(\d+, \(/).length, :>=, 2
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
def test_instrument_command_style_call_with_hash_and_block_compiles
|
|
68
|
+
skip "RubyVM::InstructionSequence unavailable" unless defined?(RubyVM::InstructionSequence)
|
|
69
|
+
|
|
70
|
+
src = <<~RUBY
|
|
71
|
+
def path(*args)
|
|
72
|
+
yield if block_given?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
path = "x"
|
|
76
|
+
glob = "*.rb"
|
|
77
|
+
|
|
78
|
+
path path, "glob" => glob do
|
|
79
|
+
path
|
|
80
|
+
end
|
|
81
|
+
RUBY
|
|
82
|
+
|
|
83
|
+
out = Lumitrace::RecordInstrument.instrument_source(src, [], file_label: "sample.rb")
|
|
84
|
+
RubyVM::InstructionSequence.compile(out, "sample.rb")
|
|
85
|
+
assert_includes out, "path path"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_instrument_does_not_wrap_inside_defined
|
|
89
|
+
skip "RubyVM::InstructionSequence unavailable" unless defined?(RubyVM::InstructionSequence)
|
|
90
|
+
|
|
91
|
+
src = <<~RUBY
|
|
92
|
+
foo = 1
|
|
93
|
+
bar = 2
|
|
94
|
+
defined?(foo + bar)
|
|
95
|
+
RUBY
|
|
96
|
+
|
|
97
|
+
out = Lumitrace::RecordInstrument.instrument_source(src, [], file_label: "sample.rb")
|
|
98
|
+
RubyVM::InstructionSequence.compile(out, "sample.rb")
|
|
99
|
+
|
|
100
|
+
assert_includes out, "defined?(foo + bar)"
|
|
101
|
+
refute_match(/defined\?\([^)]*Lumitrace::R\(/, out)
|
|
102
|
+
end
|
|
103
|
+
|
|
67
104
|
def test_render_all_generates_html
|
|
68
105
|
Dir.mktmpdir do |dir|
|
|
69
106
|
path = File.join(dir, "lumitrace_events.json")
|
|
@@ -243,6 +280,37 @@ class LumiTraceTest < Minitest::Test
|
|
|
243
280
|
end
|
|
244
281
|
end
|
|
245
282
|
|
|
283
|
+
def test_events_from_ids_includes_unhit_locations
|
|
284
|
+
with_record_instrument_state do
|
|
285
|
+
mod = Lumitrace::RecordInstrument
|
|
286
|
+
mod.instance_variable_set(:@events_by_id, [])
|
|
287
|
+
mod.instance_variable_set(:@loc_by_id, [])
|
|
288
|
+
mod.instance_variable_set(:@next_id, 0)
|
|
289
|
+
Lumitrace.install_collect_mode("history")
|
|
290
|
+
mod.max_samples_per_expr = 3
|
|
291
|
+
|
|
292
|
+
hit_id = mod.register_location(
|
|
293
|
+
"a.rb",
|
|
294
|
+
{ start_line: 1, start_col: 0, end_line: 1, end_col: 1 },
|
|
295
|
+
kind: :expr
|
|
296
|
+
)
|
|
297
|
+
mod.register_location(
|
|
298
|
+
"a.rb",
|
|
299
|
+
{ start_line: 2, start_col: 0, end_line: 2, end_col: 1 },
|
|
300
|
+
kind: :expr
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
Lumitrace::R(hit_id, 1)
|
|
304
|
+
|
|
305
|
+
events = mod.events_from_ids.sort_by { |e| e[:start_line] }
|
|
306
|
+
assert_equal 2, events.length
|
|
307
|
+
assert_equal 1, events[0][:total]
|
|
308
|
+
assert_equal 0, events[1][:total]
|
|
309
|
+
assert_equal [], events[1][:sampled_values]
|
|
310
|
+
assert_equal({}, events[1][:types])
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
246
314
|
def test_text_comment_value_includes_type
|
|
247
315
|
events = [
|
|
248
316
|
{
|
|
@@ -346,6 +414,46 @@ class LumiTraceTest < Minitest::Test
|
|
|
346
414
|
assert_equal "types: Integer(3) (3rd run)", comment
|
|
347
415
|
end
|
|
348
416
|
|
|
417
|
+
def test_comment_value_ignores_unhit_event
|
|
418
|
+
comment = Lumitrace::GenerateResultedHtml.comment_value_with_total_for_line(
|
|
419
|
+
[
|
|
420
|
+
{
|
|
421
|
+
marker: true,
|
|
422
|
+
kind: "expr",
|
|
423
|
+
start_col: 0,
|
|
424
|
+
end_col: 1,
|
|
425
|
+
sampled_values: [],
|
|
426
|
+
types: {},
|
|
427
|
+
total: 0
|
|
428
|
+
}
|
|
429
|
+
]
|
|
430
|
+
)
|
|
431
|
+
assert_nil comment
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def test_render_text_marks_unhit_line_with_bang
|
|
435
|
+
text = Lumitrace::GenerateResultedHtml.render_text_from_normalized_events(
|
|
436
|
+
"foo = bar\n",
|
|
437
|
+
[
|
|
438
|
+
{
|
|
439
|
+
file: "a.rb",
|
|
440
|
+
start_line: 1,
|
|
441
|
+
start_col: 6,
|
|
442
|
+
end_line: 1,
|
|
443
|
+
end_col: 9,
|
|
444
|
+
kind: "expr",
|
|
445
|
+
sampled_values: [],
|
|
446
|
+
types: {},
|
|
447
|
+
total: 0
|
|
448
|
+
}
|
|
449
|
+
],
|
|
450
|
+
filename: "a.rb",
|
|
451
|
+
with_header: false
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
assert_includes text, "!1| foo = bar"
|
|
455
|
+
end
|
|
456
|
+
|
|
349
457
|
def test_normalize_events_keeps_sampled_value_objects
|
|
350
458
|
events = [
|
|
351
459
|
{
|
|
@@ -362,6 +470,193 @@ class LumiTraceTest < Minitest::Test
|
|
|
362
470
|
assert_equal "<obj>", normalized.first[:sampled_values].first["preview"]
|
|
363
471
|
end
|
|
364
472
|
|
|
473
|
+
def test_event_to_html_trace_payload_uses_last_value_when_sampled_values_missing
|
|
474
|
+
payload = Lumitrace::GenerateResultedHtml.event_to_html_trace_payload(
|
|
475
|
+
{
|
|
476
|
+
start_line: 1,
|
|
477
|
+
start_col: 0,
|
|
478
|
+
end_line: 1,
|
|
479
|
+
end_col: 3,
|
|
480
|
+
kind: "expr",
|
|
481
|
+
last_value: { type: "String", preview: "\"x\"" },
|
|
482
|
+
types: { "String" => 2 },
|
|
483
|
+
total: 2
|
|
484
|
+
}
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
assert_equal [{ type: "String", preview: "\"x\"" }], payload[:sampled_values]
|
|
488
|
+
assert_equal({ "String" => 2 }, payload[:types])
|
|
489
|
+
assert_equal 2, payload[:total]
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def test_build_html_payload_includes_command_text
|
|
493
|
+
payload = Lumitrace::GenerateResultedHtml.build_html_payload(
|
|
494
|
+
mode_info: { mode: "last", text: "Mode: last (last value)", max_samples: nil },
|
|
495
|
+
files: [],
|
|
496
|
+
command_text: "ruby script.rb arg1"
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
assert_equal "ruby script.rb arg1", payload[:meta][:command]
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def test_value_type_name_with_basic_object
|
|
503
|
+
klass = Class.new(BasicObject)
|
|
504
|
+
obj = klass.new
|
|
505
|
+
type = Lumitrace::RecordInstrument.value_type_name(obj)
|
|
506
|
+
refute_nil type
|
|
507
|
+
refute_empty type
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def test_summarize_value_with_basic_object
|
|
511
|
+
klass = Class.new(BasicObject)
|
|
512
|
+
obj = klass.new
|
|
513
|
+
summary = Lumitrace::RecordInstrument.summarize_value(obj)
|
|
514
|
+
assert summary.is_a?(Hash)
|
|
515
|
+
assert summary.key?(:type)
|
|
516
|
+
assert summary.key?(:preview)
|
|
517
|
+
refute_nil summary[:type]
|
|
518
|
+
refute_empty summary[:type]
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def test_R_with_basic_object_last_mode
|
|
522
|
+
with_record_instrument_state do
|
|
523
|
+
mod = Lumitrace::RecordInstrument
|
|
524
|
+
mod.instance_variable_set(:@events_by_id, [])
|
|
525
|
+
mod.instance_variable_set(:@loc_by_id, [])
|
|
526
|
+
mod.instance_variable_set(:@next_id, 0)
|
|
527
|
+
Lumitrace.install_collect_mode("last")
|
|
528
|
+
|
|
529
|
+
id = mod.register_location(
|
|
530
|
+
"a.rb",
|
|
531
|
+
{ start_line: 1, start_col: 0, end_line: 1, end_col: 1 },
|
|
532
|
+
kind: :expr
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
klass = Class.new(BasicObject)
|
|
536
|
+
obj = klass.new
|
|
537
|
+
result = Lumitrace::R(id, obj)
|
|
538
|
+
assert_same obj, result
|
|
539
|
+
|
|
540
|
+
events = mod.events_from_ids
|
|
541
|
+
assert_equal 1, events.first[:total]
|
|
542
|
+
refute_empty events.first[:types]
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
def test_R_with_basic_object_history_mode
|
|
547
|
+
with_record_instrument_state do
|
|
548
|
+
mod = Lumitrace::RecordInstrument
|
|
549
|
+
mod.instance_variable_set(:@events_by_id, [])
|
|
550
|
+
mod.instance_variable_set(:@loc_by_id, [])
|
|
551
|
+
mod.instance_variable_set(:@next_id, 0)
|
|
552
|
+
Lumitrace.install_collect_mode("history")
|
|
553
|
+
mod.max_samples_per_expr = 3
|
|
554
|
+
|
|
555
|
+
id = mod.register_location(
|
|
556
|
+
"a.rb",
|
|
557
|
+
{ start_line: 1, start_col: 0, end_line: 1, end_col: 1 },
|
|
558
|
+
kind: :expr
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
klass = Class.new(BasicObject)
|
|
562
|
+
Lumitrace::R(id, klass.new)
|
|
563
|
+
Lumitrace::R(id, 42)
|
|
564
|
+
|
|
565
|
+
events = mod.events_from_ids
|
|
566
|
+
assert_equal 2, events.first[:total]
|
|
567
|
+
assert_equal 2, events.first[:sampled_values].length
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
def test_R_with_basic_object_types_mode
|
|
572
|
+
with_record_instrument_state do
|
|
573
|
+
mod = Lumitrace::RecordInstrument
|
|
574
|
+
mod.instance_variable_set(:@events_by_id, [])
|
|
575
|
+
mod.instance_variable_set(:@loc_by_id, [])
|
|
576
|
+
mod.instance_variable_set(:@next_id, 0)
|
|
577
|
+
Lumitrace.install_collect_mode("types")
|
|
578
|
+
|
|
579
|
+
id = mod.register_location(
|
|
580
|
+
"a.rb",
|
|
581
|
+
{ start_line: 1, start_col: 0, end_line: 1, end_col: 1 },
|
|
582
|
+
kind: :expr
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
klass = Class.new(BasicObject)
|
|
586
|
+
Lumitrace::R(id, klass.new)
|
|
587
|
+
|
|
588
|
+
events = mod.events_from_ids
|
|
589
|
+
assert_equal 1, events.first[:total]
|
|
590
|
+
refute_empty events.first[:types]
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def test_compute_coverage
|
|
595
|
+
events = [
|
|
596
|
+
{ file: "a.rb", start_line: 1, start_col: 0, end_line: 1, end_col: 5, total: 3 },
|
|
597
|
+
{ file: "a.rb", start_line: 2, start_col: 0, end_line: 2, end_col: 5, total: 0 },
|
|
598
|
+
{ file: "a.rb", start_line: 3, start_col: 0, end_line: 3, end_col: 5, total: 1 },
|
|
599
|
+
{ file: "b.rb", start_line: 1, start_col: 0, end_line: 1, end_col: 5, total: 0 }
|
|
600
|
+
]
|
|
601
|
+
coverage = Lumitrace::RecordInstrument.compute_coverage(events)
|
|
602
|
+
assert_equal 2, coverage.length
|
|
603
|
+
|
|
604
|
+
a = coverage.find { |c| c[:file] == "a.rb" }
|
|
605
|
+
assert_equal 3, a[:total_lines]
|
|
606
|
+
assert_equal 2, a[:covered_lines]
|
|
607
|
+
assert_equal 66.7, a[:coverage_percent]
|
|
608
|
+
|
|
609
|
+
b = coverage.find { |c| c[:file] == "b.rb" }
|
|
610
|
+
assert_equal 1, b[:total_lines]
|
|
611
|
+
assert_equal 0, b[:covered_lines]
|
|
612
|
+
assert_equal 0.0, b[:coverage_percent]
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
def test_dump_events_json_wrapped_format
|
|
616
|
+
Dir.mktmpdir do |dir|
|
|
617
|
+
events = [
|
|
618
|
+
{ file: "a.rb", start_line: 1, start_col: 0, end_line: 1, end_col: 5, total: 1 }
|
|
619
|
+
]
|
|
620
|
+
path = File.join(dir, "out.json")
|
|
621
|
+
Lumitrace::RecordInstrument.dump_events_json(events, path)
|
|
622
|
+
data = JSON.parse(File.read(path))
|
|
623
|
+
|
|
624
|
+
assert_equal 1, data["version"]
|
|
625
|
+
assert data.key?("events")
|
|
626
|
+
assert data.key?("coverage")
|
|
627
|
+
assert_equal 1, data["events"].length
|
|
628
|
+
assert_equal 1, data["coverage"].length
|
|
629
|
+
assert_equal 100.0, data["coverage"][0]["coverage_percent"]
|
|
630
|
+
end
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
def test_render_all_reads_wrapped_json
|
|
634
|
+
Dir.mktmpdir do |dir|
|
|
635
|
+
sample = File.join(dir, "sample.rb")
|
|
636
|
+
File.write(sample, "puts hi\n")
|
|
637
|
+
events_path = File.join(dir, "events.json")
|
|
638
|
+
payload = {
|
|
639
|
+
"version" => 1,
|
|
640
|
+
"events" => [
|
|
641
|
+
{
|
|
642
|
+
"file" => sample,
|
|
643
|
+
"start_line" => 1,
|
|
644
|
+
"start_col" => 0,
|
|
645
|
+
"end_line" => 1,
|
|
646
|
+
"end_col" => 5,
|
|
647
|
+
"sampled_values" => ["ok"],
|
|
648
|
+
"total" => 1
|
|
649
|
+
}
|
|
650
|
+
],
|
|
651
|
+
"coverage" => []
|
|
652
|
+
}
|
|
653
|
+
File.write(events_path, JSON.dump(payload))
|
|
654
|
+
|
|
655
|
+
html = Lumitrace::GenerateResultedHtml.render_all(events_path, root: dir)
|
|
656
|
+
assert_includes html, "sample.rb"
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
|
|
365
660
|
def test_env_range_parsing
|
|
366
661
|
with_env("LUMITRACE_RANGE" => "a.rb:1-3,5-6;b.rb", "LUMITRACE_COLLECT_MODE" => "types") do
|
|
367
662
|
env = Lumitrace.resolve_env_options
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lumitrace
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Koichi Sasada
|
|
@@ -34,6 +34,7 @@ files:
|
|
|
34
34
|
- lib/lumitrace/enable_git_diff.rb
|
|
35
35
|
- lib/lumitrace/env.rb
|
|
36
36
|
- lib/lumitrace/generate_resulted_html.rb
|
|
37
|
+
- lib/lumitrace/generate_resulted_html_renderer.js
|
|
37
38
|
- lib/lumitrace/git_diff.rb
|
|
38
39
|
- lib/lumitrace/help_manifest.rb
|
|
39
40
|
- lib/lumitrace/record_instrument.rb
|