andyw8-seeing_is_believing 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +60 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +2 -0
  5. data/Gemfile +2 -0
  6. data/README.md +70 -0
  7. data/Rakefile +88 -0
  8. data/appveyor.yml +32 -0
  9. data/bin/seeing_is_believing +7 -0
  10. data/docs/example.gif +0 -0
  11. data/docs/frog-brown.png +0 -0
  12. data/docs/sib-streaming.gif +0 -0
  13. data/features/deprecated-flags.feature +91 -0
  14. data/features/errors.feature +155 -0
  15. data/features/examples.feature +423 -0
  16. data/features/flags.feature +852 -0
  17. data/features/regression.feature +898 -0
  18. data/features/support/env.rb +102 -0
  19. data/features/xmpfilter-style.feature +471 -0
  20. data/lib/seeing_is_believing/binary/align_chunk.rb +47 -0
  21. data/lib/seeing_is_believing/binary/align_file.rb +24 -0
  22. data/lib/seeing_is_believing/binary/align_line.rb +25 -0
  23. data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +56 -0
  24. data/lib/seeing_is_believing/binary/annotate_every_line.rb +52 -0
  25. data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +179 -0
  26. data/lib/seeing_is_believing/binary/comment_lines.rb +36 -0
  27. data/lib/seeing_is_believing/binary/commentable_lines.rb +126 -0
  28. data/lib/seeing_is_believing/binary/config.rb +455 -0
  29. data/lib/seeing_is_believing/binary/data_structures.rb +58 -0
  30. data/lib/seeing_is_believing/binary/engine.rb +161 -0
  31. data/lib/seeing_is_believing/binary/format_comment.rb +79 -0
  32. data/lib/seeing_is_believing/binary/interline_align.rb +57 -0
  33. data/lib/seeing_is_believing/binary/remove_annotations.rb +113 -0
  34. data/lib/seeing_is_believing/binary/rewrite_comments.rb +62 -0
  35. data/lib/seeing_is_believing/binary.rb +73 -0
  36. data/lib/seeing_is_believing/code.rb +139 -0
  37. data/lib/seeing_is_believing/compatibility.rb +28 -0
  38. data/lib/seeing_is_believing/debugger.rb +32 -0
  39. data/lib/seeing_is_believing/error.rb +17 -0
  40. data/lib/seeing_is_believing/evaluate_by_moving_files.rb +195 -0
  41. data/lib/seeing_is_believing/event_stream/consumer.rb +221 -0
  42. data/lib/seeing_is_believing/event_stream/events.rb +193 -0
  43. data/lib/seeing_is_believing/event_stream/handlers/debug.rb +61 -0
  44. data/lib/seeing_is_believing/event_stream/handlers/record_exit_events.rb +26 -0
  45. data/lib/seeing_is_believing/event_stream/handlers/stream_json_events.rb +23 -0
  46. data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +41 -0
  47. data/lib/seeing_is_believing/event_stream/producer.rb +178 -0
  48. data/lib/seeing_is_believing/hard_core_ensure.rb +58 -0
  49. data/lib/seeing_is_believing/hash_struct.rb +206 -0
  50. data/lib/seeing_is_believing/result.rb +89 -0
  51. data/lib/seeing_is_believing/safe.rb +112 -0
  52. data/lib/seeing_is_believing/swap_files.rb +90 -0
  53. data/lib/seeing_is_believing/the_matrix.rb +97 -0
  54. data/lib/seeing_is_believing/version.rb +3 -0
  55. data/lib/seeing_is_believing/wrap_expressions.rb +265 -0
  56. data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +19 -0
  57. data/lib/seeing_is_believing.rb +69 -0
  58. data/seeing_is_believing.gemspec +84 -0
  59. data/spec/binary/alignment_specs.rb +27 -0
  60. data/spec/binary/comment_lines_spec.rb +852 -0
  61. data/spec/binary/config_spec.rb +831 -0
  62. data/spec/binary/engine_spec.rb +114 -0
  63. data/spec/binary/format_comment_spec.rb +210 -0
  64. data/spec/binary/marker_spec.rb +71 -0
  65. data/spec/binary/remove_annotations_spec.rb +342 -0
  66. data/spec/binary/rewrite_comments_spec.rb +106 -0
  67. data/spec/code_spec.rb +233 -0
  68. data/spec/debugger_spec.rb +45 -0
  69. data/spec/evaluate_by_moving_files_spec.rb +204 -0
  70. data/spec/event_stream_spec.rb +762 -0
  71. data/spec/hard_core_ensure_spec.rb +120 -0
  72. data/spec/hash_struct_spec.rb +514 -0
  73. data/spec/seeing_is_believing_spec.rb +1094 -0
  74. data/spec/sib_spec_helpers/version.rb +17 -0
  75. data/spec/spec_helper.rb +26 -0
  76. data/spec/spec_helper_spec.rb +16 -0
  77. data/spec/wrap_expressions_spec.rb +1013 -0
  78. metadata +340 -0
