flatware 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,67 @@
1
+ require 'cucumber/formatter/console'
2
+ require 'flatware/formatters/console'
3
+ require 'flatware/checkpoint'
4
+ module Flatware
5
+ module Formatters
6
+ class Console
7
+ class Summary
8
+ include ::Cucumber::Formatter::Console
9
+ attr_reader :io, :steps, :scenarios
10
+
11
+ def initialize(steps, scenarios=[], io=StringIO.new)
12
+ @io = io
13
+ @steps = steps
14
+ @scenarios = scenarios
15
+ end
16
+
17
+ def summarize
18
+ 2.times { io.puts }
19
+ print_failures(steps, 'step')
20
+ print_failures(scenarios.select(&:failed_outside_step?), 'scenario')
21
+ print_failed_scenarios scenarios
22
+ print_counts 'scenario', scenarios
23
+ print_counts 'step', steps
24
+ end
25
+
26
+ private
27
+
28
+ def print_failed_scenarios(scenarios)
29
+ return unless scenarios.any? &with_status(:failed)
30
+
31
+ io.puts format_string "Failing Scenarios:", :failed
32
+ scenarios.select(&with_status(:failed)).sort_by(&:file_colon_line).each do |scenario|
33
+ io.puts format_string(scenario.file_colon_line, :failed) + format_string(" # Scenario: " + scenario.name, :comment)
34
+ end
35
+ io.puts
36
+ end
37
+
38
+ def print_failures(collection, label)
39
+ failures = collection.select(&with_status(:failed))
40
+ print_elements failures, :failed, pluralize(label, failures.size)
41
+ end
42
+
43
+ def print_counts(label, collection)
44
+ io.puts pluralize(label, collection.size) + count_summary(collection)
45
+ end
46
+
47
+ def pluralize(word, number)
48
+ "#{number} #{number == 1 ? word : word + 's'}"
49
+ end
50
+
51
+ def with_status(status)
52
+ proc {|r| r.status == status}
53
+ end
54
+
55
+ def count_summary(results)
56
+ return "" unless results.any?
57
+ status_counts = STATUSES.map do |status|
58
+ count = results.select(&with_status(status)).size
59
+ format_string "#{count} #{status}", status if count > 0
60
+ end.compact.join ", "
61
+
62
+ " (#{status_counts})"
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,83 @@
1
+ require 'net/http'
2
+ require 'pathname'
3
+
4
+ module Flatware
5
+ module Formatters
6
+ class Http
7
+ attr_reader :out, :client, :server_pid
8
+ def initialize(out, err)
9
+ @out = out
10
+ @client = Client.new
11
+ end
12
+
13
+ def jobs(jobs)
14
+ client.send_message [:jobs, jobs.map {|job| {id: job.id}}]
15
+ end
16
+
17
+ def started(job)
18
+ client.send_message [:started, job: job.id, worker: job.worker]
19
+ end
20
+
21
+ def finished(job)
22
+ client.send_message [:finished, job: job.id, worker: job.worker]
23
+ end
24
+
25
+ def progress(result)
26
+ client.send_message [:progress, status: result.progress, worker: result.worker]
27
+ end
28
+
29
+ def summarize(steps, scenarios)
30
+ client.send_message [
31
+ :summarize, {
32
+ steps: steps.map(&method(:step_as_json)),
33
+ scenarios: scenarios.map(&method(:scenario_as_json))
34
+ }
35
+ ]
36
+ end
37
+
38
+ private
39
+
40
+ def step_as_json(step)
41
+ { status: step.status }.tap do |h|
42
+ h.merge(exception: {
43
+ class: step.exception.class,
44
+ message: step.exception.message,
45
+ backtrace: step.exception.backtrace
46
+ }) if step.exception
47
+ end
48
+ end
49
+
50
+ def scenario_as_json(scenario)
51
+ {
52
+ status: scenario.status,
53
+ file_colon_line: scenario.file_colon_line,
54
+ name: scenario.name
55
+ }
56
+ end
57
+
58
+ class Client
59
+ attr_reader :uri
60
+ include Net
61
+
62
+ def initialize
63
+ @uri = URI ENV['FLATWARE_URL']
64
+ end
65
+
66
+ def send_message(message)
67
+ send_request uri.path, JSON.dump(message)
68
+ end
69
+
70
+ private
71
+
72
+ def send_request(path, body=nil)
73
+ req = HTTP::Post.new path
74
+ req.body = body
75
+ res = HTTP.start uri.hostname, uri.port do |http|
76
+ http.request req
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,25 @@
1
+ require 'flatware/processor_info'
2
+ module Flatware
3
+ extend self
4
+ # All the pids of all the processes called flatware on this machine
5
+ def pids
6
+ pids_command.map do |row|
7
+ row =~ /(\d+).*flatware/ and $1.to_i
8
+ end.compact
9
+ end
10
+
11
+ def pids_command
12
+ case ProcessorInfo.operating_system
13
+ when 'Darwin'
14
+ `ps -c -opid,pgid,command`
15
+ when 'Linux'
16
+ `ps -opid,pgid,command`
17
+ end.split("\n")[1..-1]
18
+ end
19
+
20
+ def pids_of_group(group_pid)
21
+ pids_command.map(&:split).map do |pid, pgid, _|
22
+ pid.to_i if pgid.to_i == group_pid
23
+ end.compact
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ module Flatware
2
+ class Poller
3
+ attr_reader :sockets, :zmq_poller
4
+ def initialize(*sockets)
5
+ @sockets = sockets
6
+ @zmq_poller = ZMQ::Poller.new
7
+ register_sockets
8
+ end
9
+
10
+ def each(&block)
11
+ while (result = zmq_poller.poll) != 0
12
+ raise Error, ZMQ::Util.error_string, caller if result == -1
13
+ for socket in zmq_poller.readables.map &find_wrapped_socket
14
+ yield socket
15
+ end
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def find_wrapped_socket
22
+ ->(s) do
23
+ sockets.find do |socket|
24
+ socket.s == s
25
+ end
26
+ end
27
+ end
28
+
29
+ def register_sockets
30
+ sockets.each do |socket|
31
+ zmq_poller.register_readable socket.s
32
+ end
33
+ end
34
+ end
35
+ end
@@ -16,5 +16,9 @@ module Flatware
16
16
  def self.count
