rspec_telemetry 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +35 -0
- data/LICENSE.txt +21 -0
- data/README.md +193 -0
- data/examples/sample.ndjson +15 -0
- data/exe/rspec-telemetry +7 -0
- data/exe/rspec-telemetry-compare +6 -0
- data/exe/rspec-telemetry-viewer +67 -0
- data/lib/rspec_telemetry/analyzer.rb +170 -0
- data/lib/rspec_telemetry/cli.rb +71 -0
- data/lib/rspec_telemetry/compare_cli.rb +129 -0
- data/lib/rspec_telemetry/config.rb +40 -0
- data/lib/rspec_telemetry/console_report.rb +124 -0
- data/lib/rspec_telemetry/factory_aggregation.rb +50 -0
- data/lib/rspec_telemetry/factory_comparison.rb +101 -0
- data/lib/rspec_telemetry/formatter.rb +91 -0
- data/lib/rspec_telemetry/ndjson.rb +24 -0
- data/lib/rspec_telemetry/recorder.rb +75 -0
- data/lib/rspec_telemetry/subscribers/factory_bot.rb +88 -0
- data/lib/rspec_telemetry/summary.rb +134 -0
- data/lib/rspec_telemetry/trace/viewer/app.rb +269 -0
- data/lib/rspec_telemetry/trace/viewer/app_renderer.rb +88 -0
- data/lib/rspec_telemetry/trace/viewer/detail_lines.rb +75 -0
- data/lib/rspec_telemetry/trace/viewer/detail_pane.rb +28 -0
- data/lib/rspec_telemetry/trace/viewer/document.rb +198 -0
- data/lib/rspec_telemetry/trace/viewer/follow_controller.rb +51 -0
- data/lib/rspec_telemetry/trace/viewer/format.rb +23 -0
- data/lib/rspec_telemetry/trace/viewer/label.rb +84 -0
- data/lib/rspec_telemetry/trace/viewer/layout.rb +100 -0
- data/lib/rspec_telemetry/trace/viewer/pane_resizer.rb +99 -0
- data/lib/rspec_telemetry/trace/viewer/report_pane.rb +26 -0
- data/lib/rspec_telemetry/trace/viewer/report_view.rb +86 -0
- data/lib/rspec_telemetry/trace/viewer/screen/ranked_screen.rb +66 -0
- data/lib/rspec_telemetry/trace/viewer/screen/timeline_screen.rb +180 -0
- data/lib/rspec_telemetry/trace/viewer/source.rb +52 -0
- data/lib/rspec_telemetry/trace/viewer/source_pane.rb +70 -0
- data/lib/rspec_telemetry/trace/viewer/source_resolver.rb +56 -0
- data/lib/rspec_telemetry/trace/viewer/source_view.rb +63 -0
- data/lib/rspec_telemetry/trace/viewer/status_line.rb +50 -0
- data/lib/rspec_telemetry/trace/viewer/text_report.rb +49 -0
- data/lib/rspec_telemetry/trace/viewer/theme.rb +30 -0
- data/lib/rspec_telemetry/trace/viewer/time_bar.rb +46 -0
- data/lib/rspec_telemetry/trace/viewer/timeline_pane.rb +53 -0
- data/lib/rspec_telemetry/trace/viewer/version.rb +9 -0
- data/lib/rspec_telemetry/trace/viewer.rb +31 -0
- data/lib/rspec_telemetry/version.rb +5 -0
- data/lib/rspec_telemetry/writer.rb +59 -0
- data/lib/rspec_telemetry.rb +102 -0
- metadata +122 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "theme"
|
|
4
|
+
require_relative "label"
|
|
5
|
+
require_relative "format"
|
|
6
|
+
|
|
7
|
+
module RSpecTelemetry
|
|
8
|
+
module Trace
|
|
9
|
+
module Viewer
|
|
10
|
+
class TimelinePane
|
|
11
|
+
EXPANDED = "- "
|
|
12
|
+
COLLAPSED = "+ "
|
|
13
|
+
NO_CHILDREN = " "
|
|
14
|
+
EVENT_INDENT = " "
|
|
15
|
+
|
|
16
|
+
def initialize(entries, list:, focus:, collapsed: nil, childful: nil, durations: nil)
|
|
17
|
+
@entries = entries
|
|
18
|
+
@list = list
|
|
19
|
+
@focus = focus
|
|
20
|
+
@collapsed = collapsed || []
|
|
21
|
+
@childful = childful || []
|
|
22
|
+
@durations = durations || {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def draw(canvas, rect)
|
|
26
|
+
highlight = @focus ? Theme::SELECT : Theme::SELECT_BLUR
|
|
27
|
+
TuiTui::List.new(@list).draw(canvas, rect, highlight: highlight, scrollbar: Theme.base) do |index, selected|
|
|
28
|
+
entry = @entries[index]
|
|
29
|
+
text = prefix(entry) + Label.plain(entry) + duration_suffix(entry)
|
|
30
|
+
style = selected ? highlight : Theme.style(Label.category(entry))
|
|
31
|
+
TuiTui::Line[TuiTui::Span[text, style]]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def prefix(entry)
|
|
38
|
+
return EVENT_INDENT unless entry.is_a?(Document::Action)
|
|
39
|
+
return NO_CHILDREN unless @childful.include?(entry.seq)
|
|
40
|
+
|
|
41
|
+
@collapsed.include?(entry.seq) ? COLLAPSED : EXPANDED
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def duration_suffix(entry)
|
|
45
|
+
return "" unless entry.is_a?(Document::Action)
|
|
46
|
+
|
|
47
|
+
formatted = Format.ms(@durations[entry.seq])
|
|
48
|
+
formatted ? " (#{formatted})" : ""
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "viewer/version"
|
|
4
|
+
require_relative "viewer/document"
|
|
5
|
+
require_relative "viewer/format"
|
|
6
|
+
require_relative "viewer/theme"
|
|
7
|
+
require_relative "viewer/label"
|
|
8
|
+
require_relative "viewer/text_report"
|
|
9
|
+
require_relative "viewer/timeline_pane"
|
|
10
|
+
require_relative "viewer/detail_lines"
|
|
11
|
+
require_relative "viewer/detail_pane"
|
|
12
|
+
require_relative "viewer/status_line"
|
|
13
|
+
require_relative "viewer/time_bar"
|
|
14
|
+
require_relative "viewer/source_pane"
|
|
15
|
+
require_relative "viewer/source"
|
|
16
|
+
require_relative "viewer/source_resolver"
|
|
17
|
+
require_relative "viewer/source_view"
|
|
18
|
+
require_relative "viewer/layout"
|
|
19
|
+
require_relative "viewer/report_view"
|
|
20
|
+
require_relative "viewer/report_pane"
|
|
21
|
+
require_relative "viewer/app_renderer"
|
|
22
|
+
require_relative "viewer/screen/timeline_screen"
|
|
23
|
+
require_relative "viewer/screen/ranked_screen"
|
|
24
|
+
require_relative "viewer/app"
|
|
25
|
+
|
|
26
|
+
module RSpecTelemetry
|
|
27
|
+
module Trace
|
|
28
|
+
module Viewer
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module RSpecTelemetry
|
|
7
|
+
class Writer
|
|
8
|
+
def initialize(output_path, flush_each: false)
|
|
9
|
+
@output_path = output_path
|
|
10
|
+
@flush_each = flush_each
|
|
11
|
+
@mutex = Mutex.new
|
|
12
|
+
@io = nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def open
|
|
16
|
+
FileUtils.mkdir_p(File.dirname(@output_path))
|
|
17
|
+
# Each run gets a fresh stream; appending would create misleading time gaps.
|
|
18
|
+
@io = File.open(@output_path, "w")
|
|
19
|
+
rescue => e
|
|
20
|
+
warn_failure("open", e)
|
|
21
|
+
@io = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def write(event)
|
|
25
|
+
return unless @io
|
|
26
|
+
|
|
27
|
+
@mutex.synchronize do
|
|
28
|
+
@io.puts(JSON.generate(event))
|
|
29
|
+
@io.flush if @flush_each
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
rescue => e
|
|
33
|
+
warn_failure("write", e)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def flush
|
|
37
|
+
@mutex.synchronize { @io&.flush }
|
|
38
|
+
rescue => e
|
|
39
|
+
warn_failure("flush", e)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def close
|
|
43
|
+
@mutex.synchronize do
|
|
44
|
+
@io&.flush
|
|
45
|
+
@io&.close
|
|
46
|
+
@io = nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
rescue => e
|
|
50
|
+
warn_failure("close", e)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def warn_failure(action, error)
|
|
56
|
+
warn("[rspec-telemetry] failed to #{action} event: #{error.class}: #{error.message}")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rspec_telemetry/version"
|
|
4
|
+
require_relative "rspec_telemetry/config"
|
|
5
|
+
require_relative "rspec_telemetry/writer"
|
|
6
|
+
require_relative "rspec_telemetry/summary"
|
|
7
|
+
require_relative "rspec_telemetry/recorder"
|
|
8
|
+
|
|
9
|
+
module RSpecTelemetry
|
|
10
|
+
class << self
|
|
11
|
+
def config
|
|
12
|
+
@config ||= Config.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def configure
|
|
16
|
+
yield config if block_given?
|
|
17
|
+
config
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def recorder
|
|
21
|
+
@recorder ||= Recorder.new(config)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def start!
|
|
25
|
+
return unless config.enabled
|
|
26
|
+
|
|
27
|
+
recorder.start
|
|
28
|
+
return unless recorder.started?
|
|
29
|
+
|
|
30
|
+
subscribe!
|
|
31
|
+
recorder
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def finish!
|
|
35
|
+
@recorder&.finish
|
|
36
|
+
unsubscribe!
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def reset!
|
|
40
|
+
unsubscribe!
|
|
41
|
+
@config = nil
|
|
42
|
+
@recorder = nil
|
|
43
|
+
@warned = nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def safely(context)
|
|
47
|
+
yield
|
|
48
|
+
# Telemetry must never break the user's RSpec run, even on non-StandardError failures.
|
|
49
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
50
|
+
warn_once(context, e)
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def warn_once(context, error)
|
|
57
|
+
@warned ||= {}
|
|
58
|
+
return if @warned[context]
|
|
59
|
+
|
|
60
|
+
@warned[context] = true
|
|
61
|
+
warn(
|
|
62
|
+
"[rspec-telemetry] #{context} で例外を無視しました(以後同種は抑制): " \
|
|
63
|
+
"#{error.class}: #{error.message}"
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def subscribe!
|
|
68
|
+
return unless config.capture_factory_bot
|
|
69
|
+
return if @factory_bot_subscriber
|
|
70
|
+
|
|
71
|
+
subscriber = build_factory_bot_subscriber
|
|
72
|
+
return unless subscriber
|
|
73
|
+
|
|
74
|
+
@factory_bot_subscriber = subscriber
|
|
75
|
+
@factory_bot_subscriber.subscribe
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# activesupport is an optional dependency: it is only needed for FactoryBot
|
|
79
|
+
# tracking, which relies on ActiveSupport::Notifications. FactoryBot itself
|
|
80
|
+
# pulls in activesupport, so when it is absent there are no factory events to
|
|
81
|
+
# capture and we silently skip the subscription.
|
|
82
|
+
def build_factory_bot_subscriber
|
|
83
|
+
require_relative "rspec_telemetry/subscribers/factory_bot"
|
|
84
|
+
Subscribers::FactoryBot.new(recorder)
|
|
85
|
+
rescue LoadError
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def unsubscribe!
|
|
90
|
+
@factory_bot_subscriber&.unsubscribe
|
|
91
|
+
@factory_bot_subscriber = nil
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if defined?(RSpec) && RSpec.respond_to?(:configure) && !ENV.key?("RSPEC_TELEMETRY_NO_AUTOLOAD")
|
|
97
|
+
require_relative "rspec_telemetry/formatter"
|
|
98
|
+
|
|
99
|
+
RSpec.configure do |config|
|
|
100
|
+
config.add_formatter(RSpecTelemetry::Formatter)
|
|
101
|
+
end
|
|
102
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rspec_telemetry
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- takahashimm
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rspec-core
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '3.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: tui_tui
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.2'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.2'
|
|
40
|
+
description: Collect RSpec / FactoryBot telemetry as NDJSON to find slow tests.
|
|
41
|
+
email:
|
|
42
|
+
- takahashimm@gmail.com
|
|
43
|
+
executables:
|
|
44
|
+
- rspec-telemetry
|
|
45
|
+
- rspec-telemetry-compare
|
|
46
|
+
- rspec-telemetry-viewer
|
|
47
|
+
extensions: []
|
|
48
|
+
extra_rdoc_files: []
|
|
49
|
+
files:
|
|
50
|
+
- CHANGELOG.md
|
|
51
|
+
- LICENSE.txt
|
|
52
|
+
- README.md
|
|
53
|
+
- examples/sample.ndjson
|
|
54
|
+
- exe/rspec-telemetry
|
|
55
|
+
- exe/rspec-telemetry-compare
|
|
56
|
+
- exe/rspec-telemetry-viewer
|
|
57
|
+
- lib/rspec_telemetry.rb
|
|
58
|
+
- lib/rspec_telemetry/analyzer.rb
|
|
59
|
+
- lib/rspec_telemetry/cli.rb
|
|
60
|
+
- lib/rspec_telemetry/compare_cli.rb
|
|
61
|
+
- lib/rspec_telemetry/config.rb
|
|
62
|
+
- lib/rspec_telemetry/console_report.rb
|
|
63
|
+
- lib/rspec_telemetry/factory_aggregation.rb
|
|
64
|
+
- lib/rspec_telemetry/factory_comparison.rb
|
|
65
|
+
- lib/rspec_telemetry/formatter.rb
|
|
66
|
+
- lib/rspec_telemetry/ndjson.rb
|
|
67
|
+
- lib/rspec_telemetry/recorder.rb
|
|
68
|
+
- lib/rspec_telemetry/subscribers/factory_bot.rb
|
|
69
|
+
- lib/rspec_telemetry/summary.rb
|
|
70
|
+
- lib/rspec_telemetry/trace/viewer.rb
|
|
71
|
+
- lib/rspec_telemetry/trace/viewer/app.rb
|
|
72
|
+
- lib/rspec_telemetry/trace/viewer/app_renderer.rb
|
|
73
|
+
- lib/rspec_telemetry/trace/viewer/detail_lines.rb
|
|
74
|
+
- lib/rspec_telemetry/trace/viewer/detail_pane.rb
|
|
75
|
+
- lib/rspec_telemetry/trace/viewer/document.rb
|
|
76
|
+
- lib/rspec_telemetry/trace/viewer/follow_controller.rb
|
|
77
|
+
- lib/rspec_telemetry/trace/viewer/format.rb
|
|
78
|
+
- lib/rspec_telemetry/trace/viewer/label.rb
|
|
79
|
+
- lib/rspec_telemetry/trace/viewer/layout.rb
|
|
80
|
+
- lib/rspec_telemetry/trace/viewer/pane_resizer.rb
|
|
81
|
+
- lib/rspec_telemetry/trace/viewer/report_pane.rb
|
|
82
|
+
- lib/rspec_telemetry/trace/viewer/report_view.rb
|
|
83
|
+
- lib/rspec_telemetry/trace/viewer/screen/ranked_screen.rb
|
|
84
|
+
- lib/rspec_telemetry/trace/viewer/screen/timeline_screen.rb
|
|
85
|
+
- lib/rspec_telemetry/trace/viewer/source.rb
|
|
86
|
+
- lib/rspec_telemetry/trace/viewer/source_pane.rb
|
|
87
|
+
- lib/rspec_telemetry/trace/viewer/source_resolver.rb
|
|
88
|
+
- lib/rspec_telemetry/trace/viewer/source_view.rb
|
|
89
|
+
- lib/rspec_telemetry/trace/viewer/status_line.rb
|
|
90
|
+
- lib/rspec_telemetry/trace/viewer/text_report.rb
|
|
91
|
+
- lib/rspec_telemetry/trace/viewer/theme.rb
|
|
92
|
+
- lib/rspec_telemetry/trace/viewer/time_bar.rb
|
|
93
|
+
- lib/rspec_telemetry/trace/viewer/timeline_pane.rb
|
|
94
|
+
- lib/rspec_telemetry/trace/viewer/version.rb
|
|
95
|
+
- lib/rspec_telemetry/version.rb
|
|
96
|
+
- lib/rspec_telemetry/writer.rb
|
|
97
|
+
homepage: https://github.com/takahashim/rspec_telemetry
|
|
98
|
+
licenses:
|
|
99
|
+
- MIT
|
|
100
|
+
metadata:
|
|
101
|
+
allowed_push_host: https://rubygems.org
|
|
102
|
+
homepage_uri: https://github.com/takahashim/rspec_telemetry
|
|
103
|
+
source_code_uri: https://github.com/takahashim/rspec_telemetry
|
|
104
|
+
changelog_uri: https://github.com/takahashim/rspec_telemetry
|
|
105
|
+
rdoc_options: []
|
|
106
|
+
require_paths:
|
|
107
|
+
- lib
|
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
109
|
+
requirements:
|
|
110
|
+
- - ">="
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: '3.2'
|
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
requirements: []
|
|
119
|
+
rubygems_version: 3.6.9
|
|
120
|
+
specification_version: 4
|
|
121
|
+
summary: Collect RSpec / FactoryBot telemetry as NDJSON to find slow tests.
|
|
122
|
+
test_files: []
|