Class: ParallelSpecs::CLI::Dashboard
- Inherits:
-
Object
- Object
- ParallelSpecs::CLI::Dashboard
- Defined in:
- lib/parallel_specs/cli/dashboard.rb
Defined Under Namespace
Classes: WorkerState
Constant Summary collapse
- SPINNER =
['-', '\\', '|', '/'].freeze
- PROGRESS_BAR_WIDTH =
24- REFRESH_INTERVAL =
0.1
Instance Attribute Summary collapse
-
#workers ⇒ Object
readonly
Returns the value of attribute workers.
Instance Method Summary collapse
- #frame ⇒ Object
-
#initialize(groups:, event_files:, output: $stdout, use_colors: true, mode: :interactive, now: -> { ParallelSpecs.now }, width: nil, refresh_interval: REFRESH_INTERVAL) ⇒ Dashboard
constructor
A new instance of Dashboard.
- #plain? ⇒ Boolean
- #poll_once ⇒ Object
- #process_event(process_number, event) ⇒ Object
- #start ⇒ Object
- #stop ⇒ Object
- #worker_finished(process_number, exit_status:) ⇒ Object
- #worker_started(process_number) ⇒ Object
Constructor Details
#initialize(groups:, event_files:, output: $stdout, use_colors: true, mode: :interactive, now: -> { ParallelSpecs.now }, width: nil, refresh_interval: REFRESH_INTERVAL) ⇒ Dashboard
Returns a new instance of Dashboard.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 33 def initialize(groups:, event_files:, output: $stdout, use_colors: true, mode: :interactive, now: -> { ParallelSpecs.now }, width: nil, refresh_interval: REFRESH_INTERVAL) @workers = groups.each_with_index.map do |group, index| WorkerState.new(index + 1, group.size, nil, 0, 0, 0, nil, nil, nil, nil) end @event_files = event_files @output = output @use_colors = use_colors @mode = mode @now = now @width = width @refresh_interval = refresh_interval @mutex = Mutex.new @event_offsets = Hash.new(0) @event_remainders = Hash.new { |hash, key| hash[key] = +"" } @spinner_index = 0 @rendered_lines = 0 @dirty = true end |
Instance Attribute Details
#workers ⇒ Object (readonly)
Returns the value of attribute workers.
27 28 29 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 27 def workers @workers end |
Instance Method Details
#frame ⇒ Object
160 161 162 163 164 165 166 167 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 160 def frame lines = if interactive? [header_line, *workers.map { |worker| worker_line(worker) }] else [plain_header_line, *workers.map { |worker| plain_worker_line(worker) }] end "#{lines.join("\n")}\n" end |
#plain? ⇒ Boolean
29 30 31 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 29 def plain? @mode == :plain end |
#poll_once ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 137 def poll_once @event_files.each do |process_number, path| next unless File.exist?(path) File.open(path, 'r') do |file| file.seek(@event_offsets[process_number]) chunk = file.read.to_s @event_offsets[process_number] = file.pos next if chunk.empty? buffer = @event_remainders[process_number] << chunk lines = buffer.split("\n", -1) @event_remainders[process_number] = lines.pop.to_s lines.each do |line| next if line.empty? process_event(process_number, JSON.parse(line)) end end end end |
#process_event(process_number, event) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 114 def process_event(process_number, event) worker = @workers.fetch(process_number) worker.started_at ||= @now.call case event.fetch('event') when 'start' worker.example_total = event['total'] when 'example_started' worker.current_example = event['example'] when 'example_passed' worker.passed += 1 worker.current_example = event['example'] when 'example_pending' worker.pending += 1 worker.current_example = event['example'] when 'example_failed' worker.failed += 1 worker.current_example = event['example'] end @dirty = true end |
#start ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 52 def start @mutex.synchronize do @started_at = @now.call render if interactive? end return unless interactive? @running = true @refresh_thread = Thread.new do Thread.current.report_on_exception = false if Thread.current.respond_to?(:report_on_exception=) begin while @running sleep @refresh_interval @mutex.synchronize do poll_once @spinner_index += 1 render if @dirty end end rescue StandardError => e @running = false warn "parallel_specs: dashboard refresh failed while polling #{event_file_context}: #{e.class}: #{e.}" end end end |
#stop ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 80 def stop @running = false @refresh_thread&.join @mutex.synchronize do begin poll_once rescue StandardError => e warn "parallel_specs: dashboard final poll failed while polling #{event_file_context}: #{e.class}: #{e.}" end render @output.puts if interactive? @output.flush if @output.respond_to?(:flush) end end |
#worker_finished(process_number, exit_status:) ⇒ Object
104 105 106 107 108 109 110 111 112 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 104 def worker_finished(process_number, exit_status:) synchronize do worker = @workers.fetch(process_number) worker.started_at ||= @now.call worker.finished_at = @now.call worker.exit_status = exit_status @dirty = true end end |
#worker_started(process_number) ⇒ Object
96 97 98 99 100 101 102 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 96 def worker_started(process_number) synchronize do worker = @workers.fetch(process_number) worker.started_at ||= @now.call @dirty = true end end |