uspec 1.1.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +7 -0
- data/README.markdown +82 -46
- data/lib/uspec/cli.rb +30 -6
- data/lib/uspec/define.rb +12 -0
- data/lib/uspec/harness.rb +58 -0
- data/lib/uspec/result.rb +1 -1
- data/lib/uspec/spec.rb +34 -0
- data/lib/uspec/stats.rb +10 -7
- data/lib/uspec/version.rb +1 -1
- data/lib/uspec.rb +2 -1
- data/uspec/cli_spec.rb +46 -17
- data/uspec/dsl_spec.rb +93 -4
- data/uspec/stats_spec.rb +12 -0
- data/uspec/test_specs/kill_this_script.sh +26 -0
- data/uspec/test_specs/kill_this_spec +8 -0
- data/uspec/test_specs/pending_spec +0 -3
- data/uspec/uspec_helper.rb +41 -1
- data/uspec/uspec_spec.rb +0 -76
- data/uspec.gemspec +1 -4
- metadata +10 -34
- data/lib/uspec/dsl.rb +0 -86
- data/uspec/jump_spec.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d17e77cb5065265172f613c90f9edfb022c217386b56d34af6858b078ba406d8
|
4
|
+
data.tar.gz: 1c77be04e49ca61e243d7c35aa84c9de56dac3ae8f099ba351f6efa5d9a5ca38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf99ec58c05e076e9060b7ac71271e5fcdfd8ab44e03810b41a1ac2a67a4921777dca4ed2a5c27637f071b20fabb0262118b4a7214448b63ea8193450dc663b6
|
7
|
+
data.tar.gz: eb1c4de66aef1dfef542774fd18b9484bf26778bfe5651c0594fc6cd543472003c32484048435a41d9baacdf3695376fcb1e25fd659ff36183072ddb44eafb41
|
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -12,26 +12,20 @@ Philosophy / Why Uspec?
|
|
12
12
|
|
13
13
|
> Uspec is just Ruby!
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
- There's no need for special matchers
|
16
|
+
- You never have to worry that your tests lack assertions
|
17
|
+
- There is no monkey patching of core classes
|
18
18
|
|
19
|
-
|
20
|
-
to determine if the test has passed or failed. Standard Ruby comparisons are your friend!
|
21
|
-
No more digging around in your test framework's documentation to figure out what matcher you're supposed to use.
|
22
|
-
This also means *no monkey patching* core classes!
|
19
|
+
No more digging around in your test framework's documentation to figure out what matcher you're supposed to use. Because you just use Ruby!
|
23
20
|
|
24
|
-
Uspec
|
25
|
-
with red for failures, green for successes, and yellow for pending specs. Here's a screenshot:
|
26
|
-
|
27
|
-
![Screenshot!](https://i.imgur.com/Baqggck.png)
|
21
|
+
Uspec is well under 500 lines of code. Most of that is there to gracefully handle the weird edge cases that pop up all the time during development and testing of software. Uspec will catch issues at every stage and display a nicely formatted message to provide hints at what might have gone wrong.
|
28
22
|
|
29
23
|
Uspec is tiny, painless, and easy to use. Download it and give it a try!
|
30
24
|
|
31
|
-
|
32
|
-
|
25
|
+
Writing Tests
|
26
|
+
----------
|
33
27
|
|
34
|
-
Uspec is
|
28
|
+
Uspec is deceptively simple. You only need to remember one method: `spec`.
|
35
29
|
|
36
30
|
Writing a spec is easy:
|
37
31
|
|
@@ -43,31 +37,18 @@ end
|
|
43
37
|
|
44
38
|
That's it!
|
45
39
|
|
46
|
-
Installation
|
47
|
-
------------
|
48
|
-
|
49
|
-
Add this line to your application's Gemfile:
|
50
|
-
|
51
|
-
gem 'uspec'
|
52
|
-
|
53
|
-
And then execute:
|
54
|
-
|
55
|
-
$ bundle
|
56
|
-
|
57
|
-
Or install it directly with:
|
58
|
-
|
59
|
-
$ gem install uspec
|
60
|
-
|
61
|
-
|
62
40
|
Quickstart
|
63
41
|
----------
|
64
42
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
2.
|
43
|
+
1. Install in the typical way using Rubygems or Bundler:
|
44
|
+
- `gem install uspec`
|
45
|
+
- `echo 'gem "uspec"' >> Gemfile && bundle install`
|
46
|
+
2. Create a `uspec` directory to keep your specs in
|
47
|
+
3. Name your specs ending with `_spec.rb`
|
48
|
+
4. Write some specs in Ruby using the `spec` method (example above)
|
49
|
+
5. Use the included `uspec` executable to run your specs
|
69
50
|
|
70
|
-
|
51
|
+
And always remember that Uspec is **just Ruby**!
|
71
52
|
|
72
53
|
Commandline Usage
|
73
54
|
-----------------
|
@@ -78,19 +59,23 @@ uspec - minimalistic ruby testing framework
|
|
78
59
|
usage: uspec [<file_or_path>...]
|
79
60
|
```
|
80
61
|
|
81
|
-
- Without arguments the `uspec` command will
|
62
|
+
- Without arguments the `uspec` command will automatically look for a `uspec` directory and load any `*_spec.rb` files inside it.
|
82
63
|
- You can also pass in arbitrary files and it will attempt to run them as specs.
|
83
64
|
- If you pass in directories `uspec` will scan for and run any specs inside them.
|
84
|
-
- Uspec will return the number of failures as its status code to the
|
65
|
+
- Uspec will return the number of failures as its exit status code to the or `0` if none.
|
85
66
|
|
86
67
|
Output
|
87
68
|
------
|
88
69
|
|
70
|
+
Uspec's output is in beautiful ANSI technicolor, with red for failures, green for successes, and yellow for pending specs.
|
71
|
+
|
72
|
+
![Screenshot!](https://i.imgur.com/Baqggck.png)
|
73
|
+
|
89
74
|
A brief explanation of `uspec`'s output to show you what it can do!
|
90
75
|
|
91
76
|
### Success
|
92
77
|
|
93
|
-
If a spec passes (returns true):
|
78
|
+
If a spec passes (returns `true`):
|
94
79
|
|
95
80
|
```
|
96
81
|
-- AwesomeMcCoolname.generate creates a cool name: true
|
@@ -98,7 +83,7 @@ If a spec passes (returns true):
|
|
98
83
|
|
99
84
|
### Failure
|
100
85
|
|
101
|
-
If a spec fails (returns false):
|
86
|
+
If a spec fails (returns `false`):
|
102
87
|
|
103
88
|
```
|
104
89
|
-- AwesomeMcCoolname.generate creates a cool name: false
|
@@ -106,7 +91,7 @@ If a spec fails (returns false):
|
|
106
91
|
|
107
92
|
### Exception
|
108
93
|
|
109
|
-
If the spec encounters an error (raises an Exception):
|
94
|
+
If the spec encounters an error (raises an `Exception`):
|
110
95
|
|
111
96
|
```
|
112
97
|
-- AwesomeMcCoolname.generate creates a cool name: Exception
|
@@ -132,7 +117,7 @@ end
|
|
132
117
|
|
133
118
|
Then Uspec will let you know so you can debug it:
|
134
119
|
|
135
|
-
```
|
120
|
+
```
|
136
121
|
-- AwesomeMcCoolname.generate creates a badass name: Failed
|
137
122
|
|
138
123
|
Spec did not return a boolean value
|
@@ -155,8 +140,59 @@ When you run the test Uspec will helpfully display:
|
|
155
140
|
-- a feature I have not implemented yet: pending
|
156
141
|
```
|
157
142
|
|
158
|
-
|
159
|
-
|
143
|
+
Reusing Test Code
|
144
|
+
------------
|
145
|
+
|
146
|
+
Test code reuse doesn't require doing anything special. It's just like any other Ruby code. But here are a few examples!
|
147
|
+
|
148
|
+
**Hint:** A lot of people put `require_relative 'spec_helper'` at the top of their test files and put shared code and helper methods in a file called `spec_helper.rb`.
|
149
|
+
|
150
|
+
### Methods
|
151
|
+
|
152
|
+
If you find yourself repeating the same code in tests several times you can extract that code into a method and then call it within your `spec` blocks.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
def new_generator
|
156
|
+
AwesomeMcCoolname.new max_length: 15
|
157
|
+
end
|
158
|
+
|
159
|
+
spec 'generates a cool name' do
|
160
|
+
new_generator.generate.include? 'Badass'
|
161
|
+
end
|
162
|
+
```
|
163
|
+
|
164
|
+
### Instance Variables
|
165
|
+
|
166
|
+
This also works for instance variables!
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
@favorite_color = 'fuschia'
|
170
|
+
|
171
|
+
spec 'remembers favorite color' do
|
172
|
+
ColorDB.fetch(:favorite) == @favorite_color
|
173
|
+
end
|
174
|
+
```
|
175
|
+
|
176
|
+
### Memoized Methods
|
177
|
+
|
178
|
+
By combining the previous two capabilities of Ruby, it is trivial to memoize method output as well:
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
def reusable_generator
|
182
|
+
@reusable_generator ||= AwesomeMcCoolname.new max_length: 15
|
183
|
+
end
|
184
|
+
|
185
|
+
spec 'generates a cool name' do
|
186
|
+
reusable_generator.generate.include? 'Badass'
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
190
|
+
This is all the same kind of code that you use when writing any other Ruby object. You can put methods into objects and use those if you like too!
|
191
|
+
|
192
|
+
Test Matching in Plain Ruby
|
193
|
+
-----------
|
194
|
+
|
195
|
+
When the `spec` block is evaluated the `return` value is used (in a very Ruby-like way) to determine if the test has passed or failed. Standard Ruby comparisons are your friend!
|
160
196
|
|
161
197
|
Because there's no matchers and only one method there's no need for specialized reference documentation, but here are some ideas to get you going!
|
162
198
|
|
@@ -198,8 +234,8 @@ end
|
|
198
234
|
```
|
199
235
|
|
200
236
|
If there's no error, then Uspec will see the result of the method call (whatever it might be).
|
201
|
-
|
202
|
-
Ruby will dutifully pass along the error for Uspec to display.
|
237
|
+
|
238
|
+
If the wrong Exception is raised, then because of reraising (by just calling `raise` without parameters), Ruby will dutifully pass along the error for Uspec to display.
|
203
239
|
|
204
240
|
Mocks, Spies, Stubs, and More!
|
205
241
|
-----------------------
|
@@ -218,4 +254,4 @@ Contributing
|
|
218
254
|
Author
|
219
255
|
------
|
220
256
|
|
221
|
-
> Anthony M. Cook 2013-
|
257
|
+
> Anthony M. Cook 2013-2024
|
data/lib/uspec/cli.rb
CHANGED
@@ -8,9 +8,9 @@ class Uspec::CLI
|
|
8
8
|
@paths = args
|
9
9
|
@pwd = Pathname.pwd.freeze
|
10
10
|
@stats = Uspec::Stats.new
|
11
|
-
@
|
11
|
+
@harness = Uspec::Harness.new self
|
12
12
|
end
|
13
|
-
attr :stats, :
|
13
|
+
attr :stats, :harness
|
14
14
|
|
15
15
|
def usage
|
16
16
|
warn "uspec v#{::Uspec::VERSION} - minimalistic ruby testing framework"
|
@@ -24,14 +24,32 @@ class Uspec::CLI
|
|
24
24
|
|
25
25
|
def invoke
|
26
26
|
run_specs
|
27
|
-
|
28
|
-
exit exit_code
|
27
|
+
die!
|
29
28
|
end
|
30
29
|
|
31
30
|
def exit_code
|
32
31
|
[@stats.failure.size, 255].min
|
33
32
|
end
|
34
33
|
|
34
|
+
def handle_interrupt! type = Interrupt
|
35
|
+
if SignalException === type || SystemExit === type then
|
36
|
+
if type === Module then
|
37
|
+
err = type
|
38
|
+
msg = "signal"
|
39
|
+
else
|
40
|
+
err = type.class
|
41
|
+
msg = type.message
|
42
|
+
end
|
43
|
+
puts "Uspec received #{err} #{msg} - exiting!"
|
44
|
+
die!
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def die!
|
49
|
+
puts @stats.summary
|
50
|
+
exit exit_code
|
51
|
+
end
|
52
|
+
|
35
53
|
def paths
|
36
54
|
if @paths.empty? then
|
37
55
|
['spec', 'uspec', 'test'].each do |path|
|
@@ -56,12 +74,16 @@ class Uspec::CLI
|
|
56
74
|
end
|
57
75
|
elsif path.exist? then
|
58
76
|
puts "#{path.basename path.extname}:"
|
59
|
-
|
77
|
+
harness.define.instance_eval(path.read, path.to_s)
|
60
78
|
else
|
61
79
|
warn "path not found: #{path}"
|
62
80
|
end
|
63
81
|
rescue Exception => error
|
64
82
|
|
83
|
+
if SignalException === error || SystemExit === error then
|
84
|
+
exit 3
|
85
|
+
end
|
86
|
+
|
65
87
|
error_file, error_line, _ = error.backtrace.first.split ?:
|
66
88
|
|
67
89
|
message = <<-MSG
|
@@ -79,7 +101,9 @@ class Uspec::CLI
|
|
79
101
|
MSG
|
80
102
|
puts
|
81
103
|
warn message
|
82
|
-
stats
|
104
|
+
stats << Uspec::Result.new(message, error, caller)
|
105
|
+
|
106
|
+
handle_interrupt! error
|
83
107
|
end
|
84
108
|
|
85
109
|
end
|
data/lib/uspec/define.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative "result"
|
2
|
+
require_relative "spec"
|
3
|
+
|
4
|
+
module Uspec
|
5
|
+
class Harness
|
6
|
+
|
7
|
+
def initialize cli
|
8
|
+
@cli = cli
|
9
|
+
@define = Uspec::Define.new self
|
10
|
+
end
|
11
|
+
attr_accessor :cli, :define
|
12
|
+
|
13
|
+
def stats
|
14
|
+
cli.stats
|
15
|
+
end
|
16
|
+
|
17
|
+
def spec_eval description, &block
|
18
|
+
state = 0
|
19
|
+
print ' -- ', description
|
20
|
+
|
21
|
+
if block then
|
22
|
+
begin
|
23
|
+
state = 1
|
24
|
+
raw_result = Uspec::Spec.new(self, description, &block).__uspec_block
|
25
|
+
state = 2
|
26
|
+
rescue Exception => raw_result
|
27
|
+
state = 3
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
result = Uspec::Result.new description, raw_result, caller
|
32
|
+
|
33
|
+
unless block then
|
34
|
+
state = 4
|
35
|
+
result.pending!
|
36
|
+
end
|
37
|
+
|
38
|
+
stats << result
|
39
|
+
|
40
|
+
print ': ', result.pretty, "\n"
|
41
|
+
rescue => error
|
42
|
+
state = 5
|
43
|
+
message = <<-MSG
|
44
|
+
#{error.class} : #{error.message}
|
45
|
+
|
46
|
+
Uspec encountered an internal error, please report this bug: https://github.com/acook/uspec/issues/new
|
47
|
+
|
48
|
+
\t#{error.backtrace.join "\n\t"}
|
49
|
+
MSG
|
50
|
+
puts
|
51
|
+
warn message
|
52
|
+
stats << Uspec::Result.new(message, error, caller)
|
53
|
+
ensure
|
54
|
+
cli.handle_interrupt! result.raw
|
55
|
+
return [state, error, result, raw_result]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/uspec/result.rb
CHANGED
@@ -5,7 +5,7 @@ module Uspec
|
|
5
5
|
class Result
|
6
6
|
include Terminal
|
7
7
|
|
8
|
-
PREFIX = "#{Terminal.newline}#{Terminal.yellow}>\t#{Terminal.normal}"
|
8
|
+
PREFIX = "#{Uspec::Terminal.newline}#{Uspec::Terminal.yellow}>\t#{Uspec::Terminal.normal}"
|
9
9
|
|
10
10
|
def initialize spec, raw, source
|
11
11
|
@spec = spec
|
data/lib/uspec/spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative "result"
|
2
|
+
|
3
|
+
module Uspec
|
4
|
+
class Spec
|
5
|
+
|
6
|
+
def initialize harness, description, &block
|
7
|
+
@__uspec_description = description
|
8
|
+
@__uspec_harness = harness
|
9
|
+
ns = harness.define
|
10
|
+
|
11
|
+
ns.instance_variables.each do |name|
|
12
|
+
self.instance_variable_set(
|
13
|
+
name,
|
14
|
+
ns.instance_variable_get(name)
|
15
|
+
) unless name.to_s.include? '@__uspec'
|
16
|
+
end
|
17
|
+
|
18
|
+
ns.methods(false).each do |name|
|
19
|
+
self.define_singleton_method name do |*args, &block|
|
20
|
+
ns.send name, *args, &block
|
21
|
+
end unless name.to_s.include? '__uspec'
|
22
|
+
end
|
23
|
+
|
24
|
+
if block then
|
25
|
+
self.define_singleton_method :__uspec_block, &block
|
26
|
+
else
|
27
|
+
self.define_singleton_method :__uspec_block do
|
28
|
+
raise NotImplementedError, "Uspec: No block provided for `#{@__uspec_description}`"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end # initialize
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/lib/uspec/stats.rb
CHANGED
@@ -5,19 +5,22 @@ module Uspec
|
|
5
5
|
end
|
6
6
|
attr :success, :failure, :pending
|
7
7
|
|
8
|
+
def << result
|
9
|
+
if result.success?
|
10
|
+
self.success << result
|
11
|
+
elsif result.pending?
|
12
|
+
self.pending << result
|
13
|
+
else
|
14
|
+
self.failure << result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
8
18
|
def clear_results!
|
9
19
|
@success = Array.new
|
10
20
|
@failure = Array.new
|
11
21
|
@pending = Array.new
|
12
22
|
end
|
13
23
|
|
14
|
-
def inspect
|
15
|
-
<<-INFO
|
16
|
-
#{super} Failures: #{exit_code}
|
17
|
-
#{results.map{|r| r.inspect}.join "\n\t" }
|
18
|
-
INFO
|
19
|
-
end
|
20
|
-
|
21
24
|
def results
|
22
25
|
@success + @failure + @pending
|
23
26
|
end
|
data/lib/uspec/version.rb
CHANGED
data/lib/uspec.rb
CHANGED
data/uspec/cli_spec.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
require_relative 'uspec_helper'
|
2
2
|
|
3
|
+
def new_cli path = '.'
|
4
|
+
Uspec::CLI.new(Array(path))
|
5
|
+
end
|
6
|
+
|
7
|
+
def run_specs path
|
8
|
+
new_cli(path).run_specs
|
9
|
+
end
|
10
|
+
|
11
|
+
|
3
12
|
spec 'shows usage' do
|
4
13
|
output = capture do
|
5
14
|
exec 'bin/uspec -h'
|
@@ -8,42 +17,62 @@ spec 'shows usage' do
|
|
8
17
|
output.include? 'usage'
|
9
18
|
end
|
10
19
|
|
11
|
-
spec 'pending test doesn\'t crash'
|
12
|
-
|
13
20
|
spec 'runs a path of specs' do
|
14
|
-
output =
|
15
|
-
|
16
|
-
Uspec::CLI.new(Array(path)).run_specs
|
21
|
+
output = outstr do
|
22
|
+
run_specs exdir.to_s
|
17
23
|
end
|
18
24
|
|
19
25
|
output.include?('I love passing tests') || output
|
20
26
|
end
|
21
27
|
|
22
28
|
spec 'runs an individual spec' do
|
23
|
-
output =
|
24
|
-
|
25
|
-
Uspec::CLI.new(Array(path)).run_specs
|
29
|
+
output = outstr do
|
30
|
+
run_specs exdir.join('example_spec.rb').to_s
|
26
31
|
end
|
27
32
|
|
28
33
|
output.include?('I love passing tests') || output
|
29
34
|
end
|
30
35
|
|
31
36
|
spec 'broken requires in test files count as test failures' do
|
32
|
-
|
37
|
+
output, status = Open3.capture2e "#{root}/bin/uspec #{testdir.join('broken_require_spec')}"
|
33
38
|
|
34
|
-
|
35
|
-
|
39
|
+
status.exitstatus == 1 || status
|
40
|
+
end
|
41
|
+
|
42
|
+
spec 'displays information about test file with broken require' do
|
43
|
+
output, status = Open3.capture2e "#{root}/bin/uspec #{testdir.join('broken_require_spec')}"
|
44
|
+
|
45
|
+
output.include?('cannot load such file') || output
|
46
|
+
end
|
47
|
+
|
48
|
+
spec 'exit code is the number of failures' do
|
49
|
+
expected = 50
|
50
|
+
cli = new_cli
|
51
|
+
|
52
|
+
outstr do
|
53
|
+
expected.times do |count|
|
54
|
+
cli.harness.define.spec "fail ##{count + 1}" do
|
55
|
+
false
|
56
|
+
end
|
57
|
+
end
|
36
58
|
end
|
37
59
|
|
38
|
-
|
60
|
+
actual = cli.exit_code
|
61
|
+
actual == expected || output
|
39
62
|
end
|
40
63
|
|
41
|
-
spec '
|
42
|
-
|
64
|
+
spec 'when more than 255 failures, exit status is 255' do
|
65
|
+
expected = 255
|
66
|
+
cli = new_cli
|
43
67
|
|
44
|
-
output =
|
45
|
-
|
68
|
+
output = outstr do
|
69
|
+
500.times do
|
70
|
+
cli.harness.define.spec 'fail' do
|
71
|
+
false
|
72
|
+
end
|
73
|
+
end
|
46
74
|
end
|
47
75
|
|
48
|
-
|
76
|
+
actual = cli.exit_code
|
77
|
+
actual == expected || [$?, output]
|
49
78
|
end
|
data/uspec/dsl_spec.rb
CHANGED
@@ -1,20 +1,109 @@
|
|
1
1
|
require_relative "uspec_helper"
|
2
2
|
|
3
|
+
spec 'catches errors' do
|
4
|
+
cli = new_cli
|
5
|
+
|
6
|
+
output = outstr do
|
7
|
+
cli.harness.define.spec 'exception' do
|
8
|
+
raise 'test exception'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
output.include?('Exception') || output
|
13
|
+
end
|
14
|
+
|
15
|
+
spec 'catches even non-StandardError-subclass exceptions' do
|
16
|
+
cli = new_cli
|
17
|
+
|
18
|
+
output = outstr do
|
19
|
+
cli.harness.define.spec 'not implemented error' do
|
20
|
+
raise ::NotImplementedError, 'test exception'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
output.include?('Exception') || output
|
25
|
+
end
|
26
|
+
|
27
|
+
spec 'Uspec exits when sent a termination signal' do
|
28
|
+
path = testdir.join('kill_this_spec')
|
29
|
+
|
30
|
+
stdin, allout, thread = Open3.popen2e "#{testdir}/kill_this_script.sh \"#{path}\""
|
31
|
+
stdin.close
|
32
|
+
output = allout.read
|
33
|
+
|
34
|
+
begin
|
35
|
+
Process.waitpid(thread.pid)
|
36
|
+
rescue Errno::ECHILD
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
summary_match = output.match(/0.*successful.*,.*1.*failed.*,.*0.*pending/)
|
41
|
+
no_copy_match = output.match(/2.{0,5}pending/) # previous versions continued after "exiting"
|
42
|
+
|
43
|
+
(!!summary_match && !no_copy_match) || output
|
44
|
+
end
|
45
|
+
|
46
|
+
spec 'complains when spec block returns non boolean' do
|
47
|
+
cli = new_cli
|
48
|
+
|
49
|
+
output = outstr do
|
50
|
+
cli.harness.define.spec 'whatever' do
|
51
|
+
"string"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
output.include?('Failed') || output
|
56
|
+
end
|
57
|
+
|
58
|
+
spec 'marks test as pending when no block supplied' do
|
59
|
+
path = testdir.join('pending_spec')
|
60
|
+
|
61
|
+
output = capture do
|
62
|
+
exec "#{root}/bin/uspec #{path}"
|
63
|
+
end
|
64
|
+
|
65
|
+
output.include?('1 pending') || output
|
66
|
+
end
|
67
|
+
|
68
|
+
spec 'should not define DSL methods on arbitrary objects' do
|
69
|
+
!(Array.respond_to? :spec)
|
70
|
+
end
|
71
|
+
|
72
|
+
spec 'when return used in spec, capture it as an error' do
|
73
|
+
path = testdir.join('return_spec')
|
74
|
+
|
75
|
+
output = capture do
|
76
|
+
exec "#{root}/bin/uspec #{path}"
|
77
|
+
end
|
78
|
+
|
79
|
+
output.include?('Invalid return') || output.include?('Spec did not return a boolean value') || output
|
80
|
+
end
|
81
|
+
|
82
|
+
spec 'when break used in spec, capture it as an error' do
|
83
|
+
path = testdir.join('break_spec')
|
84
|
+
|
85
|
+
output = capture do
|
86
|
+
exec "#{root}/bin/uspec #{path}"
|
87
|
+
end
|
88
|
+
|
89
|
+
output.include?('Invalid break') || output.include?('Spec did not return a boolean value') || output
|
90
|
+
end
|
91
|
+
|
3
92
|
spec 'when instance variables are defined in the DSL instance, they are available in the spec body' do
|
4
|
-
path =
|
93
|
+
path = testdir.join('ivar_spec')
|
5
94
|
|
6
95
|
output = capture do
|
7
|
-
exec "bin/uspec #{path}"
|
96
|
+
exec "#{root}/bin/uspec #{path}"
|
8
97
|
end
|
9
98
|
|
10
99
|
output.include?('1 successful') || output
|
11
100
|
end
|
12
101
|
|
13
102
|
spec 'when methods are defined in the DSL instance, they are available in the spec body' do
|
14
|
-
path =
|
103
|
+
path = testdir.join('method_spec')
|
15
104
|
|
16
105
|
output = capture do
|
17
|
-
exec "bin/uspec #{path}"
|
106
|
+
exec "#{root}/bin/uspec #{path}"
|
18
107
|
end
|
19
108
|
|
20
109
|
output.include?('1 successful') || output
|
data/uspec/stats_spec.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative 'uspec_helper'
|
2
|
+
|
3
|
+
spec 'stats can be inspected' do
|
4
|
+
actual = @__uspec_harness.stats.inspect
|
5
|
+
actual.include?("failure") || actual
|
6
|
+
end
|
7
|
+
|
8
|
+
spec 'stats inspect does not have any stray whitespace' do
|
9
|
+
output = @__uspec_harness.stats.inspect
|
10
|
+
match = output.match /(.*(?: |\n))/m
|
11
|
+
match == nil || match
|
12
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env sh
|
2
|
+
|
3
|
+
command_exists() { command -v "$1" 1>&- 2>&-; }
|
4
|
+
|
5
|
+
THISDIR="$(realpath "$(dirname "$0")")"
|
6
|
+
ROOTDIR="$(realpath "$THISDIR/../..")"
|
7
|
+
|
8
|
+
path="$1"
|
9
|
+
|
10
|
+
if ! [ -f "$path" ]; then
|
11
|
+
echo "file not found: $path"
|
12
|
+
exit 255
|
13
|
+
fi
|
14
|
+
|
15
|
+
if command_exists bundle; then
|
16
|
+
echo running with bundler
|
17
|
+
bundle exec "$ROOTDIR/bin/uspec" "$path" 2>&1 &
|
18
|
+
pid="$!"
|
19
|
+
else
|
20
|
+
echo running directly
|
21
|
+
"$ROOTDIR/bin/uspec" "$path" 2>&1 &
|
22
|
+
pid="$!"
|
23
|
+
fi
|
24
|
+
|
25
|
+
sleep 1
|
26
|
+
kill "$pid"
|
data/uspec/uspec_helper.rb
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'pry'
|
3
|
+
rescue LoadError => err
|
4
|
+
nil
|
5
|
+
end
|
6
|
+
require 'open3'
|
7
|
+
require 'stringio'
|
2
8
|
|
3
9
|
require_relative '../lib/uspec'
|
4
10
|
extend Uspec
|
@@ -19,3 +25,37 @@ def capture
|
|
19
25
|
|
20
26
|
output
|
21
27
|
end
|
28
|
+
|
29
|
+
def outstr
|
30
|
+
old_stdout = $stdout
|
31
|
+
old_stderr = $stderr
|
32
|
+
|
33
|
+
outio = StringIO.new
|
34
|
+
$stdout = outio
|
35
|
+
|
36
|
+
errio = StringIO.new
|
37
|
+
$stderr = errio
|
38
|
+
|
39
|
+
val = yield
|
40
|
+
|
41
|
+
outio.string + errio.string
|
42
|
+
ensure
|
43
|
+
$stdout = old_stdout
|
44
|
+
$stderr = old_stderr
|
45
|
+
end
|
46
|
+
|
47
|
+
def root
|
48
|
+
Pathname.new(__FILE__).parent.parent
|
49
|
+
end
|
50
|
+
|
51
|
+
def exdir
|
52
|
+
root.join('example_specs')
|
53
|
+
end
|
54
|
+
|
55
|
+
def specdir
|
56
|
+
root.join('uspec')
|
57
|
+
end
|
58
|
+
|
59
|
+
def testdir
|
60
|
+
specdir.join('test_specs')
|
61
|
+
end
|
data/uspec/uspec_spec.rb
CHANGED
@@ -1,77 +1 @@
|
|
1
1
|
require_relative 'uspec_helper'
|
2
|
-
|
3
|
-
spec 'catches errors' do
|
4
|
-
output = capture do
|
5
|
-
spec 'exception' do
|
6
|
-
raise 'test exception'
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
output.include?('Exception') || output
|
11
|
-
end
|
12
|
-
|
13
|
-
spec 'catches even non-StandardError-subclass exceptions' do
|
14
|
-
output = capture do
|
15
|
-
spec 'not implemented error' do
|
16
|
-
raise ::NotImplementedError, 'test exception'
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
output.include?('Exception') || output
|
21
|
-
end
|
22
|
-
|
23
|
-
spec 'complains when spec block returns non boolean' do
|
24
|
-
output = capture do
|
25
|
-
spec 'whatever' do
|
26
|
-
"string"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
output.include?('Failed') || output
|
31
|
-
end
|
32
|
-
|
33
|
-
spec 'marks test as pending when no block supplied' do
|
34
|
-
output = capture do
|
35
|
-
spec 'pending test'
|
36
|
-
end
|
37
|
-
|
38
|
-
output.include?('pending') || output
|
39
|
-
end
|
40
|
-
|
41
|
-
spec 'should not define DSL methods on arbitrary objects' do
|
42
|
-
!(Array.respond_to? :spec)
|
43
|
-
end
|
44
|
-
|
45
|
-
spec 'exit code is the number of failures' do
|
46
|
-
expected = 50
|
47
|
-
output = capture do
|
48
|
-
__uspec_stats.clear_results! # because we're forking, we will have a copy of the current results
|
49
|
-
|
50
|
-
expected.times do |count|
|
51
|
-
spec "fail ##{count + 1}" do
|
52
|
-
false
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
exit __uspec_cli.exit_code
|
57
|
-
end
|
58
|
-
actual = $?.exitstatus
|
59
|
-
|
60
|
-
actual == expected || output
|
61
|
-
end
|
62
|
-
|
63
|
-
spec 'when more than 255 failures, exit status is 255' do
|
64
|
-
output = capture do
|
65
|
-
__uspec_stats.clear_results! # because we're forking, we will have a copy of the current results
|
66
|
-
|
67
|
-
500.times do
|
68
|
-
spec 'fail' do
|
69
|
-
false
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
exit __uspec_cli.exit_code
|
74
|
-
end
|
75
|
-
|
76
|
-
$?.exitstatus == 255 || [$?, output]
|
77
|
-
end
|
data/uspec.gemspec
CHANGED
@@ -22,8 +22,5 @@ Gem::Specification.new do |gem|
|
|
22
22
|
# technically should still work in 2.0 but some of the test suite won't pass
|
23
23
|
gem.required_ruby_version = ">= 2.1"
|
24
24
|
|
25
|
-
gem.add_dependency "that_object_is_so_basic", "
|
26
|
-
|
27
|
-
gem.add_development_dependency "pry"
|
28
|
-
gem.add_development_dependency "pry-doc"
|
25
|
+
gem.add_dependency "that_object_is_so_basic", ">= 0.0.5"
|
29
26
|
end
|
metadata
CHANGED
@@ -1,57 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anthony M. Cook
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: that_object_is_so_basic
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 0.0.5
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 0.0.5
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: pry
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
22
|
version_requirements: !ruby/object:Gem::Requirement
|
37
23
|
requirements:
|
38
24
|
- - ">="
|
39
25
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: pry-doc
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
26
|
+
version: 0.0.5
|
55
27
|
description: Uspec is a shiny little spec framework for your apps! Unlike other testing
|
56
28
|
frameworks there's no need for matchers, there can only be one assertion per test,
|
57
29
|
and you never have to worry that your tests lack assertions.
|
@@ -77,19 +49,23 @@ files:
|
|
77
49
|
- example_specs/spec_helper.rb
|
78
50
|
- lib/uspec.rb
|
79
51
|
- lib/uspec/cli.rb
|
80
|
-
- lib/uspec/
|
52
|
+
- lib/uspec/define.rb
|
53
|
+
- lib/uspec/harness.rb
|
81
54
|
- lib/uspec/result.rb
|
55
|
+
- lib/uspec/spec.rb
|
82
56
|
- lib/uspec/stats.rb
|
83
57
|
- lib/uspec/terminal.rb
|
84
58
|
- lib/uspec/version.rb
|
85
59
|
- uspec.gemspec
|
86
60
|
- uspec/cli_spec.rb
|
87
61
|
- uspec/dsl_spec.rb
|
88
|
-
- uspec/jump_spec.rb
|
89
62
|
- uspec/result_spec.rb
|
63
|
+
- uspec/stats_spec.rb
|
90
64
|
- uspec/test_specs/break_spec
|
91
65
|
- uspec/test_specs/broken_require_spec
|
92
66
|
- uspec/test_specs/ivar_spec
|
67
|
+
- uspec/test_specs/kill_this_script.sh
|
68
|
+
- uspec/test_specs/kill_this_spec
|
93
69
|
- uspec/test_specs/method_spec
|
94
70
|
- uspec/test_specs/pending_spec
|
95
71
|
- uspec/test_specs/return_spec
|
data/lib/uspec/dsl.rb
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
require_relative "result"
|
2
|
-
|
3
|
-
module Uspec
|
4
|
-
class DSL
|
5
|
-
USPEC_CLI_BLOCK = -> { @__uspec_dsl.__uspec_cli }
|
6
|
-
USPEC_STAT_BLOCK = -> { @__uspec_dsl.__uspec_cli.stats }
|
7
|
-
USPEC_SPEC_BLOCK = ->(description, &block) { @__uspec_dsl.spec description, &block }
|
8
|
-
|
9
|
-
def initialize cli
|
10
|
-
@__uspec_cli = cli
|
11
|
-
end
|
12
|
-
|
13
|
-
def __uspec_cli
|
14
|
-
@__uspec_cli
|
15
|
-
end
|
16
|
-
|
17
|
-
def __uspec_stats
|
18
|
-
@__uspec_cli.stats
|
19
|
-
end
|
20
|
-
|
21
|
-
def __uspec_eval block
|
22
|
-
o = Object.new
|
23
|
-
o.define_singleton_method :__uspec_stats, USPEC_STAT_BLOCK
|
24
|
-
o.define_singleton_method :__uspec_cli, USPEC_CLI_BLOCK
|
25
|
-
o.instance_variable_set :@__uspec_cli, @__uspec_cli
|
26
|
-
o.instance_variable_set :@__uspec_dsl, self
|
27
|
-
o.define_singleton_method :spec, USPEC_SPEC_BLOCK
|
28
|
-
o.define_singleton_method :spec_block, &block
|
29
|
-
self.instance_variables.each do |name|
|
30
|
-
o.instance_variable_set(name, self.instance_variable_get(name)) unless name.to_s.include? '@__uspec'
|
31
|
-
end
|
32
|
-
self.methods(false).each do |name|
|
33
|
-
o.define_singleton_method name do |*args, &block|
|
34
|
-
@__uspec_dsl.send name, *args, &block
|
35
|
-
end unless name.to_s.include? '__uspec'
|
36
|
-
end
|
37
|
-
o.spec_block
|
38
|
-
end
|
39
|
-
|
40
|
-
def spec description, &block
|
41
|
-
state = 0
|
42
|
-
print ' -- ', description
|
43
|
-
|
44
|
-
if block then
|
45
|
-
begin
|
46
|
-
state = 1
|
47
|
-
raw_result = __uspec_eval block
|
48
|
-
state = 2
|
49
|
-
rescue Exception => raw_result
|
50
|
-
state = 3
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
result = Result.new description, raw_result, caller
|
55
|
-
|
56
|
-
unless block then
|
57
|
-
state = 4
|
58
|
-
result.pending!
|
59
|
-
end
|
60
|
-
|
61
|
-
if result.success?
|
62
|
-
__uspec_stats.success << result
|
63
|
-
elsif result.pending?
|
64
|
-
__uspec_stats.pending << result
|
65
|
-
else
|
66
|
-
__uspec_stats.failure << result
|
67
|
-
end
|
68
|
-
|
69
|
-
print ': ', result.pretty, "\n"
|
70
|
-
rescue => error
|
71
|
-
state = 5
|
72
|
-
message = <<-MSG
|
73
|
-
#{error.class} : #{error.message}
|
74
|
-
|
75
|
-
Uspec encountered an internal error, please report this bug: https://github.com/acook/uspec/issues/new
|
76
|
-
|
77
|
-
\t#{error.backtrace.join "\n\t"}
|
78
|
-
MSG
|
79
|
-
puts
|
80
|
-
warn message
|
81
|
-
__uspec_stats.failure << Uspec::Result.new(message, error, caller)
|
82
|
-
ensure
|
83
|
-
return [state, error, result, raw_result]
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
data/uspec/jump_spec.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
require_relative "uspec_helper"
|
2
|
-
|
3
|
-
spec 'when return used in spec, capture it as an error' do
|
4
|
-
path = Pathname.new(__FILE__).parent.join('test_specs', 'return_spec')
|
5
|
-
|
6
|
-
output = capture do
|
7
|
-
exec "bin/uspec #{path}"
|
8
|
-
end
|
9
|
-
|
10
|
-
output.include?('Invalid return') || output.include?('Spec did not return a boolean value') || output
|
11
|
-
end
|
12
|
-
|
13
|
-
spec 'when break used in spec, capture it as an error' do
|
14
|
-
path = Pathname.new(__FILE__).parent.join('test_specs', 'break_spec')
|
15
|
-
|
16
|
-
output = capture do
|
17
|
-
exec "bin/uspec #{path}"
|
18
|
-
end
|
19
|
-
|
20
|
-
output.include?('Invalid break') || output.include?('Spec did not return a boolean value') || output
|
21
|
-
end
|