yap-shell 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +11 -0
  5. data/Gemfile.travis +8 -0
  6. data/README.md +9 -11
  7. data/addons/history/history.rb +1 -0
  8. data/addons/history_search/history_search.rb +21 -17
  9. data/bin/yap +47 -55
  10. data/bin/yap-dev +0 -1
  11. data/lib/yap/configuration.rb +25 -0
  12. data/lib/yap/shell/builtins/alias.rb +16 -0
  13. data/lib/yap/shell/commands.rb +15 -3
  14. data/lib/yap/shell/evaluation/shell_expansions.rb +10 -10
  15. data/lib/yap/shell/evaluation.rb +29 -4
  16. data/lib/yap/shell/execution/context.rb +3 -2
  17. data/lib/yap/shell/execution/file_system_command_execution.rb +9 -17
  18. data/lib/yap/shell/repl.rb +2 -5
  19. data/lib/yap/shell/version.rb +1 -1
  20. data/lib/yap/shell.rb +8 -2
  21. data/lib/yap/world.rb +23 -18
  22. data/lib/yap.rb +52 -7
  23. data/spec/features/aliases_spec.rb +78 -0
  24. data/spec/features/environment_variables_spec.rb +69 -0
  25. data/spec/features/filesystem_commands_spec.rb +61 -0
  26. data/spec/features/first_time_spec.rb +45 -0
  27. data/spec/features/grouping_spec.rb +81 -0
  28. data/spec/features/line_editing_spec.rb +166 -0
  29. data/spec/features/range_spec.rb +35 -0
  30. data/spec/features/redirection_spec.rb +225 -0
  31. data/spec/features/repetition_spec.rb +118 -0
  32. data/spec/features/shell_expansions_spec.rb +127 -0
  33. data/spec/spec_helper.rb +162 -0
  34. data/spec/support/matchers/have_not_printed.rb +30 -0
  35. data/spec/support/matchers/have_printed.rb +30 -0
  36. data/spec/support/very_soon.rb +9 -0
  37. data/spec/support/yap_spec_dsl.rb +240 -0
  38. data/yap-shell.gemspec +4 -2
  39. metadata +69 -8