17
17
  new.count
18
18
  end
19
+
20
+ def self.operating_system
21
+ new.operating_system
22
+ end
19
23
  end
20
24
  end
@@ -1,9 +1,10 @@
1
1
  module Flatware
2
2
  class Result
3
- attr_reader :progress, :steps
3
+ attr_reader :progress, :worker
4
4
 
5
- def initialize(progress, steps=nil)
6
- @progress, @steps = progress, steps || []
5
+ def initialize(progress)
6
+ @progress = progress
7
+ @worker = ENV['TEST_ENV_NUMBER'].to_i
7
8
  end
8
9
 
9
10
  class << self
@@ -13,7 +14,7 @@ module Flatware
13
14
  end
14
15
 
15
16
  def status(status)
16
- new Cucumber::ProgressString.format status
17
+ new status
17
18
  end
18
19
 
19
20
  def background(status, exception)
@@ -1,24 +1,22 @@
1
+ require 'forwardable'
1
2
  module Flatware
2
3
  class ScenarioDecorator
3
- attr_reader :status
4
+ extend Forwardable
5
+ def_delegators :scenario, :name, :file_colon_line
4
6
 
5
- def initialize(scenario)
6
- @scenario, @status = scenario, scenario.status
7
- @scenario = scenario.scenario_outline if example_row?
8
- end
9
-
10
- def name
11
- @scenario.name
12
- end
7
+ attr_reader :status, :exception
13
8
 
14
- def file_colon_line
15
- @scenario.file_colon_line
9
+ def initialize(scenario)
10
+ @scenario, @status, @exception = scenario, scenario.status, scenario.exception
11
+ @scenario, @exception = scenario.scenario_outline, scenario.exception if example_row?
16
12
  end
17
13
 
18
14
  private
19
15
 
16
+ attr_reader :scenario
17
+
20
18
  def example_row?
21
- @scenario.respond_to? :scenario_outline
19
+ scenario.respond_to? :scenario_outline
22
20
  end
23
21
  end
24
22
  end
@@ -1,10 +1,13 @@
1
+ require 'flatware/serialized_exception'
1
2
  module Flatware
2
3
  class ScenarioResult
3
4
  attr_reader :status, :file_colon_line, :name
4
- def initialize(status, file_colon_line, name)
5
+ def initialize(status, file_colon_line, name, e)
5
6
  @status = status
6
7
  @file_colon_line = file_colon_line
7
8
  @name = name
9
+ @exception = SerializedException.new(e.class, e.message, e.backtrace) if e
10
+ @failed_outside_step = false
8
11
  end
9
12
 
10
13
  def passed?
@@ -14,5 +17,20 @@ module Flatware
14
17
  def failed?
15
18
  status == :failed
16
19
  end
20
+
21
+ def failed_outside_step!(file_colon_line)
22
+ @failed_outside_step = file_colon_line
23
+ end
24
+
25
+ def failed_outside_step?
26
+ !!@failed_outside_step
27
+ end
28
+
29
+ def exception
30
+ @exception.tap do |e|
31
+ e.backtrace = e.backtrace.grep(Regexp.new(Dir.pwd)).map { |line| line[Dir.pwd.size..-1] }
32
+ e.backtrace = e.backtrace + [@failed_outside_step] if failed_outside_step?
33
+ end
34
+ end
17
35
  end
