erudite 0.1.0 → 0.2.0
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.
- checksums.yaml +5 -13
- data/CHANGELOG.md +8 -0
- data/LICENSE.md +18 -18
- data/README.md +54 -16
- data/bin/erudite +6 -0
- data/lib/erudite.rb +10 -9
- data/lib/erudite/example.rb +82 -79
- data/lib/erudite/executable.rb +35 -0
- data/lib/erudite/outcome.rb +19 -19
- data/lib/erudite/parser.rb +54 -54
- data/spec/erudite/example_spec.rb +366 -352
- data/spec/erudite/executable_spec.rb +76 -0
- data/spec/erudite/outcome_spec.rb +53 -53
- data/spec/erudite/parser_spec.rb +104 -104
- data/spec/erudite_spec.rb +0 -1
- data/spec/spec_helper.rb +3 -0
- metadata +30 -11
@@ -1,352 +1,366 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
expect(example.expected
|
24
|
-
expect(example.expected.
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
expect(example.binding
|
44
|
-
expect(example.binding).
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
example
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
expect(example.run.
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
expect(example.run.
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
expect(
|
177
|
-
end
|
178
|
-
|
179
|
-
it '
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
it '
|
201
|
-
|
202
|
-
expect(
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
it '
|
233
|
-
|
234
|
-
expect(
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
example
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
example
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
expect(example).to
|
303
|
-
end
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
it
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
a
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
a
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'English'
|
5
|
+
|
6
|
+
describe Erudite::Example do
|
7
|
+
it 'requires some source' do
|
8
|
+
expect { described_class.new }.to raise_error(ArgumentError)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'can be initialized' do
|
12
|
+
expect(described_class.new(nil)).to be_a(described_class)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'sets the source' do
|
16
|
+
source = double
|
17
|
+
example = described_class.new(source)
|
18
|
+
expect(example.source).to eql(source)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'uses the default expected outcome' do
|
22
|
+
example = described_class.new(nil)
|
23
|
+
expect(example.expected).to be_an(Erudite::Outcome)
|
24
|
+
expect(example.expected.result).to be(nil)
|
25
|
+
expect(example.expected.output).to be(nil)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'sets the expected result' do
|
29
|
+
result = double
|
30
|
+
example = described_class.new(nil, result)
|
31
|
+
expect(example.expected.result).to eql(result)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sets the expected output' do
|
35
|
+
output = double
|
36
|
+
example = described_class.new(nil, nil, output)
|
37
|
+
expect(example.expected.output).to eql(output)
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#binding' do
|
41
|
+
it 'uses the default binding' do
|
42
|
+
example = described_class.new(nil)
|
43
|
+
expect(example.binding).to be_a(Binding)
|
44
|
+
expect(example.binding.eval('self')).to be(TOPLEVEL_BINDING.eval('self'))
|
45
|
+
expect(example.binding).to_not be(TOPLEVEL_BINDING)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'memoizes the binding' do
|
49
|
+
example = described_class.new(nil)
|
50
|
+
binding = example.binding
|
51
|
+
expect(example.binding).to be(binding)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'sets the binding' do
|
55
|
+
example = described_class.new(nil)
|
56
|
+
binding = TOPLEVEL_BINDING.dup
|
57
|
+
example.binding = binding
|
58
|
+
expect(example.binding).to be(binding)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#run!' do
|
63
|
+
it 'evaluates the source' do
|
64
|
+
source = ':something'
|
65
|
+
example = described_class.new(source)
|
66
|
+
expect(example.run!).to eql(TOPLEVEL_BINDING.eval(source))
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'evaluates the source in the binding' do
|
70
|
+
source = 'variable'
|
71
|
+
example = described_class.new(source)
|
72
|
+
example.binding.eval('variable = :something')
|
73
|
+
expect(example.run!).to eql(example.binding.eval(source))
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'sets the file name' do
|
77
|
+
example = described_class.new('fail')
|
78
|
+
begin
|
79
|
+
example.run!
|
80
|
+
rescue => error
|
81
|
+
expect(error.backtrace.first).to_not start_with('<main>:')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'sets the line number' do
|
86
|
+
example = described_class.new('fail')
|
87
|
+
begin
|
88
|
+
example.run!
|
89
|
+
rescue => error
|
90
|
+
expect(error.backtrace.first).to_not end_with(":1:in `<main>'")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '#run' do
|
96
|
+
it 'returns the result' do
|
97
|
+
source = ':something'
|
98
|
+
example = described_class.new(source)
|
99
|
+
expect(example.run.first).to eql(TOPLEVEL_BINDING.eval(source))
|
100
|
+
expect(example.run.last).to be(nil)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'returns the exception' do
|
104
|
+
source = 'fail'
|
105
|
+
example = described_class.new(source)
|
106
|
+
expect(example.run.first).to be(nil)
|
107
|
+
expect(example.run.last).to be_a(StandardError)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '.without_stdio' do
|
112
|
+
it 'requires a block' do
|
113
|
+
expect { described_class.without_stdio }.to raise_error(LocalJumpError)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'calls the block' do
|
117
|
+
result = double
|
118
|
+
expect(described_class.without_stdio { result }.first).to eql(result)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'redirects STDIN' do
|
122
|
+
stdin = $stdin
|
123
|
+
expect(described_class.without_stdio { $stdin }.first).to_not be(stdin)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'redirects STDOUT' do
|
127
|
+
stdout = $stdout
|
128
|
+
expect(described_class.without_stdio { $stdout }.first).to_not be(stdout)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'redirects STDERR' do
|
132
|
+
stderr = $stderr
|
133
|
+
expect(described_class.without_stdio { $stderr }.first).to_not be(stderr)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'clears ARGV' do
|
137
|
+
$ARGV.push(double)
|
138
|
+
result = described_class.without_stdio { $ARGV.dup }.first
|
139
|
+
$ARGV.pop
|
140
|
+
expect(result).to eql([])
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'reconnects STDIN' do
|
144
|
+
stdin = $stdin
|
145
|
+
begin
|
146
|
+
described_class.without_stdio { fail }
|
147
|
+
rescue
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
expect($stdin).to be(stdin)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'reconnects STDOUT' do
|
154
|
+
stdout = $stdout
|
155
|
+
begin
|
156
|
+
described_class.without_stdio { fail }
|
157
|
+
rescue
|
158
|
+
nil
|
159
|
+
end
|
160
|
+
expect($stdout).to be(stdout)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'reconnects STDERR' do
|
164
|
+
stderr = $stderr
|
165
|
+
begin
|
166
|
+
described_class.without_stdio { fail }
|
167
|
+
rescue
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
expect($stderr).to be(stderr)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'restores ARGV' do
|
174
|
+
argv = $ARGV.dup
|
175
|
+
described_class.without_stdio { $ARGV.push('something') }
|
176
|
+
expect($ARGV).to eql(argv)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'returns the redirected IO' do
|
180
|
+
expect(described_class.without_stdio {}.last).to be_a(StringIO)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'handles STDIN' do
|
184
|
+
result, _ = described_class.without_stdio { gets }
|
185
|
+
expect(result).to be(nil)
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'handles STDOUT' do
|
189
|
+
_, io = described_class.without_stdio { puts 'something' }
|
190
|
+
expect(io.string).to eql("something\n")
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'handles STDERR' do
|
194
|
+
_, io = described_class.without_stdio { warn 'something' }
|
195
|
+
expect(io.string).to eql("something\n")
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe '.format_exception' do
|
200
|
+
it 'formats the exception' do
|
201
|
+
exception = StandardError.new('something')
|
202
|
+
expect(described_class.format_exception(exception))
|
203
|
+
.to eql("#{exception.class.name}: #{exception.message}")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe '#actual' do
|
208
|
+
it 'sets the result' do
|
209
|
+
example = described_class.new('"something"')
|
210
|
+
expect(example.actual.result).to eql('"something"')
|
211
|
+
expect(example.actual.output).to eql('')
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'handles STDIN' do
|
215
|
+
example = described_class.new('gets')
|
216
|
+
expect(example.actual.result).to eql('nil')
|
217
|
+
expect(example.actual.output).to eql('')
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'handles STDOUT' do
|
221
|
+
example = described_class.new('puts "something"')
|
222
|
+
expect(example.actual.result).to eql('nil')
|
223
|
+
expect(example.actual.output).to eql("something\n")
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'handles STDERR' do
|
227
|
+
example = described_class.new('warn "something"')
|
228
|
+
expect(example.actual.result).to eql('nil')
|
229
|
+
expect(example.actual.output).to eql("something\n")
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'handles exceptions' do
|
233
|
+
example = described_class.new('fail StandardError, "something"')
|
234
|
+
expect(example.actual.result).to eql('nil')
|
235
|
+
expect(example.actual.output).to eql("StandardError: something\n")
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'memoizes the result' do
|
239
|
+
example = described_class.new('')
|
240
|
+
actual = example.actual
|
241
|
+
expect(example.actual).to be(actual)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe '.pattern' do
|
246
|
+
it 'returns a Regexp' do
|
247
|
+
pattern = described_class.pattern('something')
|
248
|
+
expect(pattern).to be_a(Regexp)
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'escapes meta characters' do
|
252
|
+
pattern = described_class.pattern('some.thing')
|
253
|
+
expect(pattern).to eql(/some\.thing/)
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'replaces "..."' do
|
257
|
+
pattern = described_class.pattern('some...thing')
|
258
|
+
expect(pattern).to eql(/some.*?thing/)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe '#valid_result?' do
|
263
|
+
it 'returns true without an expected result' do
|
264
|
+
example = described_class.new(nil, nil)
|
265
|
+
expect(example).to be_valid_result
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'returns true when the results match' do
|
269
|
+
example = described_class.new('"something"', '"something"')
|
270
|
+
expect(example).to be_valid_result
|
271
|
+
end
|
272
|
+
|
273
|
+
it "returns false when the results don't match" do
|
274
|
+
example = described_class.new('"something"', '"something else"')
|
275
|
+
expect(example).to_not be_valid_result
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'allows wildcards' do
|
279
|
+
example = described_class.new('Object.new', '#<Object:0x...>')
|
280
|
+
expect(example).to be_valid_result
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe '#valid_output?' do
|
285
|
+
it 'returns true without expected output' do
|
286
|
+
example = described_class.new(nil, nil)
|
287
|
+
expect(example).to be_valid_output
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'returns true when the output matches' do
|
291
|
+
example = described_class.new('puts "something"', nil, 'something')
|
292
|
+
expect(example).to be_valid_output
|
293
|
+
end
|
294
|
+
|
295
|
+
it "returns false when the output doesn't match" do
|
296
|
+
example = described_class.new('puts "something"', nil, 'something else')
|
297
|
+
expect(example).to_not be_valid_output
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'allows wildcards' do
|
301
|
+
example = described_class.new('puts Object.new', nil, '#<Object:0x...>')
|
302
|
+
expect(example).to be_valid_output
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe '#pass?' do
|
307
|
+
it 'realizes the actual outcome' do
|
308
|
+
example = described_class.new('x = 1')
|
309
|
+
example.pass?
|
310
|
+
expect(example.binding.eval('x')).to eql(1)
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'returns true if both the result and output match' do
|
314
|
+
example = described_class
|
315
|
+
.new('p "something"', '"something"', '"something"')
|
316
|
+
expect(example).to be_pass
|
317
|
+
end
|
318
|
+
|
319
|
+
it "returns false if the result doesn't match" do
|
320
|
+
example = described_class
|
321
|
+
.new('p "something"', '"something else"', '"something"')
|
322
|
+
expect(example).to_not be_pass
|
323
|
+
end
|
324
|
+
|
325
|
+
it "returns false if the output doesn't match" do
|
326
|
+
example = described_class
|
327
|
+
.new('p "something"', '"something"', '"something else"')
|
328
|
+
expect(example).to_not be_pass
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
describe '#==' do
|
333
|
+
it 'returns true when they have the same source, result, and output' do
|
334
|
+
source = 'x'
|
335
|
+
result = 'y'
|
336
|
+
output = 'z'
|
337
|
+
a = described_class.new(source, result, output)
|
338
|
+
b = described_class.new(source, result, output)
|
339
|
+
expect(a).to eq(b)
|
340
|
+
end
|
341
|
+
|
342
|
+
it "returns false when they don't have the same source" do
|
343
|
+
result = 'y'
|
344
|
+
output = 'z'
|
345
|
+
a = described_class.new('a', result, output)
|
346
|
+
b = described_class.new('b', result, output)
|
347
|
+
expect(a).to_not eq(b)
|
348
|
+
end
|
349
|
+
|
350
|
+
it "returns false when they don't have the same result" do
|
351
|
+
source = 'x'
|
352
|
+
output = 'z'
|
353
|
+
a = described_class.new(source, 'a', output)
|
354
|
+
b = described_class.new(source, 'b', output)
|
355
|
+
expect(a).to_not eq(b)
|
356
|
+
end
|
357
|
+
|
358
|
+
it "returns false when they don't have the same output" do
|
359
|
+
source = 'x'
|
360
|
+
result = 'y'
|
361
|
+
a = described_class.new(source, result, 'a')
|
362
|
+
b = described_class.new(source, result, 'b')
|
363
|
+
expect(a).to_not eq(b)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|