rspec-interactive 0.8.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 255c2aca56cb003fad2c6e7e5f72cb75b2ed82f1820edb42294ded182dbdd25c
4
- data.tar.gz: afb9f469028aa3a4388585e6d34e281ce1bae8fe7227e73ba0939f2cf3585255
3
+ metadata.gz: 17f3d7cb90e4271d19e1eb1a835a9c759084d12baf1f7404bedc521d8ff54047
4
+ data.tar.gz: 98688109728bbaa18e0b64339987fc0d2320331095ad09f2761d2162254ad868
5
5
  SHA512:
6
- metadata.gz: abae1515ca7dade3476088e21fef619f1cdda2b3b126f0a19db30b0aabd2f7250bee9874f11cccf5a836d39dd45d7b6a5848003a32637bf2eb925c4d229622dc
7
- data.tar.gz: 90c4118df931f6e30c3af286f8fb9a1a382bd30dfcd3d052480943fa4f305975784c87e51fddba17de4343ef0748169c07831dc52e7f31190efefef516216efa
6
+ metadata.gz: f0fbe89b3308f29db1302f28b265c0374a6b5c586b32b5c2bee2351d035faaf82c5f5d0362bb95158966d5ea9354de404dc126efd7b899d1bb9d2f8bee40edde
7
+ data.tar.gz: a8bbeaf1ca9e8f51b975cde6bf51be7cc5c6a57b1266bc4ee527c1e1ffcf995b9ac8c78ac77fe1a8ca79b937c4c31d712c9d58119ca4283854da8a6c4f78ccc9
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  rspec-interactive-*.gem
2
2
  .rspec_interactive_history
3
3
  .rspec_interactive_config
4
+ .idea
data/Gemfile.lock CHANGED
@@ -1,33 +1,43 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rspec-interactive (0.8.0)
4
+ rspec-interactive (0.9.1)
5
5
  listen
6
6
  pry
7
7
  rspec-core
8
+ rspec-teamcity (= 1.0.0)
8
9
 
9
10
  GEM
10
11
  remote: https://rubygems.org/
11
12
  specs:
12
13
  coderay (1.1.3)
13
14
  diff-lcs (1.4.4)
14
- ffi (1.15.3)
15
- listen (3.5.1)
15
+ ffi (1.15.5)
16
+ listen (3.7.1)
16
17
  rb-fsevent (~> 0.10, >= 0.10.3)
17
18
  rb-inotify (~> 0.9, >= 0.9.10)
18
19
  method_source (1.0.0)
19
20
  pry (0.14.1)
20
21
  coderay (~> 1.1)
21
22
  method_source (~> 1.0)
22
- rb-fsevent (0.11.0)
23
+ rb-fsevent (0.11.1)
23
24
  rb-inotify (0.10.1)
24
25
  ffi (~> 1.0)
26
+ rspec (3.9.0)
27
+ rspec-core (~> 3.9.0)
28
+ rspec-expectations (~> 3.9.0)
29
+ rspec-mocks (~> 3.9.0)
25
30
  rspec-core (3.9.3)
26
31
  rspec-support (~> 3.9.3)
27
32
  rspec-expectations (3.9.4)
28
33
  diff-lcs (>= 1.2.0, < 2.0)
29
34
  rspec-support (~> 3.9.0)
35
+ rspec-mocks (3.9.1)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.9.0)
30
38
  rspec-support (3.9.4)
39
+ rspec-teamcity (1.0.0)
40
+ rspec (>= 2.99, >= 2.14.2, < 4)
31
41
 
32
42
  PLATFORMS
33
43
  x86_64-darwin-20
data/README.md CHANGED
@@ -69,10 +69,10 @@ See: https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#ra
69
69
 
70
70
  ## Usage
71
71
 
72
- Optionally, specify a config file with `--config <config-file>`. Optionally, specify arguments to an initial RSpec invocation with `--initial-rspec-args <initial-rspec-args>`.
72
+ Optionally, specify a config file with `--config <config-file>`.
73
73
 
74
74
  ```shell
75
- bundle exec rspec-interactive [--config <config-file>] [--initial-rspec-args <initial-rspec-args>]
75
+ bundle exec rspec-interactive [--config <config-file>]
76
76
  ```
77
77
 
78
78
  ## Example Usage In This Repo
@@ -83,12 +83,6 @@ Start:
83
83
  bundle exec rspec-interactive
