flatware-rc 1.0.0.rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +132 -0
  4. data/bin/flatware +6 -0
  5. data/lib/flatware.rb +10 -0
  6. data/lib/flatware/checkpoint_handler.rb +34 -0
  7. data/lib/flatware/cli.rb +89 -0
  8. data/lib/flatware/cucumber.rb +34 -0
  9. data/lib/flatware/cucumber/checkpoint.rb +28 -0
  10. data/lib/flatware/cucumber/formatter.rb +112 -0
  11. data/lib/flatware/cucumber/result.rb +27 -0
  12. data/lib/flatware/cucumber/runtime.rb +36 -0
  13. data/lib/flatware/cucumber/scenario_decorator.rb +24 -0
  14. data/lib/flatware/cucumber/scenario_result.rb +38 -0
  15. data/lib/flatware/cucumber/step_result.rb +29 -0
  16. data/lib/flatware/formatters.rb +26 -0
  17. data/lib/flatware/formatters/cucumber/console.rb +48 -0
  18. data/lib/flatware/formatters/cucumber/console/summary.rb +66 -0
  19. data/lib/flatware/formatters/cucumber/http.rb +83 -0
  20. data/lib/flatware/formatters/rspec/console.rb +33 -0
  21. data/lib/flatware/pids.rb +25 -0
  22. data/lib/flatware/poller.rb +35 -0
  23. data/lib/flatware/processor_info.rb +24 -0
  24. data/lib/flatware/rspec.rb +28 -0
  25. data/lib/flatware/rspec/checkpoint.rb +29 -0
  26. data/lib/flatware/rspec/example_notification.rb +21 -0
  27. data/lib/flatware/rspec/examples_notification.rb +24 -0
  28. data/lib/flatware/rspec/formatter.rb +50 -0
  29. data/lib/flatware/rspec/summary.rb +40 -0
  30. data/lib/flatware/serialized_exception.rb +24 -0
  31. data/lib/flatware/sink.rb +105 -0
  32. data/lib/flatware/sink/client.rb +25 -0
  33. data/lib/flatware/socket.rb +181 -0
  34. data/lib/flatware/version.rb +3 -0
  35. data/lib/flatware/worker.rb +57 -0
  36. metadata +170 -0
