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.
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, generated)
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}"
@@ -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.2
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