Class: Towel::RSpec::Formatter

Inherits:
RSpec::Core::Formatters::BaseFormatter
  • Object
show all
Defined in:
lib/towel/rspec/formatter.rb

Overview

Takes RSpec events and turns them into a streaming upload to Towel.

Instance Method Summary collapse

Constructor Details

#initialize(io) ⇒ Formatter

Returns a new instance of Formatter.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/towel/rspec/formatter.rb', line 12

def initialize(io)
  super
  @session = Towel::Session.new
  @stdout = nil
  @stderr = nil
  @previous_stdout = nil
  @previous_stderr = nil
  @cancel_queue = Queue.new

  # Start a thread to listen for cancellations. Cancel the invocation if
  # requested. This exists because gRPC requests may not be made directly
  # from within a signal handler. It appears that RSpec shutdown runs as
  # such.
  Thread.new do
    cancelled = @cancel_queue.pop
    @session.cancel_invocation if cancelled
  end
end

Instance Method Details

#close(_notification) ⇒ Object



111
112
113
114
115
116
117
118
119
120
# File 'lib/towel/rspec/formatter.rb', line 111

def close(_notification)
  super

  $stdout = @previous_stdout
  $stderr = @previous_stderr

  unless @cancel_queue.closed?
    @session.finish_invocation
  end
end

#example_finished(notification) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/towel/rspec/formatter.rb', line 72

def example_finished(notification)
  example_group = notification.example.example_group
  example = notification.example
  group = example_group.described_class.to_s + example_group.description

  state = case example.execution_result.status
          when :passed then Towel::V1alpha::ResultState::SUCCESS
          when :failed then Towel::V1alpha::ResultState::FAILURE
          when :pending then Towel::V1alpha::ResultState::SKIPPED
          end

  duration = nil
  unless state == Towel::V1alpha::ResultState::SKIPPED
    duration = example.execution_result.run_time
  end

  description = nil
  if state == Towel::V1alpha::ResultState::SKIPPED
    description = example.execution_result.pending_message
  elsif state == Towel::V1alpha::ResultState::FAILURE
    description = example.execution_result.exception.message
  end

  # Cannot make a gRPC call from a signal handler, so check to make sure
  # we aren't operating from one.
  unless @cancel_queue.closed?
    @session.update_result(
      group,
      example.id,
      state: state,
      duration: duration,
      description: description
    )
  end

  @stdout.context = nil
  @stderr.context = nil
end

#example_started(notification) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/towel/rspec/formatter.rb', line 57

def example_started(notification)
  example_group = notification.example.example_group
  example = notification.example
  group = example_group.described_class.to_s + example_group.description

  context = @session.create_result(
    group,
    example.id,
    display_name: example.description
  )

  @stdout.context = context
  @stderr.context = context
end

#start(_notification) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/towel/rspec/formatter.rb', line 31

def start(_notification)
  super

  url = @session.create_invocation
  puts "View test results at #{url}"

  # Register a cancellation handler
  old_handler = Signal.trap("INT") do
    @cancel_queue << true
    # Mark the queue as closed so that other observers can determine if
    # the run has been cancelled or not.
    @cancel_queue.close
    if old_handler.respond_to?(:call)
      old_handler.call
    else
      raise Interrupt
    end
  end

  # Capture logs
  @previous_stdout = $stdout
  $stdout = @stdout = Towel::LogIO.new(@session.create_log("STDOUT"))
  @previous_stderr = $stderr
  $stderr = @stderr = Towel::LogIO.new(@session.create_log("STDERR"))
end