84
84
  ```
85
85
 
86
- Start with an initial RSpec invocation:
87
-
88
- ```shell
89
- bundle exec rspec-interactive --initial-rspec-args examples/passing_spec.rb
90
- ```
91
-
92
86
  Run a passing spec:
93
87
 
94
88
  ```shell
@@ -3,18 +3,27 @@
3
3
  require 'optparse'
4
4
  require 'rspec-interactive'
5
5
 
6
- @options = {}
6
+ @options = {
7
+ server: true,
8
+ server_port: RSpec::Interactive::DEFAULT_PORT
9
+ }
10
+
7
11
  parser = OptionParser.new do |opts|
8
12
  opts.banner = "Starts an interactive RSpec shell.\n\n"\
9
- "Usage: bundle exec rspec-interactive [-c config-file] <initial-rspec-args>"
13
+ "Usage: bundle exec rspec-interactive [--config config-file] [--no-server] [--port <port>]"
10
14
 
11
15
  opts.on("-c", "--config <config-file>", String, "Optional. Path to the RSpec Interactive config file.") do |config_file|
12
16
  @options[:config_file] = config_file
13
17
  end
14
18
 
15
- opts.on("-r", "--initial-rspec-args <initial-rspec-args>", String, "Optional. Arguments to pass to an initial invocation of RSpec.") do |initial_rspec_args|
16
- @options[:initial_rspec_args] = initial_rspec_args
19
+ opts.on("--no-server", "Optional. Disable server.") do
20
+ @options[:server] = false
21
+ end
22
+
23
+ opts.on("-p", "--port <port>", Integer, "Optional. Server port. Defaults to #{RSpec::Interactive::DEFAULT_PORT}.") do |port|
24
+ @options[:port] = port
17
25
  end
26
+
18
27
  end.parse!
19
28
 
20
- RSpec::Interactive.start(config_file: @options[:config_file], initial_rspec_args: @options[:initial_rspec_args])
29
+ RSpec::Interactive.start(config_file: @options[:config_file], server: @options[:server], port: @options[:server_port])
@@ -0,0 +1,7 @@
1
+ RSpec::Interactive.configure do |config|
2
+ config.watch_dirs += ['lib']
3
+
4
+ config.configure_rspec do
5
+ RSpec.configuration.formatter = :documentation
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ module RSpec
2
+ module Interactive
3
+ class ClientOutput
4
+ def initialize(client)
5
+ @client = client
6
+ end
7
+
8
+ def puts(str = "")
9
+ @client.puts(str&.to_s || '')
10
+ end
11
+
12
+ def string
13
+ @output
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ require 'pry'
2
+
3
+ class Pry
4
+ alias_method :old_eval, :eval
5
+
6
+ def eval(line, options = {})
7
+ RSpec::Interactive.eval do
8
+ old_eval(line, options)
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,15 @@
1
+ require 'rspec/core'
2
+
3
+ module RSpec
4
+ module Core
5
+ class Example
6
+ alias_method :old_run, :run
7
+
8
+ def run(example_group_instance, reporter)
9
+ execution_result.started_at = RSpec::Core::Time.now
10
+ old_run(example_group_instance, reporter)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,39 @@
1
+ module RSpec
2
+ module Interactive
3
+ class Stdio
4
+ def self.capture(output)
5
+ raise ArgumentError, 'missing block' unless block_given?
6
+
7
+ stdout, stderr = STDOUT.dup, STDERR.dup
8
+
9
+ IO.pipe do |stdout_read, stdout_write|
10
+ IO.pipe do |stderr_read, stderr_write|
11
+ STDOUT.reopen(stdout_write)
12
+ STDERR.reopen(stderr_write)
13
+
14
+ stdout_write.close
15
+ stderr_write.close
16
+
17
+ thread = Thread.new do
18
+ until stdout_read.eof? && stderr_read.eof? do
19
+ line = stdout_read.gets
20
+ output.puts line if line
21
+ line = stderr_read.gets
22
+ output.puts if line
23
+ end
24
+ end
25
+
26
+ begin
27
+ yield
28
+ ensure
29
+ STDOUT.reopen stdout
30
+ STDERR.reopen stderr
31
+ end
32
+
33
+ thread.join
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module Interactive
5
- VERSION = "0.8.1"
5
+ VERSION = "0.9.2"
6
6
  end
7
7
  end
@@ -4,19 +4,26 @@ require 'pry'
4
4
  require 'readline'
5
5
  require 'rspec/core'
6
6
  require 'shellwords'