@@ -0,0 +1,831 @@
1
+ require 'spec_helper'
2
+ require 'seeing_is_believing/binary/config'
3
+
4
+
5
+ RSpec.describe SeeingIsBelieving::Binary::Config do
6
+ RSpec::Matchers.define :have_error do |error_assertion|
7
+ match do |config|
8
+ config.errors.find do |error|
9
+ case error_assertion
10
+ when Regexp
11
+ error_assertion =~ error.to_s
12
+ else
13
+ error.to_s.include? error_assertion
14
+ end
15
+ end
16
+ end
17
+
18
+ failure_message do |options|
19
+ "#{error_assertion.inspect} should have matched one of the errors: #{options[:errors].inspect}"
20
+ end
21
+
22
+ failure_message_when_negated do |options|
23
+ "#{error_assertion.inspect} should NOT have matched any of the errors: #{options[:errors].inspect}"
24
+ end
25
+ end
26
+
27
+ let(:matrix_file) { 'seeing_is_believing/the_matrix' }
28
+ let(:default_markers) { SeeingIsBelieving::Binary::Markers.new }
29
+
30
+ def parse(args)
31
+ described_class.new.parse_args(args)
32
+ end
33
+
34
+ def assert_deprecated(flag, *args)
35
+ deprecated_args = parse([flag, *args]).deprecations
36
+ expect(deprecated_args.size).to eq 1
37
+ deprecated = deprecated_args.first
38
+ expect(deprecated.args).to eq [flag, *args]
39
+ expect(deprecated.explanation).to be_a_kind_of String
40
+ expect(deprecated.to_s).to include "Deprecated"
41
+ end
42
+
43
+ shared_examples 'it requires a positive int argument' do |flags|
44
+ it 'expects an integer argument' do
45
+ flags.each do |flag|
46
+ expect(parse([flag, '1'])).to_not have_error /#{flag}/
47
+ expect(parse([flag, '0'])).to have_error /#{flag}/
48
+ expect(parse([flag, '-1'])).to have_error /#{flag}/
49
+ expect(parse([flag, '1.0'])).to have_error /#{flag}/
50
+ expect(parse([flag, 'a'])).to have_error /#{flag}/
51
+ expect(parse([flag, '' ])).to have_error /#{flag}/
52
+ expect(parse([flag ])).to have_error /#{flag}/
53
+ end
54
+ end
55
+ end
56
+
57
+ shared_examples 'it requires a non-negative float or int' do |flags|
58
+ it 'expects a non-negative float or int argument' do
59
+ flags.each do |flag|
60
+ expect(parse([flag, '1'])).to_not have_error /#{flag}/
61
+ expect(parse([flag, '0'])).to_not have_error /#{flag}/
62
+ expect(parse([flag, '-1'])).to have_error /#{flag}/
63
+ expect(parse([flag,'-1.0'])).to have_error /#{flag}/
64
+ expect(parse([flag, '1.0'])).to_not have_error /#{flag}/
65
+ expect(parse([flag, 'a'])).to have_error /#{flag}/
66
+ expect(parse([flag, '' ])).to have_error /#{flag}/
67
+ expect(parse([flag ])).to have_error /#{flag}/
68
+ end
69
+ end
70
+ end
71
+
72
+ shared_examples 'it can extract its argument from conjoined shortflags' do |flag, arg, verify|
73
+ it 'can be the only item (the argument has no space)' do
74
+ instance_exec parse(%W[-#{flag}#{arg}]), &verify
75
+
76
+ # sanity check the verifier by giving it someting it should fail on
77
+ expect { instance_exec parse(%W[-#{flag}#{arg}X]), &verify }.to raise_error RSpec::Expectations::ExpectationNotMetError
78
+ end
79
+
80
+ it 'can appear after another flag' do
81
+ expect( parse(%W[-#{flag}#{arg}] )[:debug]).to eq false
82
+ expect( parse(%W[-g#{flag}#{arg}] )[:debug]).to eq true
83
+ instance_exec parse(%W[-g#{flag}#{arg}] ), &verify
84
+ end
85
+ end
86
+
87
+ describe 'parsing from args' do
88
+ it 'does not mutate the input array' do
89
+ ary = ['a']
90
+ parse(ary)
91
+ expect(ary).to eq ['a']
92
+ end
93
+
94
+ it 'correctly parses multiple args' do
95
+ config = parse(%w[filename -h -r torequire])
96
+ expect(config.filename).to eq 'filename'
97
+ expect(config.lib_options.require_files).to include 'torequire'
98
+ expect(config.print_help?).to eq true
99
+ expect(config.errors).to be_empty
100
+ end
101
+
102
+ # This sill dance is b/c equality assertions are annoying and not relevant anywhere else
103
+ # Don't like having to go implement this stuff just for a few high-level tests.
104
+ def flat_options(config)
105
+ flat_keys = config.keys - [:lib_options, :annotator_options]
106
+ flat_keys.each_with_object({}) { |key, hash| hash[key] = config[key] }
107
+ end
108
+ def assert_same_flat_opts(args1, args2)
109
+ flatopts1 = flat_options parse args1
110
+ flatopts2 = flat_options parse args2
111
+ expect(flatopts1).to eq flatopts2
112
+ end
113
+ it 'can interpret conjoined short-flags' do
114
+ assert_same_flat_opts ['-hjg'], ['-h', '-j', '-g'] # help, json, debug
115
+ end
116
+ it 'can interpret conjoined short-flags where one of them is h+' do
117
+ assert_same_flat_opts ['-h+jg'], ['-h+', '-j', '-g']
118
+ assert_same_flat_opts ['-jh+g'], ['-j', '-h+', '-g']
119
+ assert_same_flat_opts ['-jgh+'], ['-j', '-g', '-h+']
120
+ end
121
+
122
+ describe 'ignore_unknown_flags?' do
123
+ it 'is false by default' do
124
+ expect(parse([]).ignore_unknown_flags?).to eq false
125
+ end
126
+
127
+ it 'is set to true when it sees the --ignore-unknown-options flag' do
128
+ expect(parse(['--ignore-unknown-flags']).ignore_unknown_flags?).to eq true
129
+ end
130
+
131
+ specify 'when false, unknown flags set an error' do
132
+ expect(parse(['--xyz' ])).to have_error '--xyz is not a flag'
133
+ expect(parse(['-y' ])).to have_error '-y is not a flag'
134
+ expect(parse(['-y', 'b'])).to have_error '-y is not a flag'
135
+ expect(parse(['-+h' ])).to have_error '-+ is not a flag'
136
+ end
137
+
138
+ specify 'when true, unknown flags do not set an error' do
139
+ expect(parse(['--zomg'])).to have_error /zomg/
140
+ expect(parse(['--ignore-unknown-flags', '--zomg'])).to_not have_error /zomg/
141
+ expect(parse(['--zomg', '--ignore-unknown-flags'])).to_not have_error /zomg/
142
+ end
143
+ end
144
+
145
+ describe 'filename and lib_options.filename' do
146
+ specify 'default to nil' do
147
+ expect(parse([]).filename).to be_nil
148
+ expect(parse([]).lib_options.filename).to be_nil
149
+ end
150
+
151
+ specify 'both filename and lib_options.filename are set when a filename is seen' do
152
+ config = parse ['a']
153
+ expect(config.filename).to eq 'a'
154
+ expect(config.lib_options.filename).to eq 'a'
155
+ end
156
+
157
+ specify 'the filename is a nonflag / nonarg' do
158
+ # nonflag / nonarg
159
+ expect(parse(['-x']).filename).to eq nil
160
+ expect(parse(['-n', '3']).filename).to eq nil
161
+
162
+ # find it amidst flags/largs
163
+ expect(parse(['a']).filename).to eq 'a'
164
+ expect(parse(['-x', 'a']).filename).to eq 'a'
165
+ expect(parse(['a', '-x']).filename).to eq 'a'
166
+ expect(parse(['a', '-n', '3']).filename).to eq 'a'
167
+ expect(parse(['-n', '3', 'a', '-r', 'f']).filename).to eq 'a'
168
+ end
169
+
170
+ it 'does not confuse filenames with unknown args' do
171
+ unknown_arg = '-y'
172
+ expect(parse([unknown_arg]).filename).to be_nil
173
+ end
174
+
175
+ it 'sets an error if given multiple filenames' do
176
+ expect(parse([]).errors).to be_empty
177
+ expect(parse(['a']).errors).to be_empty
178
+ expect(parse(['a', 'b'])).to have_error /"a", "b"/
179
+ end
180
+
181
+ specify '-a and --as set lib_options.filename, but not filename' do
182
+ expect(parse(%w[-a abc]).filename).to eq nil
183
+ expect(parse(%w[--as abc]).filename).to eq nil
184
+ expect(parse(%w[-a abc]).lib_options.filename).to eq 'abc'
185
+ expect(parse(%w[--as abc]).lib_options.filename).to eq 'abc'
186
+ end
187
+
188
+ specify '-a and --as always win over a filename' do
189
+ config = parse(['fn', '-a', 'as'])
190
+ expect(config.filename).to eq 'fn'
191
+ expect(config.lib_options.filename).to eq 'as'
192
+
193
+ config = parse(['-a', 'as', 'fn'])
194
+ expect(config.filename).to eq 'fn'
195
+ expect(config.lib_options.filename).to eq 'as'
196
+ end
197
+
198
+ it 'sets an error if -a/--as are given without the filename to execute as' do
199
+ expect(parse(%w[-a f])).to_not have_error /-a/
200
+ expect(parse(%w[-as f])).to_not have_error /--as/
201
+ expect(parse(%w[-a ])).to have_error /-a/
202
+ expect(parse(%w[--as ])).to have_error /--as/
203
+ end
204
+
205
+ it_behaves_like 'it can extract its argument from conjoined shortflags', 'a', 'some-filename.whatever', -> parsed do
206
+ expect(parsed.lib_options.filename).to eq 'some-filename.whatever'
207
+ end
208
+ end
209
+
210
+
211
+ describe 'lib_options.local_cwd?' do
212
+ it 'defaults to false' do
213
+ expect(parse([]).lib_options.local_cwd?).to eq false
214
+ end
215
+ it 'is set to true by --local-cwd' do
216
+ expect(parse(['--local-cwd']).lib_options.local_cwd?).to eq true
217
+ end
218
+ end
219
+
220
+
221
+ describe 'annotator_options.max_result_length' do
222
+ it 'defaults to infinity' do
223
+ expect(parse([]).annotator_options.max_result_length).to eq Float::INFINITY
224
+ end
225
+
226
+ it 'is set with -D and --result-length' do
227
+ expect(parse(['-D', '10']).annotator_options.max_result_length).to eq 10
228
+ expect(parse(['--result-length', '10']).annotator_options.max_result_length).to eq 10
229
+ end
230
+
231
+ it_behaves_like 'it requires a positive int argument', ['-D', '--result-length']
232
+
233
+ it_behaves_like 'it can extract its argument from conjoined shortflags', 'D', '12', -> parsed do
234
+ expect(parsed.annotator_options.max_result_length).to eq 12
235
+ end
236
+ end
237
+
238
+ describe 'annotator_options.max_line_length' do
239
+ it 'defaults to infinity' do
240
+ expect(parse([]).annotator_options.max_line_length).to eq Float::INFINITY
241
+ end
242
+
243
+ it 'is set with -d and --line-length' do
244
+ expect(parse(['-d', '10']).annotator_options.max_line_length).to eq 10
245
+ expect(parse(['--line-length', '10']).annotator_options.max_line_length).to eq 10
246
+ end
247
+
248
+ it_behaves_like 'it requires a positive int argument', ['-d', '--line-length']
249
+
250
+ it_behaves_like 'it can extract its argument from conjoined shortflags', 'd', '12', -> parsed do
251
+ expect(parsed.annotator_options.max_line_length).to eq 12
252
+ end
253
+ end
254
+
255
+ describe 'lib_options.require_files' do
256
+ it 'defaults to the matrix file array' do
257
+ expect(parse([]).lib_options.require_files).to eq [matrix_file]
258
+ end
259
+
260
+ it 'appends pp for xmpfilter style' do
261
+ expect(parse(['-x']).lib_options.require_files).to eq [matrix_file, 'pp']
262
+ end
263
+
264
+ specify '-r and --require set an error if not provided with a filename' do
265
+ expect(parse(['--require', 'f'])).to_not have_error /-r/
266
+ expect(parse(['-r'])).to have_error /-r\b/
267
+ expect(parse(['--require'])).to have_error /--require\b/
268
+ end
269
+
270
+ specify '-r and --require add the filename into the result array' do
271
+ expect(parse(%w[-r f1 --require f2]).lib_options.require_files).to eq [matrix_file, 'f1', 'f2']
272
+ end
273
+
274
+ it_behaves_like 'it can extract its argument from conjoined shortflags', 'r', 'filename', -> parsed do
275
+ expect(parsed.lib_options.require_files).to include 'filename'
276
+ end
277
+ end
278
+
279
+ describe 'print_help? and help_screen' do
280
+ let(:help_screen) { SeeingIsBelieving::Binary.help_screen default_markers }
281
+ let(:help_screen_extended) { SeeingIsBelieving::Binary.help_screen_extended default_markers }
282
+
283
+ specify 'print_help? defaults to false' do
284
+ expect(parse([]).print_help?).to eq false
285
+ end
286
+
287
+ specify 'help_screen defaults to the short help screen' do
288
+ expect(parse([]).help_screen).to eq help_screen
289
+ end
290
+
291
+ it 'print_help? is set to true with -h, --help, -h+, and --help+' do
292
+ expect(parse(['-h']).print_help?).to eq true
293
+ expect(parse(['-h+']).print_help?).to eq true
294
+ expect(parse(['--help']).print_help?).to eq true
295
+ expect(parse(['--help+']).print_help?).to eq true
296
+ end
297
+
298
+ specify '-h and --help set help_screen to the short help screen' do
299
+ expect(parse(['-h']).help_screen).to eq help_screen
300
+ expect(parse(['--help']).help_screen).to eq help_screen
301
+ end
302
+
303
+ specify '-h+ and --help+ set help_screen to the extended help screen' do
304
+ expect(parse(['-h+']).help_screen).to eq help_screen_extended
305
+ expect(parse(['--help+']).help_screen).to eq help_screen_extended
306
+ end
307
+ end
308
+
309
+
310
+ describe 'body' do
311
+ it 'defaults to nil' do
312
+ expect(parse([]).body).to eq nil
313
+ end
314
+
315
+ it 'is set by an arg to -e and --program' do
316
+ expect(parse(['-e', '1']).body).to eq '1'
317
+ expect(parse(['--program', '1']).body).to eq '1'
318
+ end
319
+
320
+ it 'sets an error if -e and --program are not given an arg' do
321
+ expect(parse([])).to_not have_error /-e/
322
+ expect(parse([])).to_not have_error /--program/
323
+ expect(parse(['-e', 'body'])).to_not have_error /-e/
324
+ expect(parse(['-e' ])).to have_error /-e/
325
+ expect(parse(['--program', 'body'])).to_not have_error /--program/
326
+ expect(parse(['--program' ])).to have_error /--program/
327
+ end
328
+
329
+ it_behaves_like 'it can extract its argument from conjoined shortflags', 'e', 'some program', -> parsed do
330
+ expect(parsed.body).to eq 'some program'
331
+ end
332
+ end
333
+
334
+ describe'lib_options.load_path_dirs' do
335
+ let(:lib_path) { File.realpath '../../lib', __dir__ }
336
+
337
+ it 'defaults to sib\'s lib path' do
338
+ expect(parse([]).lib_options.load_path_dirs).to eq [lib_path]
339
+ end
340
+
341
+ specify '-I and --load-path add their arguments to it' do
342
+ expect(parse(%w[-I f1 --load-path f2]).lib_options.load_path_dirs).to eq [lib_path, 'f1', 'f2']
343
+ end
344
+
345
+ it 'sets an error if not provided with a dir' do
346
+ expect(parse(['--load-path', 'f'])).to_not have_error /--load-path/
347
+ expect(parse(['-I'])).to have_error /-I\b/
348
+ expect(parse(['--load-path'])).to have_error /--load-path\b/
349
+ end
350
+
351
+ it_behaves_like 'it can extract its argument from conjoined shortflags', 'I', 'added-path', -> parsed do
352
+ expect(parsed.lib_options.load_path_dirs).to include 'added-path'
353
+ end
354
+ end
355
+
356
+ describe 'lib_options.encoding' do
357
+ it 'defaults to nil' do
358
+ expect(parse([]).lib_options.encoding).to be_nil
359
+ end
360
+
361
+ specify '-K and --encoding sets the encoding to the next argument' do
362
+ expect(parse(%w[-K u]).lib_options.encoding).to eq 'u'
363
+ expect(parse(%w[--encoding u]).lib_options.encoding).to eq 'u'
364
+ end
365
+
366
+ specify 'with -K, the argument can be placed immediately after it (e.g. -Ku) because Ruby allows this' do
367
+ expect(parse(['-Ku']).lib_options.encoding).to eq 'u'
368
+ expect(parse(['-Ku'])).to_not have_error /-K/
369
+ end
370
+
371
+ it 'sets an error if not provided with an encoding' do
372
+ expect(parse(['-Ku'])).to_not have_error /-K/
373
+ expect(parse(['-K u'])).to_not have_error /-K/
374
+ expect(parse(['--encoding', 'u'])).to_not have_error /--encoding/
375
+ expect(parse(['-K'])).to have_error /-K/
376
+ expect(parse(['--encoding'])).to have_error /--encoding/
377
+ end
378
+
379
+ it_behaves_like 'it can extract its argument from conjoined shortflags', 'K', 'u', -> parsed do
380
+ expect(parsed.lib_options.encoding).to eq 'u'
381
+ end
382
+
383
+ it 'is deprecated' do
384
+ assert_deprecated '--encoding', 'u'
385
+ assert_deprecated '-K', 'u'
386
+ assert_deprecated '-Ku'
387
+ end
388
+ end
389
+
390
+ describe '.print_cleaned?' do
391
+ it 'defaults to false' do
392
+ expect(parse([]).print_cleaned?).to eq false
393
+ end
394
+
395
+ it 'can be set with -c and --clean' do
396
+ expect(parse(%w[-c]).print_cleaned?).to eq true
397
+ expect(parse(%w[--clean]).print_cleaned?).to eq true
398
+ end
399
+ end
400
+
401
+ describe 'print_version?' do
402
+ it 'defaults to false' do
403
+ expect(parse([]).print_version?).to eq false
404
+ end
405
+
406
+ it 'can be set with -v and --version' do
407
+ expect(parse(%w[-v]).print_version?).to eq true
408
+ expect(parse(%w[--version]).print_version?).to eq true
409
+ end
410
+ end
411
+
412
+ describe 'timeout and lib_options.timeout_seconds' do
413
+ it 'defaults to 0 (never timeout)' do
414
+ expect(parse([]).timeout_seconds).to eq 0
415
+ expect(parse([]).lib_options.timeout_seconds).to eq 0
416
+ end
417
+
418
+ it 'can be set with -t and --timeout-seconds' do
419
+ expect(parse(['-t', '1.1']).timeout_seconds).to eq 1.1
420
+ expect(parse(['-t', '1.1']).lib_options.timeout_seconds).to eq 1.1
421
+ expect(parse(['--timeout-seconds', '1.2']).lib_options.timeout_seconds).to eq 1.2
422
+ end
423
+
424
+ it 'can be set with the deprecated flag --timeout' do
425
+ expect(parse(['--timeout', '1.3']).lib_options.timeout_seconds).to eq 1.3
426
+ assert_deprecated '--timeout', 1.4
427
+ end
428
+
429
+ it_behaves_like 'it requires a non-negative float or int', ['-t', '--timeout-seconds', '--timeout']
430
+
431
+ it_behaves_like 'it can extract its argument from conjoined shortflags', 't', '123', -> parsed do
432
+ expect(parsed.lib_options.timeout_seconds).to eq 123
433
+ end
434
+ end
435
+
436
+ describe 'annotator_options.alignment_strategy' do
437
+ let(:align_chunk) { SeeingIsBelieving::Binary::AlignChunk }
438
+ let(:align_file) { SeeingIsBelieving::Binary::AlignFile }
439
+ let(:align_line) { SeeingIsBelieving::Binary::AlignLine }
440
+
441
+ it 'defaults to AlignChunk' do
442
+ expect(parse([]).annotator_options.alignment_strategy)
443
+ .to eq align_chunk
444
+ end
445
+
446
+ specify '-s and --alignment-strategy sets the alignment strategy' do
447
+ expect(parse(['-s', 'file']).annotator_options.alignment_strategy)
448
+ .to eq align_file
449
+
450
+ expect(parse(['--alignment-strategy', 'file']).annotator_options.alignment_strategy)
451
+ .to eq align_file
452
+ end
453
+
454
+ it 'accepts values: file, line, chunk' do
455
+ expect(parse(['-s', 'file']).annotator_options.alignment_strategy).to eq align_file
456
+ expect(parse(['-s', 'line']).annotator_options.alignment_strategy).to eq align_line
457
+ expect(parse(['-s', 'chunk']).annotator_options.alignment_strategy).to eq align_chunk
458
+ end
459
+
460
+ it 'sets an error if not provided with a strategy' do
461
+ expect(parse(['-s'])).to have_error /-s/
462
+ expect(parse(['-s', 'file'])).to_not have_error /-s/
463
+ end
464
+
465
+ it 'sets an error if provided with an unknown alignment strategy' do
466
+ expect(parse(['-s', 'file'])).to_not have_error '-s'
467
+ expect(parse(['-s', 'unknown'])).to have_error '-s', 'expected one of'
468
+ end
469
+
470
+ it_behaves_like 'it can extract its argument from conjoined shortflags', 's', 'file', -> parsed do
471
+ expect(parsed.annotator_options.alignment_strategy).to eq align_file
472
+ end
473
+ end
474
+
475
+ describe 'inherit_exitstatus?' do
476
+ it 'defaults to false' do
477
+ expect(parse([]).inherit_exitstatus?).to eq false
478
+ end
479
+
480
+ it 'can be set with --inherit-exitstatus, -i' do
481
+ expect(parse(['--inherit-exitstatus']).inherit_exitstatus?).to be true
482
+ expect(parse(['-i']).inherit_exitstatus?).to be true
483
+ end
484
+
485
+ it 'can be set with the deprecated --inherit-exit-status' do
486
+ expect(parse(['--inherit-exit-status']).inherit_exitstatus?).to be true
487
+ assert_deprecated '--inherit-exit-status'
488
+ end
489
+ end
490
+
491
+ describe 'annotator and lib_options.rewrite_code' do
492
+ specify 'annotator defaults to AnnotateEveryLine' do
493
+ expect(parse([]).annotator).to be SeeingIsBelieving::Binary::AnnotateEveryLine
494
+ end
495
+
496
+ specify 'annotator can be set to AnnotateMarkedLines with --xmpfilter-style or -x' do
497
+ expect(parse(['--xmpfilter-style']).annotator).to eq SeeingIsBelieving::Binary::AnnotateMarkedLines
498
+ expect(parse(['-x']).annotator).to eq SeeingIsBelieving::Binary::AnnotateMarkedLines
499
+ end
500
+
501
+ specify 'lib_options.rewrite_code is set to the xmpfilter rewriter on -x and --xmpfilter-style' do
502
+ # not a great test, but the cukes hit its actual behaviour
503
+ expect(parse([]).lib_options.rewrite_code.call("1 # =>\n")).to_not include "pp"
504
+ expect(parse(['-x']).lib_options.rewrite_code.call("1\n# =>\n")).to include "pp"
505
+ end
506
+ end
507
+
508
+ describe 'remove_value_prefixes?' do
509
+ it 'defaults to true' do
510
+ expect(parse([]).remove_value_prefixes?).to eq true
511
+ end
512
+ it 'is false when xmpfilter style is specified' do
513
+ expect(parse(['-x']).remove_value_prefixes?).to eq false
514
+ end
515
+ end
516
+
517
+ describe '-g and --debug' do
518
+ specify 'set debug to true' do
519
+ expect(parse([])[:debug]).to eq false
520
+ expect(parse([]).debug?).to eq false
521
+ expect(parse(['-g'])[:debug]).to eq true
522
+ expect(parse(['-g']).debug?).to eq true
523
+ expect(parse(['--debug'])[:debug]).to eq true
524
+ expect(parse(['--debug']).debug?).to eq true
525
+ end
526
+ end
527
+
528
+ describe '--debug-to FILE' do
529
+ specify 'sets debug to FILE' do
530
+ expect(parse([])[:debug]).to eq false
531
+ expect(parse(['--debug-to', 'somefile'])[:debug]).to eq 'somefile'
532
+ end
533
+
534
+ specify 'sets an error if not given a next arg for the filename' do
535
+ expect(parse(['--debug-to', 'somefile'])).to_not have_error /--debug-to/
536
+ expect(parse(['--debug-to'])).to have_error /--debug-to/
537
+ end
538
+ end
539
+
540
+ describe '--shebang' do
541
+ it 'is added to the list of deprecated flags' do
542
+ assert_deprecated '--shebang', 'not_ruby'
543
+ end
544
+
545
+ it 'sets an error if not given a next arg to execute' do
546
+ expect(parse([])).to_not have_error /--shebang/
547
+ expect(parse(['--shebang', 'arg'])).to_not have_error /--shebang/
548
+ expect(parse(['--shebang'])).to have_error /--shebang/
549
+ end
550
+ end
551
+
552
+ describe 'lib_options.max_line_captures' do
553
+ it 'defaults to infinity' do
554
+ expect(parse([]).lib_options.max_line_captures).to eq Float::INFINITY
555
+ end
556
+
557
+ it 'can be set with --max-line-captures or -n' do
558
+ expect(parse(['-n', '10']).lib_options.max_line_captures).to eq 10
559
+ expect(parse(['--max-line-captures', '10']).lib_options.max_line_captures).to eq 10
560
+ end
561
+
562
+ it 'can be set with the deprecated flag --number-of-captures' do
563
+ expect(parse(['--number-of-captures', '12']).lib_options.max_line_captures).to eq 12
564
+ assert_deprecated '--number-of-captures', '12'
565
+ assert_deprecated '--number-of-captures'
566
+ end
567
+
568
+ it_behaves_like 'it requires a positive int argument', ['-n', '--max-line-captures', '--number-of-captures']
569
+
570
+ it_behaves_like 'it can extract its argument from conjoined shortflags', 'n', '12', -> parsed do
571
+ expect(parsed.lib_options.max_line_captures).to eq 12
572
+ end
573
+ end
574
+
575
+ describe 'result_as_json?' do
576
+ it 'defaults to false' do
577
+ expect(parse([]).result_as_json?).to eq false
578
+ end
579
+
580
+ it 'can be enabled with --json or -j' do
581
+ expect(parse(['--json']).result_as_json?).to eq true
582
+ expect(parse(['-j']).result_as_json?).to eq true
583
+ end
584
+
585
+ it 'sets an error if specified with xmpfilter' do
586
+ expect(parse(['--json'])).to_not have_error /json/
587
+ expect(parse(['--json', '-x'])).to have_error /json/
588
+ expect(parse(['--json', '--xmpfilter-style'])).to have_error /json/
589
+ expect(parse(['-x', '--json'])).to have_error /json/
590
+ expect(parse(['--xmpfilter-style', '--json'])).to have_error /json/
591
+ expect(parse(['-j', '-x'])).to have_error /json/
592
+ expect(parse(['-j', '-x'])).to have_error /xmpfilter/
593
+ end
594
+ end
595
+
596
+ describe 'markers' do
597
+ it 'defaults to a hash with :value, :exception, :stdout, and :stderr' do
598
+ expect(default_markers.keys).to eq [:value, :exception, :stdout, :stderr]
599
+ end
600
+
601
+ specify 'each default marker regex can re-find the the marker' do
602
+ default_markers.each do |name, marker|
603
+ comment = "#{marker.prefix}abc"
604
+ extracted = comment[marker.regex]
605
+ expect(extracted).to eq(marker.prefix)
606
+ end
607
+ end
608
+
609
+ it('defaults :value to "# => "') { expect(default_markers.value .prefix).to eq "# => " }
610
+ it('defaults :exception to "# ~> "') { expect(default_markers.exception.prefix).to eq "# ~> " }
611
+ it('defaults :stdout to "# >> "') { expect(default_markers.stdout .prefix).to eq "# >> " }
612
+ it('defaults :stderr to "# !> "') { expect(default_markers.stderr .prefix).to eq "# !> " }
613
+ end
614
+
615
+ describe 'print_event_stream?' do
616
+ it 'print_event_stream? is false by default' do
617
+ expect(parse([]).print_event_stream?).to eq false
618
+ end
619
+ it 'print_event_stream? can be turned on with --stream' do
620
+ expect(parse(['--stream']).print_event_stream?).to eq true
621
+ end
622
+ it 'adds an error if --stream is used with --json' do
623
+ expect(parse(['--stream'])).to_not have_error '--stream'
624
+ expect(parse(['--stream', '--json'])).to have_error '--stream'
625
+ expect(parse(['--json', '--stream'])).to have_error '--stream'
626
+ end
627
+ it 'adds an error if --stream is used with -x or --xmpfilter-style' do
628
+ expect(parse(['--stream'])).to_not have_error '--stream'
629
+ expect(parse(['--stream', '-x'])).to have_error '--stream'
630
+ expect(parse(['-x', '--stream'])).to have_error '--stream'
631
+ expect(parse(['--xmpfilter-style', '--stream'])).to have_error '--stream'
632
+ end
633
+ end
634
+
635
+ describe 'align_results?' do
636
+ it 'defaults to true' do
637
+ expect(parse([]).annotator_options.interline_align?).to eq true
638
+ end
639
+ it 'can be turned on with --interline-align' do
640
+ expect(parse(['--interline-align'])).to_not have_error '--interline-align'
641
+ expect(parse(['--interline-align']).annotator_options.interline_align?).to eq true
642
+ end
643
+ it 'can be turned off with --no-interline-align' do
644
+ expect(parse(['--no-interline-align'])).to_not have_error '--no-interline-align'
645
+ expect(parse(['--no-interline-align']).annotator_options.interline_align?).to eq false
646
+ end
647
+ end
648
+
649
+ describe 'toggle_mark/toggle_mark?' do
650
+ it 'defaults to nil/false' do
651
+ expect(parse([]).toggle_mark).to eq nil
652
+ expect(parse([]).toggle_mark?).to eq false
653
+ end
654
+ it 'can be set to true/linenum with --toggle-mark' do
655
+ expect(parse(['--toggle-mark', '123']).toggle_mark).to eq 123
656
+ expect(parse(['--toggle-mark', '123']).toggle_mark?).to eq true
657
+ end
658
+ it_behaves_like 'it requires a positive int argument', ['--toggle-mark']
659
+ it 'adds an error if used with --json or --stream' do
660
+ # expect(parse(['--toggle-mark', '123'])).to_not have_error /--toggle-mark/
661
+ expect(parse(['--stream', '--toggle-mark', '123'])).to have_error /--toggle-mark/
662
+ expect(parse(['--toggle-mark', '123', '--stream'])).to have_error /--toggle-mark/
663
+ expect(parse(['--json', '--toggle-mark', '123'])).to have_error /--toggle-mark/
664
+ expect(parse(['--toggle-mark', '123', '--json'])).to have_error /--toggle-mark/
665
+ end
666
+ end
667
+ end
668
+
669
+
670
+ describe '.finalize' do
671
+ let(:stdin_data) { 'stdin data' }
672
+ let(:stdin) { object_double $stdin, read: stdin_data, set_encoding: nil }
673
+ let(:stdout) { object_double $stdout }
674
+ let(:stderr) { object_double $stderr, :tty? => true }
675
+
676
+ let(:file_class) { class_double File }
677
+ let(:nonexisting_filename) { 'badfilename' }
678
+ let(:existing_filename) { 'goodfilename' }
679
+ let(:file_body) { 'good file body' }
680
+
681
+ before do
682
+ allow(file_class).to receive(:exist?).with(existing_filename).and_return(true)
683
+ allow(file_class).to receive(:exist?).with(nonexisting_filename).and_return(false)
684
+ allow(file_class).to receive(:read).with(existing_filename, any_args).and_return(file_body)
685
+ end
686
+
687
+ def call(attrs={})
688
+ described_class.new(attrs).finalize(stdin, stdout, stderr, file_class)
689
+ end
690
+
691
+ describe 'additional errors' do
692
+ it 'sets an error if given a filename and a program body -- cannot have two body sources' do
693
+ allow(file_class).to receive(:exist?).with('f')
694
+ matcher = /program body and a filename/
695
+ expect(call filename: 'f', body: 'b').to have_error matcher
696
+ expect(call filename: 'f', body: 'b').to have_error matcher
697
+ expect(call filename: nil, body: 'b').to_not have_error matcher
698
+ expect(call filename: 'f', body: nil).to_not have_error matcher
699
+ end
700
+
701
+ it 'sets an error if the provided filename DNE' do
702
+ expect(call filename: existing_filename).to_not have_error /filename/
703
+ expect(call filename: nonexisting_filename).to have_error /filename/
704
+ end
705
+ end
706
+
707
+ describe 'setting the body' do
708
+ it 'does not override the body if already set e.g. with -e' do
709
+ expect(call(body: 'b', filename: nil).body).to eq 'b'
710
+ end
711
+
712
+ it 'is the file body if the filename is provided and exists' do
713
+ expect(call(body: nil, filename: existing_filename).body)
714
+ .to eq file_body
715
+ end
716
+
717
+ it 'is an empty string if the filename is provided but DNE' do
718
+ expect(call(body: nil, filename: nonexisting_filename).body)
719
+ .to eq nil
720
+ end
721
+
722
+ it 'reads the body from stdin if not given a filename or body' do
723
+ expect(call(body: nil, filename: nil).body).to eq stdin_data
724
+ end
725
+
726
+ it 'is set to an empty string when we aren\'t evaluating (e.g. when printing version info)' do
727
+ expect(call( ).body).to be_a_kind_of String
728
+ expect(call(print_version: true).body).to eq ''
729
+ expect(call(print_help: true).body).to eq ''
730
+ expect(call(errors: ['e']).body).to eq ''
731
+ end
732
+ end
733
+
734
+ describe 'lib_options.stdin' do
735
+ let(:default) { SeeingIsBelieving::Options.new.stdin }
736
+
737
+ it 'is the default when we aren\'t evaluating' do
738
+ [ {errors: ['e']},
739
+ {filename: existing_filename, body: 'b'},
740
+ {filename: nonexisting_filename},
741
+ {print_version: true},
742
+ {print_help: true},
743
+ ].each do |overrides|
744
+ expect(call(overrides).lib_options.stdin).to eq default
745
+ end
746
+ end
747
+
748
+ it 'is the default when the program was taken off stdin' do
749
+ expect(call.lib_options.stdin).to eq default
750
+ expect(call(body: 'b').lib_options.stdin).to_not eq default
751
+ end
752
+
753
+ it 'is the stdin stream when the program body was provided' do
754
+ expect(call(body: 'b').lib_options.stdin).to eq stdin
755
+ end
756
+
757
+ it 'is the stdin stream when the program was pulled from a file' do
758
+ expect(call(filename: existing_filename).lib_options.stdin).to eq stdin
759
+ expect(call(filename: nonexisting_filename).lib_options.stdin).to eq default
760
+ end
761
+ end
762
+
763
+ describe 'lib_options.event_handler' do
764
+ it 'is an UpdateResult handler when print_event_stream? is false' do
765
+ expect(call(print_event_stream: false).lib_options.event_handler)
766
+ .to be_an_instance_of SeeingIsBelieving::EventStream::Handlers::UpdateResult
767
+ end
768
+ it 'is an StreamJsonEvents handler to stdout when print_event_stream? is true' do
769
+ handler = call(print_event_stream: true).lib_options.event_handler
770
+ expect(handler).to be_an_instance_of SeeingIsBelieving::EventStream::Handlers::StreamJsonEvents
771
+ expect(handler.stream).to eq stdout
772
+ end
773
+ end
774
+
775
+ describe 'debugger, lib_options.debugger' do
776
+ specify 'default to a null debugger' do
777
+ handler = call
778
+ expect(handler.debugger).to_not be_enabled
779
+ expect(handler.lib_options.debugger).to_not be_enabled
780
+ end
781
+
782
+ context 'when debug? is true' do
783
+ specify 'are the same debugger' do
784
+ handler = call debug: true
785
+ expect(handler.lib_options.debugger).to equal handler.debugger
786
+ end
787
+
788
+ specify 'print their output to stderr' do
789
+ handler = call debug: true
790
+ expect(handler.debugger).to be_enabled
791
+ expect(handler.debugger.stream).to eq stderr
792
+ end
793
+
794
+ specify 'turn colour on if stderr is a tty' do
795
+ handler = call debug: true
796
+ expect(handler.debugger).to be_coloured
797
+ end
798
+
799
+ specify 'turn colour off if stderr isn\'t a tty' do
800
+ allow(stderr).to receive(:tty?).and_return(false)
801
+ handler = call debug: true
802
+ expect(handler.debugger).to_not be_coloured
803
+ end
804
+ end
805
+
806
+ context 'when debug? is a string' do
807
+ let(:proving_grounds_dir) { File.expand_path '../../proving_grounds', __dir__ }
808
+ let(:path) { File.join proving_grounds_dir, 'test.log' }
809
+ before { Dir.mkdir proving_grounds_dir unless Dir.exist? proving_grounds_dir }
810
+
811
+ specify 'are the same debugger' do
812
+ handler = call debug: path
813
+ expect(handler.lib_options.debugger).to equal handler.debugger
814
+ end
815
+
816
+ specify 'consider the string to be a filename, appending to it' do
817
+ File.write path, "pre"
818
+ debugger = call(debug: path).debugger
819
+ expect(debugger.stream.path).to eq path
820
+ debugger.context("test")
821
+ expect(File.read path).to match /pretest/
822
+ end
823
+
824
+ specify 'do not turn colour on' do
825
+ handler = call debug: path
826
+ expect(handler.debugger).to_not be_coloured
827
+ end
828
+ end
829
+ end
830
+ end
831
+ end