@@ -0,0 +1,225 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'I/O Redirection', type: :feature do
4
+ before do
5
+ write_executable_script 'fail', <<-SCRIPT.strip_heredoc
6
+ |#!/bin/sh
7
+ |>&2 echo fail $1
8
+ |exit 1
9
+ SCRIPT
10
+ end
11
+
12
+ describe 'STDOUT' do
13
+ it 'redirects with: >' do
14
+ type 'echo foo > output.txt'
15
+ enter
16
+ clear_all_output
17
+
18
+ type 'cat output.txt'
19
+ enter
20
+ expect { output }.to have_printed('foo')
21
+ end
22
+
23
+ it 'redirects with: 1>' do
24
+ type 'echo hrm 1> output.txt'
25
+ enter
26
+ clear_all_output
27
+
28
+ type 'cat output.txt'
29
+ enter
30
+ expect { output }.to have_printed('hrm')
31
+ end
32
+
33
+ it 'overwrites the file' do
34
+ type 'echo foo > output.txt'
35
+ enter
36
+ type 'echo bar > output.txt'
37
+ enter
38
+ clear_all_output
39
+
40
+ type 'cat output.txt'
41
+ enter
42
+ expect { output }.to have_printed('bar')
43
+ expect { output }.to have_not_printed('foo')
44
+ end
45
+
46
+ describe 'appending' do
47
+ before do
48
+ type 'echo foo > output.txt'
49
+ enter
50
+ type 'cat output.txt'
51
+ enter
52
+ expect { output }.to have_printed(/foo/)
53
+ clear_all_output
54
+ end
55
+
56
+ it 'appends with: >>' do
57
+ type 'echo bar >> output.txt'
58
+ enter
59
+ type 'cat output.txt'
60
+ enter
61
+ expect { output }.to have_printed(/foo.*\n.*bar/)
62
+ end
63
+
64
+ it 'appends with: 1>>' do
65
+ type 'echo bar 1>> output.txt'
66
+ enter
67
+ type 'cat output.txt'
68
+ enter
69
+ expect { output }.to have_printed(/foo.*\n.*bar/)
70
+ end
71
+ end
72
+ end
73
+
74
+ describe 'STDERR' do
75
+ it 'redirects with: 2>' do
76
+ type './fail foo 2> error.txt'
77
+ enter
78
+ clear_all_output
79
+
80
+ type 'cat error.txt'
81
+ enter
82
+ expect { output }.to have_printed('fail foo')
83
+ end
84
+
85
+ it 'overwrites the file' do
86
+ type './fail foo 2> error.txt'
87
+ enter
88
+ type './fail bar 2> error.txt'
89
+ enter
90
+ clear_all_output
91
+
92
+ type 'cat error.txt'
93
+ enter
94
+ expect { output }.to have_printed('fail bar')
95
+ expect { output }.to have_not_printed('fail foo')
96
+ clear_all_output
97
+ end
98
+
99
+ describe 'appending' do
100
+ before do
101
+ type './fail caz 2> error.txt'
102
+ enter
103
+ type 'cat error.txt'
104
+ enter
105
+ expect { output }.to have_printed(/fail caz/)
106
+ clear_all_output
107
+ end
108
+
109
+ it 'appends with: 2>>' do
110
+ type './fail box 2>> error.txt'
111
+ enter
112
+ type 'cat error.txt'
113
+ enter
114
+ expect { output }.to have_printed(/fail caz.*\n.*fail box/)
115
+ end
116
+ end
117
+ end
118
+
119
+ describe 'STDOUT and STDERR separately for a command' do
120
+ before do
121
+ write_executable_script 'print-stuff', <<-SCRIPT.strip_heredoc
122
+ |#!/bin/sh
123
+ |echo 'normal foo'
124
+ |(>&2 echo 'error foo')
125
+ SCRIPT
126
+ end
127
+
128
+ it 'redirects with: > and 2>' do
129
+ type './print-stuff > stdout.txt 2> stderr.txt'
130
+ enter
131
+ clear_all_output
132
+
133
+ type 'cat stdout.txt'
134
+ enter
135
+ expect { output }.to have_printed('normal foo')
136
+ clear_all_output
137
+
138
+ type 'cat stdout.txt'
139
+ enter
140
+ expect { output }.to have_not_printed('error foo')
141
+ clear_all_output
142
+
143
+ type 'cat stderr.txt'
144
+ enter
145
+ expect { output }.to have_printed('error foo')
146
+ clear_all_output
147
+
148
+ type 'cat stderr.txt'
149
+ enter
150
+ expect { output }.to have_not_printed('normal foo')
151
+ end
152
+
153
+ it 'redirects with: 1> and 2>' do
154
+ type './print-stuff 1> stdout.txt 2> stderr.txt'
155
+ enter
156
+ clear_all_output
157
+
158
+ type 'cat stdout.txt'
159
+ enter
160
+ expect { output }.to have_printed('normal foo')
161
+ clear_all_output
162
+
163
+ type 'cat stdout.txt'
164
+ enter
165
+ expect { output }.to have_not_printed('error foo')
166
+ clear_all_output
167
+
168
+ type 'cat stderr.txt'
169
+ enter
170
+ expect { output }.to have_printed('error foo')
171
+ clear_all_output
172
+
173
+ type 'cat stderr.txt'
174
+ enter
175
+ expect { output }.to have_not_printed('normal foo')
176
+ end
177
+ end
178
+
179
+ describe 'STDOUT and STDERR pointing to the same file' do
180
+ before do
181
+ write_executable_script 'echo-stdout-and-stderr', <<-SCRIPT.strip_heredoc
182
+ |#!/bin/sh
183
+ |echo $1
184
+ |>&2 echo $2
185
+ SCRIPT
186
+ end
187
+
188
+ it 'redirects and overwrites with: &>' do
189
+ type './echo-stdout-and-stderr foo bar &> output-and-error.txt'
190
+ enter
191
+ type 'cat output-and-error.txt'
192
+ enter
193
+ expect { output }.to have_printed(/foo.*\n.*bar/m)
194
+ end
195
+
196
+ it 'redirects and overwrites with: 2>&1' do
197
+ type './echo-stdout-and-stderr bar foo 2>&1 output-and-error.txt'
198
+ enter
199
+ type 'cat output-and-error.txt'
200
+ enter
201
+ expect { output }.to have_printed(/bar.*\n.*foo/m)
202
+ end
203
+
204
+ it 'redirects and appends with: &>>' do
205
+ type './echo-stdout-and-stderr howdy hey &>> output-and-error.txt'
206
+ enter
207
+ type 'cat output-and-error.txt'
208
+ enter
209
+ expect { output }.to have_printed(/howdy.*\n.*hey/m)
210
+ end
211
+ end
212
+
213
+ describe 'STDIN' do
214
+ it 'redirect with: <' do
215
+ type 'echo foo > stdin.txt'
216
+ enter
217
+ clear_all_output
218
+
219
+ type 'cat < stdin.txt'
220
+ enter
221
+ expect { output }.to have_printed('foo')
222
+ end
223
+ end
224
+
225
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Repetition', type: :feature do
4
+ describe 'N.times: statement' do
5
+ it 'runs the line N times' do
6
+ type '5.times : echo foo'
7
+ enter
8
+ expect { output }.to have_printed(/(.*foo){5}/m)
9
+ end
10
+
11
+ it 'is not white-space sensitive' do
12
+ type '5.times : echo foo'
13
+ enter
14
+ expect { output }.to have_printed(/(.*foo){5}/m)
15
+ end
16
+ end
17
+
18
+ describe 'N.times: statement1 ; statement2' do
19
+ it 'runs all the statements on the line N times' do
20
+ type '3.times : echo foo ; echo bar'
21
+ enter
22
+ expect { output }.to have_printed(/(.*foo.*bar){3}/m)
23
+ end
24
+
25
+ it 'is not white-space sensitive' do
26
+ type '3.times:echo foo ; echo bar'
27
+ enter
28
+ expect { output }.to have_printed(/(.*foo.*bar){3}/m)
29
+ end
30
+ end
31
+
32
+ describe 'parameters' do
33
+ it 'supplies an index for each execution' do
34
+ type '3.times as n: echo foo$n ; echo bar$n '
35
+ enter
36
+ expect { output }.to have_printed(/foo1.*bar1.*foo2.*bar2.*foo3.*bar3/m)
37
+ end
38
+
39
+ it 'does not overwrite existing variables with the same name' do
40
+ type "n='hello world'"
41
+ enter
42
+ type '3.times as n: echo $n'
43
+ enter
44
+ expect { output }.to have_printed(/1.*2.*3/m)
45
+ clear_all_output
46
+
47
+ type 'echo $n'
48
+ enter
49
+ expect { output }.to have_printed('hello world')
50
+ end
51
+ end
52
+
53
+ describe 'repetition with blocks' do
54
+ it 'runs the code inside the block N times' do
55
+ type '4.times { echo foo }'
56
+ enter
57
+ expect { output }.to have_printed(/(.*foo){4}/m)
58
+ end
59
+
60
+ xit 'requires white-space before the opening curly brace' do
61
+ # this breaks everything :(
62
+ type '4.times{ echo foo }'
63
+ enter
64
+ expect { output }.to have_printed(/Infinite loop detected/m)
65
+ skip 'Provide a better error message for the user.'
66
+ end
67
+
68
+ it 'requires white-space before the closing curly brace' do
69
+ type '4.times { echo foo}'
70
+ enter
71
+ expect { output }.to have_printed(/Parse error/m)
72
+ # should this be an error? Really? We can't tell that we're in a block
73
+ # and we've got a closing curly brace right there?
74
+ skip 'Provide a better error message for the user.'
75
+ end
76
+
77
+ it 'does not require white-space after the opening curly brace' do
78
+ type '4.times {echo foo }'
79
+ enter
80
+ expect { output }.to have_printed(/(.*foo){4}/m)
81
+ end
82
+
83
+ it 'does not require white-space after the closing curly brace' do
84
+ type '4.times { echo foo };'
85
+ enter
86
+ expect { output }.to have_printed(/Parse error/m)
87
+ skip "This shouldn't be an error"
88
+ end
89
+
90
+ describe 'block parameters' do
91
+ it 'supplies an index for each execution' do
92
+ type 'echo beg ; 4.times { |n| echo $n } ; echo end'
93
+ enter
94
+ expect { output }.to have_printed(/beg.*\n.*1.*2.*3.*4.*\n.*end/m)
95
+ end
96
+
97
+ it 'does not overwrite existing variables with the same name' do
98
+ type "n='hello world'"
99
+ enter
100
+ type 'echo beg ; 3.times { |n| echo $n } ; echo end'
101
+ enter
102
+ expect { output }.to have_printed(/beg.*\n.*1.*2.*3.*\n.*end/m)
103
+ clear_all_output
104
+
105
+ type 'echo $n'
106
+ enter
107
+ expect { output }.to have_printed('hello world')
108
+ end
109
+
110
+ it 'can step thru the repetition in groups based on how many block params are provided' do
111
+ type 'echo beg ; 5.times { |a, b| echo $a $b } ; echo end'
112
+ enter
113
+ expect { output }.to have_printed(/beg.*\n.*1 2.*\n.*3 4.*\n.*5.*\n.*end/m)
114
+ end
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Shell expansions', type: :feature do
4
+ describe 'aliases' do
5
+ before do
6
+ type 'alias foo="echo bar"'
7
+ enter
8
+ expect { output }.to have_printed('Setting alias foo')
9
+ end
10
+
11
+ it 'expands aliases found in command position' do
12
+ type 'foo'
13
+ enter
14
+ expect { output }.to have_printed('bar')
15
+ end
16
+
17
+ it 'does not expand aliases found in argument position' do
18
+ type 'echo foo'
19
+ enter
20
+ expect { output }.to have_printed('foo')
21
+ expect { output }.to have_not_printed('bar')
22
+ end
23
+ end
24
+
25
+ describe 'environment variable expansion' do
26
+ it 'expands upper-case env vars with a preceding $' do
27
+ type 'FOO=bar echo $FOO'
28
+ enter
29
+ expect { output }.to have_printed('bar')
30
+ end
31
+
32
+ it 'expands lower-case vars with a preceding $' do
33
+ type 'foo=bar echo $foo'
34
+ enter
35
+ expect { output }.to have_printed('bar')
36
+ end
37
+
38
+ it 'is case sensitive' do
39
+ type 'foo=bar echo $foo'
40
+ enter
41
+ expect { output }.to have_printed('bar')
42
+
43
+ type 'echo $FOO'
44
+ enter
45
+ expect { output }.to have_printed('$FOO')
46
+ end
47
+
48
+ it 'does not expand escaped env vars' do
49
+ type 'FOO=bar echo \\$FOO'
50
+ enter
51
+ expect { output }.to have_printed('$FOO')
52
+ end
53
+ end
54
+
55
+ describe 'exit status for last command: $?' do
56
+ it 'prints whatever the exit status of the last command was' do
57
+ write_executable_script 'exit-with-status-code', <<-SCRIPT.strip_heredoc
58
+ |#!/bin/sh
59
+ |exit $1
60
+ SCRIPT
61
+
62
+ type './exit-with-status-code 0 ; echo $?'
63
+ enter
64
+ expect { output }.to have_printed('0')
65
+
66
+ type './exit-with-status-code 1 ; echo $?'
67
+ enter
68
+ expect { output }.to have_printed('1')
69
+
70
+ type './exit-with-status-code 99 ; echo $?'
71
+ enter
72
+ expect { output }.to have_printed('99')
73
+ end
74
+ end
75
+
76
+ describe 'word expansions using curly braces' do
77
+ it 'expands in command position' do
78
+ type 'foo_{}'
79
+ enter
80
+ expect { error_output }.to have_printed('yap: command not found: foo_')
81
+ end
82
+
83
+ describe 'arguments' do
84
+ it 'expands no words' do
85
+ type 'echo foo_{}'
86
+ enter
87
+ expect { output }.to have_printed('foo_')
88
+ end
89
+
90
+ it 'expands single words' do
91
+ type 'echo foo_{bar}'
92
+ enter
93
+ expect { output }.to have_printed('foo_bar')
94
+ end
95
+
96
+ it 'multiple words in comma-separated list' do
97
+ type 'echo foo_{bar,bay,baz}'
98
+ enter
99
+ expect { output }.to have_printed('foo_bar foo_bay')
100
+ end
101
+
102
+ it 'expands environment variables' do
103
+ type 'FOO=huzzah echo foo_{$FOO}'
104
+ enter
105
+ expect { output }.to have_printed('foo_huzzah')
106
+ end
107
+
108
+ it 'does not expand environment variables in double quotes' do
109
+ type 'FOO=huzzah echo foo_{"$FOO"}'
110
+ enter
111
+ expect { output }.to have_printed('foo_{$FOO}')
112
+ end
113
+
114
+ it 'does not expand environment variables in single quotes' do
115
+ type %|FOO=huzzah echo foo_{'$FOO'}|
116
+ enter
117
+ expect { output }.to have_printed('foo_{$FOO}')
118
+ end
119
+
120
+ it 'treats ~ as any a normal character, not home directory expansion' do
121
+ type 'echo foo_{~}'
122
+ enter
123
+ expect { output }.to have_printed('foo_~')
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,162 @@
1
+ require 'ansi_string'
2
+ require 'childprocess'
3
+ require 'pry'
4
+
5
+ Dir[File.dirname(__FILE__) + '/support/**/**/*.rb'].each do |file|
6
+ require file
7
+ end
8
+
9
+ class String
10
+ def strip_heredoc
11
+ gsub(/^\s*\|/, '')
12
+ end
13
+ end
14
+
15
+ # This file was generated by the `rspec --init` command. Conventionally, all
16
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
17
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
18
+ # this file to always be loaded, without a need to explicitly require it in any
19
+ # files.
20
+ #
21
+ # Given that it is always loaded, you are encouraged to keep this file as
22
+ # light-weight as possible. Requiring heavyweight dependencies from this file
23
+ # will add to the boot time of your test suite on EVERY test run, even for an
24
+ # individual file that may not need all of that loaded. Instead, consider making
25
+ # a separate helper file that requires the additional dependencies and performs
26
+ # the additional setup, and require it from the spec files that actually need
27
+ # it.
28
+ #
29
+ # The `.rspec` file also contains a few flags that are not defaults but that
30
+ # users commonly want.
31
+ #
32
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
33
+ RSpec.configure do |config|
34
+ config.include Yap::Spec::DSL
35
+
36
+ config.before(:all, type: :feature) do
37
+ ENV['HOME'] = tmp_dir.to_s
38
+
39
+ set_yap_command_line_arguments \
40
+ '--no-history', '--no-addons', '--no-rcfiles', '--skip-first-time'
41
+
42
+ turn_on_debug_log(debug: 'editor')
43
+
44
+ initialize_shell
45
+
46
+ mkdir tmp_dir
47
+ Dir.chdir tmp_dir
48
+
49
+ type "cd #{tmp_dir}"
50
+ enter
51
+
52
+ type "pwd"
53
+ enter
54
+ expect { output }.to have_printed(File.expand_path(tmp_dir))
55
+ clear_all_output
56
+ end
57
+
58
+ config.after(:all, type: :feature) do
59
+ shell.stop if shell
60
+ end
61
+
62
+ config.after(:each, type: :feature) do
63
+ begin
64
+ if self.class.metadata[:forks]
65
+ # no-op because the example will cause yap to fork inside the child
66
+ # childprocess and will break the IO pipes we know about in the test
67
+ else
68
+ enter if typed_content_awaiting_enter?
69
+
70
+ # Use yap to tell us when it's done doing whatever is was doing.
71
+ # This ensures we don'try to clean up before yap is done!
72
+ type 'echo done with this test'
73
+ enter
74
+ expect { output }.to have_printed('done with this test')
75
+ end
76
+ clear_all_output
77
+ ensure
78
+ Dir.chdir(tmp_dir) do
79
+ # remove all files including hidden but not current directory and
80
+ # parent directory
81
+ FileUtils.rm_rf Dir.glob('{.,}*') - %w(. ..)
82
+ end
83
+ end
84
+ end
85
+
86
+ # rspec-expectations config goes here. You can use an alternate
87
+ # assertion/expectation library such as wrong or the stdlib/minitest
88
+ # assertions if you prefer.
89
+ config.expect_with :rspec do |expectations|
90
+ # This option will default to `true` in RSpec 4. It makes the `description`
91
+ # and `failure_message` of custom matchers include text for helper methods
92
+ # defined using `chain`, e.g.:
93
+ # be_bigger_than(2).and_smaller_than(4).description
94
+ # # => "be bigger than 2 and smaller than 4"
95
+ # ...rather than:
96
+ # # => "be bigger than 2"
97
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
98
+ end
99
+
100
+ # rspec-mocks config goes here. You can use an alternate test double
101
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
102
+ config.mock_with :rspec do |mocks|
103
+ # Prevents you from mocking or stubbing a method that does not exist on
104
+ # a real object. This is generally recommended, and will default to
105
+ # `true` in RSpec 4.
106
+ mocks.verify_partial_doubles = true
107
+ end
108
+
109
+ # The settings below are suggested to provide a good initial experience
110
+ # with RSpec, but feel free to customize to your heart's content.
111
+ =begin
112
+ # These two settings work together to allow you to limit a spec run
113
+ # to individual examples or groups you care about by tagging them with
114
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
115
+ # get run.
116
+ config.filter_run :focus
117
+ config.run_all_when_everything_filtered = true
118
+
119
+ # Allows RSpec to persist some state between runs in order to support
120
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
121
+ # you configure your source control system to ignore this file.
122
+ config.example_status_persistence_file_path = "spec/examples.txt"
123
+
124
+ # Limits the available syntax to the non-monkey patched syntax that is
125
+ # recommended. For more details, see:
126
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
127
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
128
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
129
+ config.disable_monkey_patching!
130
+
131
+ # This setting enables warnings. It's recommended, but in some cases may
132
+ # be too noisy due to issues in dependencies.
133
+ config.warnings = true
134
+
135
+ # Many RSpec users commonly either run the entire suite or an individual
136
+ # file, and it's useful to allow more verbose output when running an
137
+ # individual spec file.
138
+ if config.files_to_run.one?
139
+ # Use the documentation formatter for detailed output,
140
+ # unless a formatter has already been configured
141
+ # (e.g. via a command-line flag).
142
+ config.default_formatter = 'doc'
143
+ end
144
+
145
+ # Print the 10 slowest examples and example groups at the
146
+ # end of the spec run, to help surface which specs are running
147
+ # particularly slow.
148
+ config.profile_examples = 10
149
+
150
+ # Run specs in random order to surface order dependencies. If you find an
151
+ # order dependency and want to debug it, you can fix the order by providing
152
+ # the seed, which is printed after each run.
153
+ # --seed 1234
154
+ config.order = :random
155
+
156
+ # Seed global randomization in this process using the `--seed` CLI option.
157
+ # Setting this allows you to use `--seed` to deterministically reproduce
158
+ # test failures related to randomization by passing the same `--seed` value
159
+ # as the one that triggered the failure.
160
+ Kernel.srand config.seed
161
+ =end
162
+ end
@@ -0,0 +1,30 @@
1
+ RSpec::Matchers.define :have_not_printed do |expected|
2
+ output_seen_so_far = ""
3
+
4
+ match do |block|
5
+ regex = expected
6
+ regex = /#{Regexp.escape(regex)}/m unless regex.is_a?(Regexp)
7
+ begin
8
+ very_soon do
9
+ current_output = block.call
10
+ output_seen_so_far << current_output
11
+ current_output.length > 0
12
+ end
13
+ rescue Timeout::Error
14
+ return true # we didn't see it, make the assumption we're good
15
+ end
16
+ !regex.match(output_seen_so_far)
17
+ end
18
+
19
+ failure_message do |actual|
20
+ if expected.is_a?(Regexp)
21
+ "expected that #{expected.inspect} would not match #{output_seen_so_far.inspect}"
22
+ else
23
+ "expected that #{expected.inspect} would not appear in #{output_seen_so_far.inspect}"
24
+ end
25
+ end
26
+
27
+ def supports_block_expectations?
28
+ true
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ RSpec::Matchers.define :have_printed do |expected|
2
+ output_seen_so_far = ""
3
+
4
+ match do |block|
5
+ begin
6
+ regex = expected
7
+ regex = /#{Regexp.escape(regex)}/m unless regex.is_a?(Regexp)
8
+ very_soon do
9
+ current_output = block.call
10
+ output_seen_so_far << current_output
11
+ regex.match(current_output)
12
+ end
13
+ true
14
+ rescue Timeout::Error
15
+ false
16
+ end
17
+ end
18
+
19
+ failure_message do |actual|
20
+ if expected.is_a?(Regexp)
21
+ "expected that #{expected.inspect} would match #{output_seen_so_far.inspect}"
22
+ else
23
+ "expected that #{expected.inspect} would appear in #{output_seen_so_far.inspect}"
24
+ end
25
+ end
26
+
27
+ def supports_block_expectations?
28
+ true
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ def very_soon(timeout: 5, &block)
2
+ Timeout.timeout(timeout) do
3
+ loop do
4
+ result = block.call
5
+ break result if result
6
+ sleep 0.1
7
+ end
8
+ end
9
+ end