7
+ require 'socket'
8
+ require 'teamcity/spec/runner/formatter/teamcity/formatter'
7
9
 
8
- require 'rspec-interactive/runner'
10
+ require 'rspec-interactive/client_output'
9
11
  require 'rspec-interactive/config'
10
- require 'rspec-interactive/rspec_config_cache'
11
12
  require 'rspec-interactive/input_completer'
13
+ require 'rspec-interactive/pry'
12
14
  require 'rspec-interactive/refresh_command'
13
15
  require 'rspec-interactive/rspec_command'
16
+ require 'rspec-interactive/rspec_config_cache'
17
+ require 'rspec-interactive/rspec_core_example'
14
18
  require 'rspec-interactive/rubo_cop_command'
19
+ require 'rspec-interactive/runner'
20
+ require 'rspec-interactive/stdio'
15
21
 
16
22
  module RSpec
17
23
  module Interactive
18
24
 
19
25
  DEFAULT_HISTORY_FILE = '.rspec_interactive_history'.freeze
26
+ DEFAULT_PORT = 5678
20
27
 
21
28
  class << self
22
29
  attr_accessor :configuration
@@ -26,11 +33,20 @@ module RSpec
26
33
  block.call(@configuration)
27
34
  end
28
35
 
29
- def self.start(config_file: nil, initial_rspec_args: nil, history_file: DEFAULT_HISTORY_FILE, input_stream: STDIN, output_stream: STDOUT, error_stream: STDERR)
36
+ def self.start(
37
+ config_file: nil,
38
+ server: false,
39
+ port: DEFAULT_PORT,
40
+ history_file: DEFAULT_HISTORY_FILE,
41
+ input_stream: STDIN,
42
+ output_stream: STDOUT,
43
+ error_stream: STDERR)
44
+
30
45
  @history_file = history_file
31
46
  @updated_files = []
32
47
  @stty_save = %x`stty -g`.chomp
33
- @mutex = Mutex.new
48
+ @file_change_mutex = Mutex.new
49
+ @command_mutex = Mutex.new
34
50
  @output_stream = output_stream
35
51
  @input_stream = input_stream
36
52
  @error_stream = error_stream
@@ -40,21 +56,34 @@ module RSpec
40
56
  load config_file if config_file
41
57
 
42
58
  check_rails
43
- trap_interrupt
44
59
  configure_pry
45
60
 
46
- @init_thread = Thread.start {
47
- @config_cache.record_configuration { @configuration.configure_rspec.call }
48
- start_file_watcher
49
- }
61
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
62
+ @config_cache.record_configuration { @configuration.configure_rspec.call }
63
+ end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
64
+ if end_time - start_time > 5
65
+ @output_stream.puts "Configuring RSpec took #{(end_time - start_time).round} seconds."
66
+ end
67
+
68
+ start_file_watcher
69
+
70
+ if server
71
+ @output_stream.puts "Listening on port #{port}."
72
+ server_thread = Thread.start do
73
+ server = TCPServer.new port
50
74
 
51
- if initial_rspec_args
52
- open(@history_file, 'a') { |f| f.puts "rspec #{initial_rspec_args.strip}" }
53
- rspec Shellwords.split(initial_rspec_args)
75
+ while client = server.accept
76
+ request = client.gets
77
+ args = Shellwords.split(request)
78
+ rspec_for_server(client, args)
79
+ client.close
80
+ end
81
+ end
54
82
  end
55
83
 
56
84
  Pry.start
57
85
  @listener.stop if @listener
86
+ server_thread.exit if server_thread
58
87
  0
59
88
  end
60
89
 
@@ -66,23 +95,11 @@ module RSpec
66
95
  end
67
96
  end
68
97
 
69
- def self.configure_rspec
98
+ def self.configure_rspec(error_stream: @error_stream, output_stream: @output_stream)
70
99
  RSpec.configure do |config|
71
- config.error_stream = @error_stream
72
- config.output_stream = @output_stream
73
- end
74
- end
75
-
76
- def self.trap_interrupt
77
- trap('INT') do
78
- if @runner
79
- # We are on a different thread. There is a race here. Ignore nil.
80
- @runner&.quit
81
- else
82
- @output_stream.puts
83
- system "stty", @stty_save
84
- exit!(0)
85
- end
100
+ config.error_stream = error_stream
101
+ config.output_stream = output_stream
102
+ config.start_time = RSpec::Core::Time.now
86
103
  end
87
104
  end
88
105
 
