flatware 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 004764c1a65d325a871a4e5dc10959436dbe7b11
4
+ data.tar.gz: 6fde0c535fa7868237c65bc066d449db2fe506ed
5
+ SHA512:
6
+ metadata.gz: ccdef8b34f2425936014ad0fbb9a53bad94c29df3c72f770e46aae3d75cafcd9659e8c1533624783667c4238f1a1a0fedaaf82ce2595f3d0bed409ec7c542ad2
7
+ data.tar.gz: 55b5196a2d5a08231d5440bc683c249b3508356666f719bfa566302b558978e5c3c5fdeae35e6c801800913de0c8d084818ceffe7db29c43dae2a8aa97519f19
data/lib/flatware.rb CHANGED
@@ -1,86 +1,17 @@
1
- require 'forwardable'
2
- require 'ffi-rzmq'
3
-
4
1
  module Flatware
2
+ require 'flatware/checkpoint'
5
3
  require 'flatware/checkpoint_handler'
6
4
  require 'flatware/processor_info'
7
5
  require 'flatware/cli'
8
6
  require 'flatware/cucumber'
9
7
  require 'flatware/dispatcher'
10
8
  require 'flatware/fireable'
9
+ require 'flatware/formatters'
11
10
  require 'flatware/result'
11
+ require 'flatware/scenario_decorator'
12
12
  require 'flatware/scenario_result'
13
13
  require 'flatware/sink'
14
+ require 'flatware/socket'
14
15
  require 'flatware/step_result'
15
- require 'flatware/summary'
16
16
  require 'flatware/worker'
17
- require 'flatware/scenario_decorator'
18
-
19
- Error = Class.new StandardError
20
-
21
- Job = Struct.new :id, :args
22
-
23
- extend self
24
- def socket(type, options={})
25
- Socket.new(context.socket(type)).tap do |socket|
26
- sockets.push socket
27
- if port = options[:connect]
28
- socket.connect port
29
- end
30
- if port = options[:bind]
31
- socket.bind port
32
- end
33
- #FIXME: figure out how to do this without waiting
34
- sleep 0.05
35
- end
36
- end
37
-
38
- def close
39
- sockets.each &:close
40
- context.terminate
41
- @context = nil
42
- end
43
-
44
- def log(*message)
45
- if verbose?
46
- $stderr.print "#{$$} "
47
- $stderr.puts *message
48
- end
49
- end
50
-
51
- attr_writer :verbose
52
- def verbose?
53
- !!@verbose
54
- end
55
-
56
- private
57
- def context
58
- @context ||= ZMQ::Context.new
59
- end
60
-
61
- def sockets
62
- @sockets ||= []
63
- end
64
-
65
- Socket = Struct.new :s do
66
- extend Forwardable
67
- def_delegators :s, :bind, :connect, :setsockopt
68
- def send(message)
69
- result = s.send_string(Marshal.dump(message))
70
- raise Error, ZMQ::Util.error_string, caller if result == -1
71
- message
72
- end
73
-
74
- def close
75
- s.setsockopt(ZMQ::LINGER, 1)
76
- s.close
77
- end
78
-
79
- def recv
80
- message = ''
81
- result = s.recv_string(message)
82
- raise Error, ZMQ::Util.error_string, caller if result == -1
83
- Marshal.load message
84
- end
85
- end
86
17
  end
@@ -20,7 +20,7 @@ module Flatware
20
20
 
21
21
  def serialize_scenarios(scenarios)
22
22
  scenarios.map do |scenario|
23
- ScenarioResult.new scenario.status, scenario.file_colon_line, scenario.name
23
+ ScenarioResult.new scenario.status, scenario.file_colon_line, scenario.name, scenario.exception
24
24
  end
25
25
  end
26
26
 
@@ -1,14 +1,16 @@
1
1
  module Flatware
2
2
  class CheckpointHandler