18
36
  end
@@ -0,0 +1,20 @@
1
+ module Flatware
2
+ class SerializedException
3
+ attr_reader :class, :message
4
+ attr_accessor :backtrace
5
+ def initialize(klass, message, backtrace)
6
+ @class, @message, @backtrace = serialized(klass), message, backtrace
7
+ end
8
+
9
+ private
10
+ def serialized(klass)
11
+ SerializedClass.new(klass.to_s)
12
+ end
13
+
14
+ class SerializedClass
15
+ attr_reader :name
16
+ alias to_s name
17
+ def initialize(name); @name = name end
18
+ end
19
+ end
20
+ end
data/lib/flatware/sink.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'flatware'
2
- require 'flatware/cucumber/formatter'
3
2
  module Flatware
4
3
  class Sink
5
4
  PORT = 'ipc://sink'
@@ -8,22 +7,28 @@ module Flatware
8
7
  client.push message
9
8
  end
10
9
 
11
- def finished(job)
12
- push job
13
- end
14
-
15
10
  def start_server(*args)
16
11
  Server.new(*args).start
17
12
  end
18
13
 
14
+ private
15
+
19
16
  def client
20
17
  @client ||= Client.new
21
18
  end
22
19
  end
23
20
 
21
+ %w[finished started progress checkpoint].each do |message|
22
+ define_singleton_method message do |content|
23
+ push [message.to_sym, content]
24
+ end
25
+ end
26
+
24
27
  class Server
25
- def initialize(jobs, out=$stdout, error=$stderr, fail_fast=false)
26
- @jobs, @out, @error, @fail_fast = jobs, out, error, fail_fast
28
+ def initialize(jobs, formatter, options={})
29
+ @jobs, @formatter = jobs, formatter
30
+ options = {fail_fast: false}.merge options
31
+ @fail_fast = options[:fail_fast]
27
32
  end
28
33
 
29
34
  def start
@@ -33,30 +38,33 @@ module Flatware
33
38
  exit 1
34
39
  end
35
40
 
36
- before_firing { listen }
41
+ Flatware::Fireable::bind
42
+ formatter.jobs jobs
43
+ listen
44
+ ensure
45
+ Flatware::Fireable::kill
37
46
  Flatware.close
38
47
  end
39
48
 
40
49
  def checkpoint_handler
41
- @checkpoint_handler ||= CheckpointHandler.new(out, fail_fast?)
50
+ @checkpoint_handler ||= CheckpointHandler.new(formatter, fail_fast?)
42
51
  end
43
52
 
44
53
  def listen
45
54
  until done?
46
- result = socket.recv
47
- case result
48
- when Result
49
- print result.progress
50
- when Checkpoint
51
- checkpoint_handler.handle! result
52
- when Job
53
- completed_jobs << result
54
- log "COMPLETED SCENARIO"
55
+ message, content = socket.recv
56
+ case message
57
+ when :checkpoint
58
+ checkpoint_handler.handle! content
59
+ when :finished
60
+ completed_jobs << content
61
+ formatter.finished content
55
62
  else
56
- log "i don't know that message, bro.", result
63
+ formatter.send message, content
57
64
  end
58
65
  end
59
66
  checkpoint_handler.summarize
67
+ !checkpoint_handler.had_failures?
60
68
  rescue Error => e
61
69
  raise unless e.message == "Interrupted system call"
62
70
  end
@@ -67,41 +75,22 @@ module Flatware
67
75
  @fail_fast
68
76
  end
69
77
 
70
- attr_reader :out, :jobs
71
-
72
- def print(*args)
73
- out.print *args
74
- end
75
-
76
- def puts(*args)
77
- out.puts *args
78
- end
78
+ attr_reader :jobs, :formatter
79
79
 
80
80
  def summarize_remaining
81
81
  return if remaining_work.empty?
82
- puts
83
- puts "The following features have not been run:"
84
- for job in remaining_work
85
- puts job.id
86
- end
82
+ formatter.summarize_remaining remaining_work
87
83
  end
88
84
 
89
85
  def log(*args)
90
86
  Flatware.log *args
91
87
  end
92
88
 
93
- def before_firing(&block)
94
- Flatware::Fireable::bind
95
- block.call
96
- Flatware::Fireable::kill
97
- end
98
-
99
89
  def completed_jobs
100
90
  @completed_jobs ||= []
101
91
  end
102
92
 
103
93
  def done?
104
- log remaining_work
105
94
  remaining_work.empty? || checkpoint_handler.done?
106
95
  end
107
96