@@ -91,7 +108,7 @@ module RSpec
91
108
 
92
109
  # Only polling seems to work in Docker.
93
110
  @listener = Listen.to(*@configuration.watch_dirs, only: /\.rb$/, force_polling: true) do |modified, added|
94
- @mutex.synchronize do
111
+ @file_change_mutex.synchronize do
95
112
  @updated_files.concat(added + modified)
96
113
  end
97
114
  end
@@ -99,9 +116,6 @@ module RSpec
99
116
  end
100
117
 
101
118
  def self.configure_pry
102
- # Prevent Pry from trapping too. It will break ctrl-c handling.
103
- Pry.config.should_trap_interrupts = false
104
-
105
119
  # Set up IO.
106
120
  Pry.config.input = Readline
107
121
  Pry.config.output = @output_stream
@@ -115,7 +129,7 @@ module RSpec
115
129
  end
116
130
 
117
131
  def self.refresh
118
- @mutex.synchronize do
132
+ @file_change_mutex.synchronize do
119
133
  @updated_files.uniq.each do |filename|
120
134
  @output_stream.puts "changed: #{filename}"
121
135
  trace = TracePoint.new(:class) do |tp|
@@ -131,8 +145,8 @@ module RSpec
131
145
  @configuration.refresh.call
132
146
  end
133
147
 