@@ -0,0 +1,27 @@
1
+ module Flatware
2
+ module Cucumber
3
+ class Result
4
+ attr_reader :progress, :worker
5
+
6
+ def initialize(progress)
7
+ @progress = progress
8
+ @worker = ENV['TEST_ENV_NUMBER'].to_i
9
+ end
10
+
11
+ class << self
12
+ def step(*args)
13
+ step = StepResult.new *args
14
+ new step.progress, [step]
15
+ end
16
+
17
+ def status(status)
18
+ new status
19
+ end
20
+
21
+ def background(status, exception)
22
+ new '', [StepResult.new(status, exception)]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ require 'cucumber'
2
+
3
+ module Flatware
4
+ module Cucumber
5
+ class Runtime < ::Cucumber::Runtime
6
+
7
+ attr_accessor :configuration, :loader
8
+ attr_reader :out, :err
9
+ attr_reader :visitor
10
+
11
+ def initialize(out=StringIO.new, err=out)
12
+ @out, @err = out, err
13
+ super(default_configuration)
14
+ load_step_definitions
15
+ @results = Results.new(configuration)
16
+ end
17
+
18
+ def default_configuration
19
+ config = ::Cucumber::Cli::Configuration.new
20
+ config.parse! []
21
+ config
22
+ end
23
+
24
+ def run(feature_files=[], options=[])
25
+ @loader = nil
26
+ options = Array(feature_files) + %w[--format Flatware::Cucumber::Formatter] + options
27
+
28
+ configure(::Cucumber::Cli::Main.new(options, out, err).configuration)
29
+
30
+ self.visitor = configuration.build_tree_walker(self)
31
+ visitor.visit_features(features)
32
+ results
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ require 'forwardable'
2
+ module Flatware
3
+ module Cucumber
4
+ class ScenarioDecorator
5
+ extend Forwardable
6
+ def_delegators :scenario, :name, :file_colon_line
7
+
8
+ attr_reader :status, :exception
9
+
10
+ def initialize(scenario)
11
+ @scenario, @status, @exception = scenario, scenario.status, scenario.exception
12
+ @scenario, @exception = scenario.scenario_outline, scenario.exception if example_row?
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :scenario
18
+
19
+ def example_row?
20
+ scenario.respond_to? :scenario_outline
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ require 'flatware/serialized_exception'
2
+ module Flatware
3
+ module Cucumber
4
+ class ScenarioResult
5
+ attr_reader :status, :file_colon_line, :name
6
+ def initialize(status, file_colon_line, name, e)
7
+ @status = status
8
+ @file_colon_line = file_colon_line
9
+ @name = name
10
+ @exception = SerializedException.new(e.class, e.message, e.backtrace) if e
11
+ @failed_outside_step = false
12
+ end
13
+
14
+ def passed?
15
+ status == :passed
16
+ end
17
+
18
+ def failed?
19
+ status == :failed
20
+ end
21
+
22
+ def failed_outside_step!(file_colon_line)
23
+ @failed_outside_step = file_colon_line
24
+ end
25
+
26
+ def failed_outside_step?
27
+ !!@failed_outside_step
28
+ end
29
+
30
+ def exception
31
+ @exception.tap do |e|
32
+ e.backtrace = e.backtrace.grep(Regexp.new(Dir.pwd)).map { |line| line[Dir.pwd.size..-1] }
33
+ e.backtrace = e.backtrace + [@failed_outside_step] if failed_outside_step?
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ require 'flatware/serialized_exception'
2
+ module Flatware
3
+ module Cucumber
4
+ class StepResult
5
+ attr_reader :status, :exception
6
+
7
+ def initialize(status, exception)
8
+ @status, @exception = status, serialized(exception)
9
+ end
10
+
11
+ def passed?
12
+ status == :passed
13
+ end
14
+
15
+ def failed?
16
+ status == :failed
17
+ end
18
+
19
+ def progress
20
+ Cucumber::ProgressString.format(status)
21
+ end
22
+
23
+ private
24
+ def serialized(e)
25
+ SerializedException.new(e.class, e.message, e.backtrace) if e
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ module Flatware
2
+ module Formatters
3
+ def self.load_by_name(runner, names)
4
+ formatters = names.map do |name|
5
+ namespace = const_get({rspec: 'RSpec', cucumber: 'Cucumber'}.fetch(runner))
6
+ klass = namespace.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/cucumber/console/summary'
2
+ require 'cucumber/formatter/console'
3
+
4
+ module Flatware::Formatters::Cucumber
5
+ class Console
6
+ #for format_string
7
+ include ::Cucumber::Formatter::Console
8
+
9
+ FORMATS = {
10
+ passed: '.',
11
+ failed: 'F',
12
+ undefined: 'U',
13
+ pending: 'P',
14
+ skipped: '-'
15
+ }
16
+
17
+ STATUSES = FORMATS.keys
18
+
19
+ attr_reader :out, :err
20
+
21
+ def initialize(stdout, stderr)
22
+ @out, @err = stdout, stderr
23
+ end
24
+
25
+ def progress(result)
26
+ out.print format result.progress
27
+ end
28
+
29
+ def summarize(checkpoints)
30
+ steps = checkpoints.flat_map(&:steps)
31
+ scenarios = checkpoints.flat_map(&:scenarios)
32
+ Summary.new(steps, scenarios, out).summarize
33
+ end
34
+
35
+ def summarize_remaining(remaining_jobs)
36
+ out.puts
37
+ out.puts "The following features have not been run:"
38
+ for job in remaining_jobs
39
+ out.puts job.id
40
+ end
41
+ end
42
+
43
+ private
44
+ def format(status)
45
+ format_string FORMATS[status], status
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,66 @@
1
+ require 'cucumber/formatter/console'
2
+ require 'flatware/formatters/cucumber/console'
3
+ require 'flatware/cucumber/checkpoint'
4
+
5
+ module Flatware::Formatters::Cucumber
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
@@ -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,33 @@
1
+ module Flatware::Formatters::RSpec
2
+ class Console
3
+ attr_reader :formatter
4
+
5
+ def initialize(out, err)
6
+ ::RSpec::configuration.tty = true
7
+ ::RSpec::configuration.color = true
8
+ @formatter = ::RSpec::Core::Formatters::ProgressFormatter.new(out)
9
+ end
10
+
11
+ def progress(result)
12
+ formatter.send(message_for(result),nil)
13
+ end
14
+
15
+ def summarize(checkpoints)
16
+ result = checkpoints.reduce :+
17
+ if result
18
+ formatter.dump_failures result
19
+ formatter.dump_summary result.summary
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def message_for(result)
26
+ {
27
+ passed: :example_passed,
28
+ failed: :example_failed,
29
+ pending: :example_pending
30
+ }.fetch result.progress
31
+ end
32
+ end
33
+ end
@@ -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