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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 659b5228eb82a76f457af1da9a275cfdcc5914f03d40083fdca08f79c8b1fac6
4
- data.tar.gz: d89c4ee71c95dc202adf14110a140f9f4d667698f0e8b443a884226b6881e57d
3
+ metadata.gz: d17e77cb5065265172f613c90f9edfb022c217386b56d34af6858b078ba406d8
4
+ data.tar.gz: 1c77be04e49ca61e243d7c35aa84c9de56dac3ae8f099ba351f6efa5d9a5ca38
5
5
  SHA512:
6
- metadata.gz: 021b318bd832f4c5f290db67bca30a05e8e545fcc9b633506ab63ce2dc55cf53389885bc42d956e007f76d44245817aaa915f35f940b514eca43cbde32cc0c2a
7
- data.tar.gz: 44f6373981caa474dbb6df5f48f9fcf38c9fc3d59f680e6198940f758c098f4157ce30dc6ad13294cb3bca99c7f659ca60395d5760b11830905144368ef5a8b0
6
+ metadata.gz: cf99ec58c05e076e9060b7ac71271e5fcdfd8ab44e03810b41a1ac2a67a4921777dca4ed2a5c27637f071b20fabb0262118b4a7214448b63ea8193450dc663b6
7
+ data.tar.gz: eb1c4de66aef1dfef542774fd18b9484bf26778bfe5651c0594fc6cd543472003c32484048435a41d9baacdf3695376fcb1e25fd659ff36183072ddb44eafb41
data/Gemfile CHANGED
@@ -2,3 +2,10 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in uspec.gemspec
4
4
  gemspec
5
+
6
+ unless ENV['CI'] then
7
+ gem 'pry'
8
+ gem 'pry-doc'
9
+ gem 'pry-theme'
10
+ gem 'pry-coolline'
11
+ end
data/README.markdown CHANGED
@@ -12,26 +12,20 @@ Philosophy / Why Uspec?
12
12
 
13
13
  > Uspec is just Ruby!
14
14
 
15
- Unlike other testing frameworks there's no need for special matchers,
16
- there can only be one assertion per test,
17
- and you never have to worry that your tests lack assertions.
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
- That's because when the `spec` block is evaluated the return value is used (in a very ruby-like way)
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's output is in beautiful ansi technicolor,
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
- Example
32
- -------
25
+ Writing Tests
26
+ ----------
33
27
 
34
- Uspec is **just Ruby**. The DSL is minimal - there's only one method to remember!
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
- 0. Create a `uspec` directory to keep your specs in.
66
- 1. Name your specs ending with `_spec.rb`.
67
- 2. Write some specs in Ruby using the `spec` method (example above).
68
- 2. Use the included `uspec` executable to run your specs.
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
- **Hint:** A lot of people also put `require_relative 'spec_helper'` at the top of their test files for sharing code between tests.
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 automatially look for a `uspec` directory and load any `*_spec.rb` files inside them.
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 commandline, 0 if none.
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
- ```ruby
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
- Tips & Tricks
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
- If the wrong Exception is raised, then because of reraising (by just calling `raise` without parameters),
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-2021
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
- @dsl = Uspec::DSL.new self
11
+ @harness = Uspec::Harness.new self
12
12
  end
13
- attr :stats, :dsl
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
- puts @stats.summary
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
- dsl.instance_eval(path.read, path.to_s)
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.failure << Uspec::Result.new(message, error, caller)
104
+ stats << Uspec::Result.new(message, error, caller)
105
+
106
+ handle_interrupt! error
83
107
  end
84
108
 
85
109
  end
@@ -0,0 +1,12 @@
1
+ module Uspec
2
+ class Define
3
+
4
+ def initialize harness
5
+ @__uspec_harness = harness
6
+ end
7
+
8
+ def spec description, &block
9
+ @__uspec_harness.spec_eval description, &block
10
+ end
11
+ end
12
+ end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Uspec
2
- VERSION = '1.1.2'
2
+ VERSION = '1.3.0'
3
3
  end
data/lib/uspec.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require_relative 'uspec/version'
2
- require_relative 'uspec/dsl'
2
+ require_relative 'uspec/harness'
3
+ require_relative 'uspec/define'
3
4
  require_relative 'uspec/stats'
4
5
 
5
6
  module Uspec
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 = capture do
15
- path = Pathname.new(__FILE__).parent.parent.join('example_specs').to_s
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 = capture do
24
- path = Pathname.new(__FILE__).parent.parent.join('example_specs', 'example_spec.rb').to_s
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
- path = Pathname.new(__FILE__).parent.join('test_specs', 'broken_require_spec')
37
+ output, status = Open3.capture2e "#{root}/bin/uspec #{testdir.join('broken_require_spec')}"
33
38
 
34
- output = capture do
35
- exec "bin/uspec #{path}"
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
- $?.exitstatus == 1 || $?
60
+ actual = cli.exit_code
61
+ actual == expected || output
39
62
  end
40
63
 
41
- spec 'displays information about test file with broken require' do
42
- path = Pathname.new(__FILE__).parent.join('test_specs', 'broken_require_spec')
64
+ spec 'when more than 255 failures, exit status is 255' do
65
+ expected = 255
66
+ cli = new_cli
43
67
 
44
- output = capture do
45
- exec "bin/uspec #{path}"
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
- output.include?('cannot load such file') || output
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 = Pathname.new(__FILE__).parent.join('test_specs', 'ivar_spec')
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 = Pathname.new(__FILE__).parent.join('test_specs', 'method_spec')
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
@@ -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"
@@ -0,0 +1,8 @@
1
+ spec 'nap time, i sure hope no one kills me in my sleep' do
2
+ sleep 10
3
+ true
4
+ end
5
+
6
+ spec 'how did we get here?'
7
+
8
+ spec 'something probably went wrong'
@@ -1,6 +1,3 @@
1
-
2
1
  require_relative "../uspec_helper"
3
2
 
4
3
  spec "why is this broken??"
5
-
6
-
@@ -1,4 +1,10 @@
1
- require 'pry'
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", "~> 0.0.5"
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.1.2
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-20 00:00:00.000000000 Z
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: '0'
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/dsl.rb
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