134
- def self.rspec(args)
135
- parsed_args = args.flat_map do |arg|
148
+ def self.parse_args(args)
149
+ args.flat_map do |arg|
136
150
  if arg.match(/[\*\?\[]/)
137
151
  glob = Dir.glob(arg)
138
152
  glob.empty? ? [arg] : glob
@@ -140,15 +154,10 @@ module RSpec
140
154
  [arg]
141
155
  end
142
156
  end
157
+ end
143
158
 
144
- # Initialize the runner before waiting for the init thread so that the interrupt
145
- # handler will cancel the RSpec invocation rather than kill the app.
146
- @runner = RSpec::Interactive::Runner.new(parsed_args)
147
-
148
- if @init_thread&.alive?
149
- @init_thread.join
150
- @init_thread = nil
151
- end
159
+ def self.rspec(args)
160
+ @runner = RSpec::Interactive::Runner.new(parse_args(args))
152
161
 
153
162
  refresh
154
163
 
@@ -169,21 +178,71 @@ module RSpec
169
178
  RSpec.clear_examples
170
179
  RSpec.reset
171
180
  @config_cache.replay_configuration
181
+ rescue Interrupt => e
182
+ @runner&.quit
183
+ raise e
172
184
  ensure
173
185
  @runner = nil
174
186
  end
175
187
 
176
- def self.rubo_cop(args)
177
- if @init_thread&.alive?
178
- @init_thread.join
179
- @init_thread = nil
188
+ def self.rspec_for_server(client, args)
189
+ @command_mutex.synchronize do
190
+ # Prevent the debugger from being used. The server isn't interactive.
191
+ disable_pry = ENV['DISABLE_PRY']
192
+ ENV['DISABLE_PRY'] = 'true'
193
+
194
+ output = ClientOutput.new(client)
195
+ Stdio.capture(ClientOutput.new(client)) do
196
+ @runner = RSpec::Interactive::Runner.new(parse_args(args))
197
+
198
+ refresh
199
+
200
+ # RSpec::Interactive-specific RSpec configuration
201
+ configure_rspec
202
+
203
+ # RubyMine specifies --format. That causes a formatter to be added. It does not override
204
+ # the existing formatter (if one is set by default). Clear any formatters but resetting
205
+ # the loader.
206
+ RSpec.configuration.instance_variable_set(
207
+ :@formatter_loader,
208
+ RSpec::Core::Formatters::Loader.new(RSpec::Core::Reporter.new(RSpec.configuration)))
209
+
210
+ # Always use the teamcity formatter, even though RubyMine always specifies it.
211
+ # This make manual testing of rspec-interactive easier.
212
+ RSpec.configuration.formatter = Spec::Runner::Formatter::TeamcityFormatter
213
+
214
+ # Run.
215
+ exit_code = @runner.run
216
+
217
+ # Reset
218
+ RSpec.clear_examples
219
+ RSpec.reset
220
+ @config_cache.replay_configuration
221
+ end
222
+ ensure
223
+ ENV['DISABLE_PRY'] = disable_pry
180
224
  end
225
+ end
181
226
 
227
+ def self.rubo_cop(args)
182
228
  if defined?(RuboCop)
183
229
  RuboCop::CLI.new.run args
184
230
  else
185
231
  @error_stream.puts "fatal: RuboCop not found. Is the gem installed in this project?"
186
232
  end
187
233
  end
234
+
235
+ def self.eval(&block)
236
+ if Thread.current.thread_variable_get('holding_lock')
237
+ yield
238
+ else
239
+ @command_mutex.synchronize do
240
+ Thread.current.thread_variable_set('holding_lock', true)
241
+ yield
242
+ ensure
243
+ Thread.current.thread_variable_set('holding_lock', false)
244
+ end
245
+ end
246
+ end
188
247
  end
189
248
  end
@@ -0,0 +1,10 @@
1
+ require 'rspec/teamcity'
2
+
3
+ module Spec
4
+ module Runner
5
+ module Formatter
6
+ class TeamcityFormatter
7
+ end
8
+ end
9
+ end
10
+ end
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.require_paths = ["lib"]
27
27
 
28
28
  spec.add_dependency 'rspec-core'
29
+ spec.add_dependency 'rspec-teamcity', '1.0.0'
29
30
  spec.add_dependency 'listen'
30
31
  spec.add_dependency 'pry'
31
32
  end
data/runner/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ ruby '3.0.0'
4
+
5
+ source "https://rubygems.org"
@@ -0,0 +1,14 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+
5
+ PLATFORMS
6
+ x86_64-darwin-20
7
+
8
+ DEPENDENCIES
9
+
10
+ RUBY VERSION
11
+ ruby 3.0.0p0
12
+
13
+ BUNDLED WITH
14
+ 2.3.6
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'shellwords'
5
+ require 'socket'
6
+
7
+ @options = {
8
+ host: 'localhost',
9
+ port: 5678
10
+ }
11
+
12
+ parser = OptionParser.new do |opts|
13
+ opts.banner = "Executes RSpec by connecting to a running RSpec Interactive shell.\n\n"\
14
+ "Usage: bundle exec rspec-interactive-run [--host <host>] [--port <port>] [rspec-args]"
15
+
16
+ opts.on("-h", "--host <host>", String, "Optional. Server host. Defaults to localhost.") do |host|
17
+ @options[:host] = host
18
+ end
19
+
20
+ opts.on("-p", "--port <port>", Integer, "Optional. Server port. Defaults to 5678.") do |port|
21
+ @options[:port] = port
22
+ end
23
+
24
+ end.parse
25
+
26
+ server = TCPSocket.open(@options[:host], @options[:port])
27
+ server.puts ARGV.map{ |arg| Shellwords.escape arg }.join(' ')
28
+ while response = server.gets do
29
+ puts response
30
+ end
31
+ server.close
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-interactive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Dower
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-28 00:00:00.000000000 Z
11
+ date: 2022-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec-core
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-teamcity
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: listen
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -70,21 +84,30 @@ files:
70
84
  - bin/rspec-interactive
71
85
  - bin/setup
72
86
  - bin/test
87
+ - examples/config.rb
73
88
  - examples/debugged_spec.rb
74
89
  - examples/failing_spec.rb
75
90
  - examples/other_passing_spec.rb
76
91
  - examples/passing_spec.rb
77
92
  - examples/spec_with_syntax_error.rb
78
93
  - lib/rspec-interactive.rb
94
+ - lib/rspec-interactive/client_output.rb
79
95
  - lib/rspec-interactive/config.rb
80
96
  - lib/rspec-interactive/input_completer.rb
97
+ - lib/rspec-interactive/pry.rb
81
98
  - lib/rspec-interactive/refresh_command.rb
82
99
  - lib/rspec-interactive/rspec_command.rb
83
100
  - lib/rspec-interactive/rspec_config_cache.rb
101
+ - lib/rspec-interactive/rspec_core_example.rb
84
102
  - lib/rspec-interactive/rubo_cop_command.rb
85
103
  - lib/rspec-interactive/runner.rb
104
+ - lib/rspec-interactive/stdio.rb
86
105
  - lib/rspec-interactive/version.rb
106
+ - lib/teamcity/spec/runner/formatter/teamcity/formatter.rb
87
107
  - rspec-interactive.gemspec
108
+ - runner/Gemfile
109
+ - runner/Gemfile.lock
110
+ - runner/rspec-interactive-run
88
111
  - scripts/release.sh
89
112
  - scripts/run-with-local-dep.sh
90
113
  - tests/debugged_spec_test.rb