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 51 |
# 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_rows = 0 @last_frame = nil @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
165 166 167 168 169 170 171 172 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 165 def frame lines = if interactive? [header_line, *workers.map { |worker| worker_line(worker) }] else [plain_header_line] 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
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 138 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? begin process_event(process_number, JSON.parse(line)) rescue JSON::ParserError, KeyError => e warn "parallel_specs: dashboard event ignored while polling worker #{process_number + 1}=#{path}: #{e.class}: #{e.}" end end end end end |
#process_event(process_number, event) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 115 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
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 79 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 53 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 => e @running = false warn "parallel_specs: dashboard refresh failed while polling #{event_file_context}: #{e.class}: #{e.}" end end end |
#stop ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 81 def stop @running = false @refresh_thread&.join @mutex.synchronize do begin poll_once rescue => 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
105 106 107 108 109 110 111 112 113 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 105 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
97 98 99 100 101 102 103 |
# File 'lib/parallel_specs/cli/dashboard.rb', line 97 def worker_started(process_number) synchronize do worker = @workers.fetch(process_number) worker.started_at ||= @now.call @dirty = true end end |