paraspec 0.0.1

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.
@@ -0,0 +1,82 @@
1
+ require 'rspec/core'
2
+
3
+ module Paraspec
4
+ class WorkerFormatter
5
+ RSpec::Core::Formatters.register self,
6
+ #:start,
7
+ :example_group_started,
8
+ :example_started,
9
+ :example_passed,
10
+ :example_failed,
11
+ :example_pending,
12
+ :message,
13
+ #:stop,
14
+ :start_dump,
15
+ :dump_pending,
16
+ :dump_failures,
17
+ :dump_summary,
18
+ :seed,
19
+ :close
20
+
21
+ def initialize(output)
22
+ @master_client = output.master_client
23
+ end
24
+
25
+ def start(notification)
26
+ # Worker formatter receives start notification for each example
27
+ # that it receives from supervisor.
28
+ # The start notification contains number of examples to be executed,
29
+ # and the load time.
30
+ # The number of examples isn't useful because this is a subset of
31
+ # all of the examples to be run, and we can't wait to receive
32
+ # start notifications from all workers in the master thus
33
+ # start notifications are not aggregatable.
34
+ # Loading time is something that master can figure out on its own.
35
+ # Therefore we do not forward start notifications to master.
36
+ # At the same time master must create and send start notifications
37
+ # to its own formatters, for example the junit formatter
38
+ # requires a start notification for the summary report to work.
39
+ end
40
+
41
+ def stop(notification)
42
+ # Stop notification doesn't carry any new information, thus
43
+ # master directly invokes reporter.stop from dump_summary.
44
+ end
45
+
46
+ def dump_summary(notification)
47
+ end
48
+
49
+ def method_missing(m, args)
50
+ end
51
+
52
+ def example_started(notification)
53
+ spec = {
54
+ file_path: notification.example.metadata[:file_path],
55
+ scoped_id: notification.example.metadata[:scoped_id],
56
+ }
57
+ @master_client.request('notify-example-started', spec: spec)
58
+ end
59
+
60
+ def example_passed(notification)
61
+ example_notification(notification)
62
+ end
63
+
64
+ def example_notification(notification)
65
+ spec = {
66
+ file_path: notification.example.metadata[:file_path],
67
+ scoped_id: notification.example.metadata[:scoped_id],
68
+ }
69
+ execution_result = notification.example.execution_result
70
+ @master_client.request('example-passed',
71
+ spec: spec, result: execution_result)
72
+ end
73
+
74
+ def example_failed(notification)
75
+ example_notification(notification)
76
+ end
77
+
78
+ def example_pending(notification)
79
+ example_notification(notification)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,67 @@
1
+ module Paraspec
2
+ # An RSpec test runner - in a worker process.
3
+ # This runner collects results and output and forwards them to the
4
+ # master process via DRb.
5
+ class WorkerRunner
6
+ def initialize(options={})
7
+ @master_client = options[:master_client]
8
+
9
+ # TODO capture stdout & stderr
10
+ class << STDERR
11
+ def master_client
12
+ @master_client
13
+ end
14
+ end
15
+ STDERR.send(:instance_variable_set, '@master_client', @master_client)
16
+ runner.setup(STDOUT, STDERR)
17
+ end
18
+
19
+ def run(spec)
20
+ if RSpecFacade.all_example_groups.count == 0
21
+ raise "No example groups loaded"
22
+ end
23
+ group = RSpecFacade.all_example_groups.detect do |g|
24
+ g.metadata[:file_path] == spec[:file_path] &&
25
+ g.metadata[:scoped_id] == spec[:scoped_id]
26
+ end
27
+ unless group
28
+ puts "No example group for #{spec.inspect}, #{RSpecFacade.all_example_groups.count} total groups"
29
+ #byebug
30
+ raise "No example group for #{spec.inspect}"
31
+ end
32
+ examples = group.examples
33
+ #Paraspec.logger.debug_state("Spec #{spec}: #{examples.length} examples")
34
+ return if examples.empty?
35
+ ids = examples.map { |e| e.metadata[:scoped_id] }
36
+ RSpec.configuration.send(:instance_variable_set, '@filter_manager', RSpec::Core::FilterManager.new)
37
+ RSpec.configuration.filter_manager.add_ids(spec[:file_path], ids)
38
+ RSpec.world.filter_examples
39
+ examples = RSpec.configuration.filter_manager.prune(examples)
40
+ return if examples.empty?
41
+ # It is important to run the entire world here because if
42
+ # a particular example group is run, before/after :all hooks
43
+ # aren't always run
44
+ RSpec.world.ordered_example_groups.each do |group|
45
+ group.reset_memoized
46
+ end
47
+ # Hack to not run examples from each group each time I want to run
48
+ # a single example. It seems that rspec performs filtering by file
49
+ # at one time and by expressions/scoped id at a different time,
50
+ # because simply requesting filtering by scoped id makes rspec
51
+ # include examples from all other files (it also mutates the filters
52
+ # when querying them for example groups... ugh)
53
+ run_example_groups = RSpec.world.ordered_example_groups.select do |c_group|
54
+ c_group.metadata[:file_path] == spec[:file_path]
55
+ end
56
+ runner.run_specs(run_example_groups)
57
+ end
58
+
59
+ private def runner
60
+ @runner ||= begin
61
+ options = RSpec::Core::ConfigurationOptions.new(ARGV)
62
+ options.options[:formatters] = [['Paraspec::WorkerFormatter']]
63
+ RSpec::Core::Runner.new(options)
64
+ end
65
+ end
66
+ end
67
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paraspec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Oleg Pudeyev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 3.7.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 3.7.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: childprocess
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: hashie
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.5.7
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.5.7
55
+ - !ruby/object:Gem::Dependency
56
+ name: msgpack
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.4
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.2.4
69
+ description: Parallel RSpec runner
70
+ email: oleg@olegp.name
71
+ executables:
72
+ - paraspec
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE
77
+ - README.md
78
+ - bin/paraspec
79
+ - lib/paraspec.rb
80
+ - lib/paraspec/drb_helpers.rb
81
+ - lib/paraspec/http_client.rb
82
+ - lib/paraspec/http_server.rb
83
+ - lib/paraspec/ipc.rb
84
+ - lib/paraspec/logger.rb
85
+ - lib/paraspec/master.rb
86
+ - lib/paraspec/master_runner.rb
87
+ - lib/paraspec/msgpack_client.rb
88
+ - lib/paraspec/msgpack_helpers.rb
89
+ - lib/paraspec/msgpack_server.rb
90
+ - lib/paraspec/process_helpers.rb
91
+ - lib/paraspec/rspec_facade.rb
92
+ - lib/paraspec/rspec_patches.rb
93
+ - lib/paraspec/supervisor.rb
94
+ - lib/paraspec/version.rb
95
+ - lib/paraspec/worker.rb
96
+ - lib/paraspec/worker_formatter.rb
97
+ - lib/paraspec/worker_runner.rb
98
+ homepage: https://github.com/paraspec/paraspec
99
+ licenses:
100
+ - MIT
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options:
104
+ - "--charset=UTF-8"
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 1.9.3
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.7.3
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: paraspec-0.0.1
123
+ test_files: []