3
- def initialize(out, fails_fast)
3
+ attr_reader :formatter, :checkpoints
4
+
5
+ def initialize(formatter, fails_fast)
4
6
  @fail_fast = fails_fast
5
- @out = out
7
+ @formatter = formatter
6
8
  @checkpoints = []
7
9
  end
8
10
 
9
11
  def handle!(checkpoint)
10
- @checkpoints << checkpoint
11
- if checkpoint.failures? && @fail_fast
12
+ checkpoints << checkpoint
13
+ if checkpoint.failures? && fail_fast?
12
14
  Fireable::kill # Killing everybody
13
15
  @done = true
14
16
  end
@@ -18,10 +20,26 @@ module Flatware
18
20
  @done
19
21
  end
20
22
 
23
+ def fail_fast?
24
+ @fail_fast
25
+ end
26
+
21
27
  def summarize
22
- steps = @checkpoints.map(&:steps).flatten
23
- scenarios = @checkpoints.map(&:scenarios).flatten
24
- Summary.new(steps, scenarios, @out).summarize
28
+ formatter.summarize(steps, scenarios)
29
+ end
30
+
31
+ def had_failures?
32
+ checkpoints.any? &:failures?
33
+ end
34
+
35
+ private
36
+
37
+ def steps
38
+ checkpoints.map(&:steps).flatten
39
+ end
40
+
41
+ def scenarios
42
+ checkpoints.map(&:scenarios).flatten
25
43
  end
26
44
  end
27
45
  end
data/lib/flatware/cli.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'thor'
2
+ require 'flatware/pids'
2
3
  module Flatware
3
4
  class CLI < Thor
4
5
 
@@ -21,22 +22,23 @@ module Flatware
21
22
 
22
23
  worker_option
23
24
  method_option 'fail-fast', type: :boolean, default: false, desc: "Abort the run on first failure"
25
+ method_option 'formatters', aliases: "-f", type: :array, default: %w[console], desc: "The formatters to use for output"
24
26
  desc "[FLATWARE_OPTS] cucumber [CUCUMBER_ARGS]", "parallelizes cucumber with custom arguments"
25
27
  def cucumber(*)
28
+ Process.setpgrp
26
29
  Flatware.verbose = options[:log]
27
- Worker.spawn workers
28
30
  log "flatware options:", options
29
31
  log "cucumber options:", cucumber_args
30
32
  jobs = Cucumber.extract_jobs_from_args cucumber_args
33
+ Worker.spawn workers
31
34
  fork do
32
- log "dispatch"
33
35
  $0 = 'flatware dispatcher'
34
36
  Dispatcher.start jobs
35
37
  end
36
- log "bossman"
37
38
  $0 = 'flatware sink'
38
- Sink.start_server jobs, $stdout, $stderr, options['fail-fast']
39
+ passed = Sink.start_server jobs, Formatters.load_by_name(options['formatters']), fail_fast: options['fail-fast']
39
40
  Process.waitall
41
+ exit passed ? 0 : 1
40
42
  end
41
43
 
42
44
  worker_option
@@ -58,9 +60,7 @@ module Flatware
58
60
 
59
61
  desc "clear", "kills all flatware processes"
60
62
  def clear
61
- `ps -c -opid,command`.split("\n").map do |row|
62
- row =~ /(\d+).*flatware/ and $1.to_i
63
- end.compact.each do |pid|
63
+ (Flatware.pids - [$$]).each do |pid|
64
64
  Process.kill 6, pid
65
65
  end
66
66
  end
@@ -1,19 +1,7 @@
1
1
  require 'cucumber'
2
- require_relative 'cucumber/runtime'
2
+ require 'flatware/cucumber/runtime'
3
3
  module Flatware
4
4
  module Cucumber
5
- autoload :Formatter, 'flatware/cucumber/formatter'
6
- autoload :ProgressString, 'flatware/cucumber/formatter'
7
-
8
- FORMATS = {
9
- :passed => '.',
10
- :failed => 'F',
11
- :undefined => 'U',
12
- :pending => 'P',
13
- :skipped => '-'
14
- }
15
-
16
- STATUSES = FORMATS.keys
17
5
 
18
6
  extend self
19
7
 
@@ -1,15 +1,26 @@
1
- require 'cucumber/formatter/console'
2
1
  require 'flatware/checkpoint'
3
2
  require 'flatware/scenario_decorator'
3
+ require 'flatware/sink'
4
+ require 'ostruct'
4
5
  module Flatware
5
6
  module Cucumber
6
7
  class Formatter
8
+
7
9
  def initialize(step_mother, *)
8
10
  @collector = Collector.new step_mother
11
+ @scenarios = []
12
+ @in_a_step = false
9
13
  end
10
14
 
11
15
  def after_features(*)
12
- Sink.push collector.checkpoint
16
+ checkpoint = collector.checkpoint
17
+ scenarios.select(&:exception).map(&:file_colon_line).each do |file_colon_line|
18
+ scenario = checkpoint.scenarios.detect do |scenario|
19
+ scenario.file_colon_line == file_colon_line
20
+ end
21
+ scenario.failed_outside_step!(file_colon_line) if scenario
22
+ end
23
+ Sink.checkpoint checkpoint
13
24
  end
14
25
 
15
26
  def after_step_result(_, _, _, status, *)
@@ -20,11 +31,58 @@ module Flatware
20
31
  send_progress(status) if status
21
32
  end
22
33
 
34
+ def exception(exception, status)
35
+ unless @in_a_step
36
+ current_scenario.exception = exception
37
+ send_progress(status)
38
+ end
39
+ end
40
+
41
+ def respond_to?(x)
42
+ super
43
+ end
44
+
45
+ def before_outline_table(*)
46
+ @in_examples = true
47
+ end
48
+
49
+ def after_outline_table(*)
50
+ @in_examples = false
51
+ end
52
+
53
+ def before_table_cell(*)
54
+ @in_a_step = @in_examples
55
+ end
56
+
57
+ def after_table_cell(*)
58
+ @in_a_step = ! @in_examples
59
+ end
60
+
61
+ def after_table_row(table_row)
62
+ exception(table_row.exception, :failed) if table_row.exception
63
+ end
64
+
65
+ def before_step(*)
66
+ @in_a_step = true
67
+ end
68
+
69
+ def after_step(*)
70
+ @in_a_step = false
71
+ end
72
+
73
+ def scenario_name(_, name, file_colon_line, *)
74
+ scenarios.push OpenStruct.new file_colon_line: file_colon_line, name: name
75
+ end
76
+
23
77
  private
24
- attr_reader :collector
78
+ attr_reader :collector, :scenarios
79
+
80
+ def current_scenario
81
+ scenarios.last
82
+ end
25
83
 
26
84
  def send_progress(status)
27
- Sink.push Result.status status
85
+ Sink.progress Result.new status
28
86
  end
29
87
 
30
88
  class Collector
@@ -52,13 +110,5 @@ module Flatware
52
110
  end
53
111
  end
54
112
  end
55
-
56
- class ProgressString
57
- extend ::Cucumber::Formatter::Console
58
- def self.format(status)
59
- return '' unless status
60
- format_string FORMATS[status], status
61
- end
62
- end
63
113
  end
64
114
  end
@@ -2,18 +2,23 @@ module Flatware
2
2
  class Dispatcher
3
3
  PORT = 'ipc://dispatch'
4
4
 
5
- def self.start(jobs=Cucumber.jobs)
5
+ def self.start(jobs)
6
+ trap 'INT' do
7
+ Flatware.close
8
+ exit 1
9
+ end
6
10
  new(jobs).dispatch!
7
11
  end
8
12
 
9
13
  def initialize(jobs)
10
- @jobs = jobs
14
+ @jobs = jobs
15
+ @fireable = Fireable.new
16
+ @dispatch = Flatware.socket ZMQ::REP, bind: PORT
11
17
  end
12
18
 
13
19
  def dispatch!
14
- return if jobs.empty?
15
20
  fireable.until_fired dispatch do |request|
16
- if job = jobs.pop
21
+ if job = jobs.shift
17
22
  dispatch.send job
18
23
  else
19
24
  dispatch.send 'seppuku'
@@ -23,14 +28,6 @@ module Flatware
23
28
 
24
29
  private
25
30
 
26
- attr_reader :jobs
27
-
28
- def fireable
29
- @fireable ||= Fireable.new
30
- end
31
-
32
- def dispatch
33
- @dispatch ||= Flatware.socket ZMQ::REP, bind: PORT
34
- end
31
+ attr_reader :jobs, :fireable, :dispatch
35
32
  end
36
33
  end
@@ -1,3 +1,5 @@
1
+ require 'flatware/poller'
2
+
1
3
  module Flatware
2
4
  class Fireable
3
5
  PORT = 'ipc://die'
@@ -20,7 +22,8 @@ module Flatware
20
22
 
21
23
  def until_fired(socket, &block)
22
24
  poller = Poller.new socket, die
23
- poller.each do |message|
25
+ poller.each do |s|
26
+ message = s.recv
24
27
  break if message == 'seppuku'
25
28
  block.call message
26
29
  end
@@ -28,23 +31,4 @@ module Flatware
28
31
  Flatware.close
29
32
  end
30
33
  end
31
-
32
- class Poller
33
- attr_reader :sockets
34
- def initialize(*sockets)
35
- @sockets = sockets
36
- end
37
-
38
- def each(&block)
39
- poller = ZMQ::Poller.new
40
- for socket in sockets
41
- poller.register_readable socket.s
42
- end
43
- while poller.poll > 0
44
- poller.readables.each do |s|
45
- block.call Socket.new(s).recv
46
- end
47
- end
48
- end
49
- end
50
34
  end
@@ -0,0 +1,26 @@
1
+ module Flatware
2
+ module Formatters
3
+ def self.load_by_name(names)
4
+ formatters = names.map do |name|
5
+ require "flatware/formatters/#{name}"
6
+ klass = const_get name.capitalize
7
+ klass.new $stdout, $stderr
8
+ end
9
+ Broadcaster.new formatters
10
+ end
11
+ end
12
+
13
+ class Broadcaster
14
+ attr_reader :formatters
15
+
16
+ def initialize(formatters)
17
+ @formatters = formatters
18
+ end
19
+
20
+ def method_missing(name, *args)
21
+ formatters.each do |formatter|
22
+ formatter.send name, *args if formatter.respond_to? name
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ require 'flatware/formatters/console/summary'
2
+ require 'cucumber/formatter/console'
3
+
4
+ module Flatware
5
+ module Formatters
6
+ class Console
7
+ #for format_string
8
+ include ::Cucumber::Formatter::Console
9
+
10
+ FORMATS = {
11
+ passed: '.',
12
+ failed: 'F',
13
+ undefined: 'U',
14
+ pending: 'P',
15
+ skipped: '-'
16
+ }
17
+
18
+ STATUSES = FORMATS.keys
19
+
20
+ attr_reader :out, :err
21
+
22
+ def initialize(stdout, stderr)
23
+ @out, @err = stdout, stderr
24
+ end
25
+
26
+ def progress(result)
27
+ out.print format result.progress
28
+ end
29
+
30
+ def summarize(steps, scenarios)
31
+ Summary.new(steps, scenarios, out).summarize
32
+ end
33
+
34
+ def summarize_remaining(remaining_jobs)
35
+ out.puts
36
+ out.puts "The following features have not been run:"
37
+ for job in remaining_jobs
38
+ out.puts job.id
39
+ end
40
+ end
41
+
42
+ private
43
+ def format(status)
44
+ format_string FORMATS[status], status
45
+ end
46
+ end
47
+ end
48
+ end