uspec 1.2.0 → 1.4.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: b9c8a47af991d099f6804066c5ef9e2d8ac0c4cca8023a919354bc247ee3d901
4
- data.tar.gz: 14e4c49e40bf1a2cf19553b41c27107c7d13f110978dad55d951242a2e86fcc6
3
+ metadata.gz: 14e2539758227d31271ce1e3cf534b0edb491cb685f62ab7c16a7fbc711caf88
4
+ data.tar.gz: 638daabf4483e4ca6a34b480030f896078dd49bb4286afee24830f3395ed3563
5
5
  SHA512:
6
- metadata.gz: b1781f78237157872c0ad40f5c009a4dbed94b78049973feb164f49ea3c042dbcc022ec80d423ee5d69a0d0ca3f0d48dfe4e81a227848500a3af1466c15e015e
7
- data.tar.gz: 3ecaada229d7bc0ee4a0c3d473654c5544863c11dd390cde47e8e08d25205a5bfe8175de8b96a1a4f93aa9b499b119d702bc8f4723a1aa371b5b134e1af7d371
6
+ metadata.gz: 22747fc9d720163116d69d3f8fa8ba7ad15df46f37dcc3138b93b1eab3a07f1fe765a01b8a1d6ef7ec3cb094c60adbefa39813098841d50a23b62291ac4b9116
7
+ data.tar.gz: ca54684feb09cdd6b4fc4370b0dc492fd13dfb20ef32c5255329b7a7744ca1b799fce6afc7f8fa039f5658b473334ee06124b908d5a8f113f8a6c035efbcb7a2
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
@@ -1,10 +1,3 @@
1
1
  require 'bundler/setup'
2
2
 
3
3
  Bundler.require :development, :test
4
-
5
- require 'uspec'
6
-
7
- Dir.chdir File.dirname(__FILE__)
8
-
9
- extend Uspec
10
-
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"
@@ -74,36 +74,10 @@ class Uspec::CLI
74
74
  end
75
75
  elsif path.exist? then
76
76
  puts "#{path.basename path.extname}:"
77
- dsl.instance_eval(path.read, path.to_s)
77
+ harness.file_eval path
78
78
  else
79
79
  warn "path not found: #{path}"
80
80
  end
81
- rescue Exception => error
82
-
83
- if SignalException === error || SystemExit === error then
84
- exit 3
85
- end
86
-
87
- error_file, error_line, _ = error.backtrace.first.split ?:
88
-
89
- message = <<-MSG
90
- #{error.class} : #{error.message}
91
-
92
- Uspec encountered an error when loading a test file.
93
- This is probably a typo in the test file or the file it is testing.
94
-
95
- If you think this is a bug in Uspec please report it: https://github.com/acook/uspec/issues/new
96
-
97
- Error occured when loading test file `#{spec || path}`.
98
- The origin of the error may be in file `#{error_file}` on line ##{error_line}.
99
-
100
- \t#{error.backtrace[0,3].join "\n\t"}
101
- MSG
102
- puts
103
- warn message
104
- stats.failure << Uspec::Result.new(message, error, caller)
105
-
106
- dsl.__uspec_cli.handle_interrupt! error
107
81
  end
108
82
 
109
83
  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,90 @@
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 file_eval path
18
+ define.instance_eval(path.read, path.to_s)
19
+ rescue Exception => error
20
+ if SignalException === error || SystemExit === error then
21
+ exit 3
22
+ end
23
+
24
+ error_file, error_line, _ = error.backtrace.first.split ?:
25
+
26
+ message = <<-MSG
27
+ #{error.class} : #{error.message}
28
+
29
+ Uspec encountered an error when loading a test file.
30
+ This is probably a typo in the test file or the file it is testing.
31
+
32
+ If you think this is a bug in Uspec please report it: https://github.com/acook/uspec/issues/new
33
+
34
+ Error occured when loading test file `#{spec || path}`.
35
+ The origin of the error may be in file `#{error_file}` on line ##{error_line}.
36
+
37
+ \t#{error.backtrace[0,3].join "\n\t"}
38
+ MSG
39
+ puts
40
+ warn message
41
+ stats << Uspec::Result.new(message, error, true)
42
+
43
+ cli.handle_interrupt! error
44
+ end
45
+
46
+ def spec_eval description, &block
47
+ ex = nil
48
+ state = 0
49
+ print ' -- ', description
50
+
51
+ if block then
52
+ begin
53
+ state = 1
54
+ spec = Uspec::Spec.new(self, description, &block)
55
+ raw_result = spec.__uspec_block
56
+ state = 2
57
+ rescue Exception => raw_result
58
+ state = 3
59
+ ex = true
60
+ end
61
+ end
62
+
63
+ result = Uspec::Result.new spec, raw_result, ex
64
+
65
+ unless block then
66
+ state = 4
67
+ result.pending!
68
+ end
69
+
70
+ stats << result
71
+
72
+ print ': ', result.pretty, "\n"
73
+ rescue => error
74
+ state = 5
75
+ message = <<-MSG
76
+ #{error.class} : #{error.message}
77
+
78
+ Uspec encountered an internal error, please report this bug: https://github.com/acook/uspec/issues/new
79
+
80
+ \t#{error.backtrace.join "\n\t"}
81
+ MSG
82
+ puts
83
+ warn message
84
+ stats << Uspec::Result.new(message, error, true)
85
+ ensure
86
+ cli.handle_interrupt! result.raw
87
+ return [state, error, result, raw_result]
88
+ end
89
+ end
90
+ end
data/lib/uspec/result.rb CHANGED
@@ -5,15 +5,18 @@ 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
+ TRACE_EXCLUDE_PATTERN = /#{Uspec.libpath.join 'lib'}|#{Uspec.libpath.join 'bin'}/
9
10
 
10
- def initialize spec, raw, source
11
+ def initialize spec, raw, ex
11
12
  @spec = spec
12
13
  @raw = raw
13
- @source = source
14
+ @ex = ex
14
15
  @handler = ::TOISB.wrap raw
16
+ @full_backtrace = false
17
+ @caller = caller
15
18
  end
16
- attr_reader :spec, :raw, :source, :handler
19
+ attr_reader :spec, :raw, :ex, :handler, :full_backtrace
17
20
 
18
21
  def pretty
19
22
  if raw == true then
@@ -22,7 +25,7 @@ module Uspec
22
25
  red raw
23
26
  elsif pending? then
24
27
  yellow 'pending'
25
- elsif Exception === raw then
28
+ elsif ex == true then
26
29
  [
27
30
  red('Exception'), vspace,
28
31
  hspace, 'Spec encountered an Exception ', newline,
@@ -41,8 +44,32 @@ module Uspec
41
44
  end
42
45
 
43
46
  def trace
44
- raw.backtrace.inject(String.new) do |text, line|
47
+ @backtrace ||= indent_bt clean_bt(raw.backtrace, !full_backtrace)
48
+ end
49
+
50
+ def source
51
+ @source ||= clean_bt @caller
52
+ end
53
+
54
+ def indent_bt bt
55
+ bt.inject(String.new) do |text, line|
45
56
  text << "#{hspace}#{line}#{newline}"
57
+ end if bt
58
+ end
59
+
60
+ def clean_bt bt, skip_internal = true
61
+ bt.inject(Array.new) do |t, line|
62
+ next t if skip_internal && line.match(TRACE_EXCLUDE_PATTERN)
63
+ t << rewrite_bt_caller(line)
64
+ end if bt
65
+ end
66
+
67
+ def rewrite_bt_caller line
68
+ return line if full_backtrace
69
+ if line.match TRACE_EXCLUDE_PATTERN then
70
+ line
71
+ else
72
+ line.sub /file_eval/, 'spec_block'
46
73
  end
47
74
  end
48
75
 
@@ -54,14 +81,29 @@ module Uspec
54
81
  "#{handler.subklassinfo}: "
55
82
  end
56
83
 
84
+ def desc
85
+ if String === spec then
86
+ spec
87
+ elsif Uspec::Spec === spec then
88
+ spec.instance_variable_get :@__uspec_description
89
+ else
90
+ spec.inspect
91
+ end
92
+ end
93
+
57
94
  # Attempts to inspect an object
58
95
  def inspector
59
96
  if String === raw && raw.include?(?\n) then
60
97
  # if object is a multiline string, display it unescaped
61
-
62
98
  [
63
99
  raw.split(newline).unshift(newline).join(PREFIX), normal, newline,
64
100
  ].join
101
+ elsif Exception === raw then
102
+ [
103
+ raw.message, vspace,
104
+ white(trace),
105
+ normal, newline,
106
+ ].join
65
107
  else
66
108
  handler.inspector!
67
109
  end
@@ -82,7 +124,7 @@ module Uspec
82
124
 
83
125
  If you think this is a bug in Uspec please report it: https://github.com/acook/uspec/issues/new
84
126
 
85
- Error may have occured in test `#{spec}` in file `#{error_file}` on line ##{error_line}.
127
+ Error may have occured in test `#{desc}` in file `#{error_file}` on line ##{error_line}.
86
128
 
87
129
  \t#{error.backtrace.join "\n\t"}
88
130
  MSG
@@ -105,7 +147,7 @@ module Uspec
105
147
  end
106
148
 
107
149
  def inspect
108
- "#{self.class} for `#{spec}` -> #{pretty}"
150
+ "#{self.class} for `#{desc}` -> #{pretty}"
109
151
  end
110
152
  end
111
153
  end
data/lib/uspec/spec.rb CHANGED
@@ -3,31 +3,33 @@ require_relative "result"
3
3
  module Uspec
4
4
  class Spec
5
5
 
6
- def initialize dsl, description, &block
6
+ def initialize harness, description, &block
7
7
  @__uspec_description = description
8
- @__uspec_dsl = dsl
8
+ @__uspec_block = block
9
+ @__uspec_harness = harness
10
+ ns = harness.define
9
11
 
10
- dsl.instance_variables.each do |name|
11
- self.instance_variable_set(name, @__uspec_dsl.instance_variable_get(name)) unless name.to_s.include? '@__uspec'
12
+ ns.instance_variables.each do |name|
13
+ self.instance_variable_set(
14
+ name,
15
+ ns.instance_variable_get(name)
16
+ ) unless name.to_s.include? '@__uspec'
12
17
  end
13
18
 
14
- dsl.methods(false).each do |name|
19
+ ns.methods(false).each do |name|
15
20
  self.define_singleton_method name do |*args, &block|
16
- @__uspec_dsl.send name, *args, &block
21
+ ns.send name, *args, &block
17
22
  end unless name.to_s.include? '__uspec'
18
23
  end
19
24
 
20
25
  if block then
21
- self.define_singleton_method :__uspec_block, &block
26
+ self.define_singleton_method :__uspec_block, &block
22
27
  else
23
28
  self.define_singleton_method :__uspec_block do
24
- raise "Uspec: No block provided for `#{@__uspec_description}`"
29
+ raise NotImplementedError, "Uspec: No block provided for `#{@__uspec_description}`"
25
30
  end
26
31
  end
27
- end
32
+ end # initialize
28
33
 
29
- def spec description, &block
30
- @__uspec_dsl.spec description, &block
31
- end
32
34
  end
33
35
  end
data/lib/uspec/stats.rb CHANGED
@@ -5,6 +5,16 @@ 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
data/lib/uspec/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Uspec
2
- VERSION = '1.2.0'
2
+ VERSION = '1.4.0'
3
3
  end
data/lib/uspec.rb CHANGED
@@ -1,7 +1,3 @@
1
- require_relative 'uspec/version'
2
- require_relative 'uspec/dsl'
3
- require_relative 'uspec/stats'
4
-
5
1
  module Uspec
6
2
  def self.included object
7
3
  warn 'Use extend instead of include.'
@@ -14,4 +10,13 @@ module Uspec
14
10
  # object.extend Uspec::DSL
15
11
  #end
16
12
  end
13
+
14
+ def self.libpath
15
+ Pathname.new(__FILE__).dirname.dirname
16
+ end
17
17
  end
18
+
19
+ require_relative 'uspec/version'
20
+ require_relative 'uspec/harness'
21
+ require_relative 'uspec/define'
22
+ require_relative 'uspec/stats'
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'
@@ -9,73 +18,61 @@ spec 'shows usage' do
9
18
  end
10
19
 
11
20
  spec 'runs a path of specs' do
12
- output = capture do
13
- path = Pathname.new(__FILE__).parent.parent.join('example_specs').to_s
14
- Uspec::CLI.new(Array(path)).run_specs
21
+ output = outstr do
22
+ run_specs exdir.to_s
15
23
  end
16
24
 
17
25
  output.include?('I love passing tests') || output
18
26
  end
19
27
 
20
28
  spec 'runs an individual spec' do
21
- output = capture do
22
- path = Pathname.new(__FILE__).parent.parent.join('example_specs', 'example_spec.rb').to_s
23
- Uspec::CLI.new(Array(path)).run_specs
29
+ output = outstr do
30
+ run_specs exdir.join('example_spec.rb').to_s
24
31
  end
25
32
 
26
33
  output.include?('I love passing tests') || output
27
34
  end
28
35
 
29
36
  spec 'broken requires in test files count as test failures' do
30
- path = Pathname.new(__FILE__).parent.join('test_specs', 'broken_require_spec')
37
+ output, status = Open3.capture2e "#{root}/bin/uspec #{testdir.join('broken_require_spec')}"
31
38
 
32
- output = capture do
33
- exec "bin/uspec #{path}"
34
- end
35
-
36
- $?.exitstatus == 1 || $?
39
+ status.exitstatus == 1 || status
37
40
  end
38
41
 
39
42
  spec 'displays information about test file with broken require' do
40
- path = Pathname.new(__FILE__).parent.join('test_specs', 'broken_require_spec')
41
-
42
- output = capture do
43
- exec "bin/uspec #{path}"
44
- end
43
+ output, status = Open3.capture2e "#{root}/bin/uspec #{testdir.join('broken_require_spec')}"
45
44
 
46
45
  output.include?('cannot load such file') || output
47
46
  end
48
47
 
49
48
  spec 'exit code is the number of failures' do
50
49
  expected = 50
51
- output = capture do
52
- @__uspec_dsl.__uspec_stats.clear_results! # because we're forking, we will have a copy of the current results
50
+ cli = new_cli
53
51
 
52
+ outstr do
54
53
  expected.times do |count|
55
- spec "fail ##{count + 1}" do
54
+ cli.harness.define.spec "fail ##{count + 1}" do
56
55
  false
57
56
  end
58
57
  end
59
-
60
- exit @__uspec_dsl.__uspec_cli.exit_code
61
58
  end
62
- actual = $?.exitstatus
63
59
 
60
+ actual = cli.exit_code
64
61
  actual == expected || output
65
62
  end
66
63
 
67
64
  spec 'when more than 255 failures, exit status is 255' do
68
- output = capture do
69
- @__uspec_dsl.__uspec_stats.clear_results! # because we're forking, we will have a copy of the current results
65
+ expected = 255
66
+ cli = new_cli
70
67
 
68
+ output = outstr do
71
69
  500.times do
72
- spec 'fail' do
70
+ cli.harness.define.spec 'fail' do
73
71
  false
74
72
  end
75
73
  end
76
-
77
- exit @__uspec_dsl.__uspec_cli.exit_code
78
74
  end
79
75
 
80
- $?.exitstatus == 255 || [$?, output]
76
+ actual = cli.exit_code
77
+ actual == expected || [$?, output]
81
78
  end
data/uspec/dsl_spec.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  require_relative "uspec_helper"
2
2
 
3
3
  spec 'catches errors' do
4
- output = capture do
5
- spec 'exception' do
4
+ cli = new_cli
5
+
6
+ output = outstr do
7
+ cli.harness.define.spec 'exception' do
6
8
  raise 'test exception'
7
9
  end
8
10
  end
@@ -11,8 +13,10 @@ spec 'catches errors' do
11
13
  end
12
14
 
13
15
  spec 'catches even non-StandardError-subclass exceptions' do
14
- output = capture do
15
- spec 'not implemented error' do
16
+ cli = new_cli
17
+
18
+ output = outstr do
19
+ cli.harness.define.spec 'not implemented error' do
16
20
  raise ::NotImplementedError, 'test exception'
17
21
  end
18
22
  end
@@ -21,9 +25,9 @@ spec 'catches even non-StandardError-subclass exceptions' do
21
25
  end
22
26
 
23
27
  spec 'Uspec exits when sent a termination signal' do
24
- path = Pathname.new(__FILE__).parent.join('test_specs', 'kill_this_spec')
28
+ path = testdir.join('kill_this_spec')
25
29
 
26
- stdin, allout, thread = Open3.popen2e "uspec/test_specs/kill_this_script.sh \"#{path}\""
30
+ stdin, allout, thread = Open3.popen2e "#{testdir}/kill_this_script.sh \"#{path}\""
27
31
  stdin.close
28
32
  output = allout.read
29
33
 
@@ -40,8 +44,10 @@ spec 'Uspec exits when sent a termination signal' do
40
44
  end
41
45
 
42
46
  spec 'complains when spec block returns non boolean' do
43
- output = capture do
44
- spec 'whatever' do
47
+ cli = new_cli
48
+
49
+ output = outstr do
50
+ cli.harness.define.spec 'whatever' do
45
51
  "string"
46
52
  end
47
53
  end
@@ -50,10 +56,10 @@ spec 'complains when spec block returns non boolean' do
50
56
  end
51
57
 
52
58
  spec 'marks test as pending when no block supplied' do
53
- path = Pathname.new(__FILE__).parent.join('test_specs', 'pending_spec')
59
+ path = testdir.join('pending_spec')
54
60
 
55
61
  output = capture do
56
- exec "bin/uspec #{path}"
62
+ exec "#{root}/bin/uspec #{path}"
57
63
  end
58
64
 
59
65
  output.include?('1 pending') || output
@@ -64,40 +70,40 @@ spec 'should not define DSL methods on arbitrary objects' do
64
70
  end
65
71
 
66
72
  spec 'when return used in spec, capture it as an error' do
67
- path = Pathname.new(__FILE__).parent.join('test_specs', 'return_spec')
73
+ path = testdir.join('return_spec')
68
74
 
69
75
  output = capture do
70
- exec "bin/uspec #{path}"
76
+ exec "#{root}/bin/uspec #{path}"
71
77
  end
72
78
 
73
79
  output.include?('Invalid return') || output.include?('Spec did not return a boolean value') || output
74
80
  end
75
81
 
76
82
  spec 'when break used in spec, capture it as an error' do
77
- path = Pathname.new(__FILE__).parent.join('test_specs', 'break_spec')
83
+ path = testdir.join('break_spec')
78
84
 
79
85
  output = capture do
80
- exec "bin/uspec #{path}"
86
+ exec "#{root}/bin/uspec #{path}"
81
87
  end
82
88
 
83
89
  output.include?('Invalid break') || output.include?('Spec did not return a boolean value') || output
84
90
  end
85
91
 
86
92
  spec 'when instance variables are defined in the DSL instance, they are available in the spec body' do
87
- path = Pathname.new(__FILE__).parent.join('test_specs', 'ivar_spec')
93
+ path = testdir.join('ivar_spec')
88
94
 
89
95
  output = capture do
90
- exec "bin/uspec #{path}"
96
+ exec "#{root}/bin/uspec #{path}"
91
97
  end
92
98
 
93
99
  output.include?('1 successful') || output
94
100
  end
95
101
 
96
102
  spec 'when methods are defined in the DSL instance, they are available in the spec body' do
97
- path = Pathname.new(__FILE__).parent.join('test_specs', 'method_spec')
103
+ path = testdir.join('method_spec')
98
104
 
99
105
  output = capture do
100
- exec "bin/uspec #{path}"
106
+ exec "#{root}/bin/uspec #{path}"
101
107
  end
102
108
 
103
109
  output.include?('1 successful') || output
data/uspec/result_spec.rb CHANGED
@@ -3,7 +3,7 @@ require_relative "uspec_helper"
3
3
  bo = BasicObject.new
4
4
 
5
5
  spec "#pretty doesn't die when given a BasicObject" do
6
- result = Uspec::Result.new "BasicObject Result", bo, []
6
+ result = Uspec::Result.new "BasicObject Result", bo, nil
7
7
  expected = "#<BasicObject:"
8
8
  actual = result.pretty
9
9
  actual.include?(expected) || actual
@@ -14,28 +14,28 @@ class ::TestObject < BasicObject; end
14
14
  obj = TestObject.new
15
15
 
16
16
  spec "ensure BasicObject subclass instances work" do
17
- result = Uspec::Result.new "BasicObject Subclass Result", obj, []
17
+ result = Uspec::Result.new "BasicObject Subclass Result", obj, nil
18
18
  expected = "#<BasicObject/TestObject:"
19
19
  actual = result.pretty
20
20
  actual.include?(expected) || result.pretty
21
21
  end
22
22
 
23
23
  spec "display basic info about Object" do
24
- result = Uspec::Result.new "Object Result", Object.new, []
24
+ result = Uspec::Result.new "Object Result", Object.new, nil
25
25
  expected = "Object < BasicObject: \e[0m#<Object:"
26
26
  actual = result.pretty
27
27
  actual.include?(expected) || result.pretty
28
28
  end
29
29
 
30
30
  spec "display basic info about Array" do
31
- result = Uspec::Result.new "Array Result", [], []
31
+ result = Uspec::Result.new "Array Result", [], nil
32
32
  expected = "Array < Object"
33
33
  actual = result.pretty
34
34
  actual.include?(expected) || result.pretty
35
35
  end
36
36
 
37
37
  spec "display basic info about Array class" do
38
- result = Uspec::Result.new "Array Class Result", Array, []
38
+ result = Uspec::Result.new "Array Class Result", Array, nil
39
39
  expected = "Class < Module: \e[0mArray Class"
40
40
  actual = result.pretty
41
41
  actual.include?(expected) || result.pretty
@@ -44,7 +44,7 @@ end
44
44
  parent = [obj]
45
45
 
46
46
  spec "ensure parent object of BasicObject subclasses get a useful error message" do
47
- result = Uspec::Result.new "BasicObject Parent Result", parent, []
47
+ result = Uspec::Result.new "BasicObject Parent Result", parent, nil
48
48
  expected = "BasicObject and its subclasses"
49
49
  actual = result.pretty
50
50
  actual.include?(expected) || result.inspector
@@ -54,7 +54,7 @@ class ::InspectFail; def inspect; raise RuntimeError, "This error is intentional
54
54
  inspect_fail = InspectFail.new
55
55
 
56
56
  spec "display a useful error message when a user-defined inspect method fails" do
57
- result = Uspec::Result.new "Inspect Fail Result", inspect_fail, []
57
+ result = Uspec::Result.new "Inspect Fail Result", inspect_fail, nil
58
58
  expected = "raises an exception"
59
59
  actual = result.pretty
60
60
  actual.include?(expected) || result.inspector
@@ -63,7 +63,48 @@ end
63
63
  spec "display strings more like their actual contents" do
64
64
  string = "this string:\nshould display \e\[42;2mproperly"
65
65
  expected = /this string:\n.*should display \e\[42;2mproperly/
66
- result = Uspec::Result.new "Inspect Fail Result", string, []
66
+ result = Uspec::Result.new "Inspect Fail Result", string, nil
67
67
  actual = result.pretty
68
68
  actual.match?(expected) || result.inspector
69
69
  end
70
+
71
+ def exception_value
72
+ raise "A test exception!"
73
+ rescue => err
74
+ return err
75
+ end
76
+
77
+ spec "handles exception values" do
78
+ result = Uspec::Result.new "Exception Value Result", exception_value, nil
79
+ expected = "RuntimeError < StandardError: \e[0mA test exception!"
80
+ actual = result.pretty
81
+ actual.include?(expected) || result.pretty
82
+ end
83
+
84
+ spec "handles exception values including backtraces" do
85
+ result = Uspec::Result.new "Exception Value Result", exception_value, nil
86
+ expected = "exception_value"
87
+ actual = result.pretty
88
+ actual.include?(expected) || result.pretty
89
+ end
90
+
91
+ spec "handles raised exceptions" do
92
+ result = Uspec::Result.new "Exception Raised Result", exception_value, true
93
+ expected = "RuntimeError < StandardError: \e[0mA test exception!"
94
+ actual = result.pretty
95
+ actual.include?(expected) || result.pretty
96
+ end
97
+
98
+ spec "handles raised exceptions without backtraces" do
99
+ result = Uspec::Result.new "Exception Raised Result", Exception.new, true
100
+ expected = "Exception < Object: \e[0mException"
101
+ actual = result.pretty
102
+ actual.include?(expected) || result.pretty
103
+ end
104
+
105
+ spec "doesn't show 'run' for spec file in stack trace" do
106
+ result = Uspec::Result.new "No Run Exception Trace Result", exception_value, true
107
+ expected = /uspec.*run/
108
+ actual = result.pretty
109
+ !actual.match?(expected) || result.pretty
110
+ end
data/uspec/stats_spec.rb CHANGED
@@ -1,12 +1,19 @@
1
1
  require_relative 'uspec_helper'
2
2
 
3
+ def stats_object
4
+ Uspec::Stats.new
5
+ end
6
+
3
7
  spec 'stats can be inspected' do
4
- actual = @__uspec_dsl.__uspec_stats.inspect
8
+ # this is a regression test
9
+ # the issue it covers occured because of a refactor where stats had a custom inspect method
10
+ # stats no longer has a custom inspect method, but this makes sure that nothing breaks
11
+ actual = @__uspec_harness.stats.inspect
5
12
  actual.include?("failure") || actual
6
13
  end
7
14
 
8
15
  spec 'stats inspect does not have any stray whitespace' do
9
- output = @__uspec_dsl.__uspec_stats.inspect
10
- match = output.match(/ |\n/)
11
- match == nil
16
+ output = stats_object.inspect
17
+ match = output.match /(.*(?: |\n))/m
18
+ match == nil || match
12
19
  end
@@ -6,22 +6,21 @@ THISDIR="$(realpath "$(dirname "$0")")"
6
6
  ROOTDIR="$(realpath "$THISDIR/../..")"
7
7
 
8
8
  path="$1"
9
- name="$(basename "$path")"
10
9
 
11
10
  if ! [ -f "$path" ]; then
12
11
  echo "file not found: $path"
13
12
  exit 255
14
13
  fi
15
14
 
16
- mkdir -v -p tmp
17
15
  if command_exists bundle; then
18
- bundle exec "$ROOTDIR/bin/uspec" "$path" > "tmp/$name.output" 2>&1 &
16
+ echo running with bundler
17
+ bundle exec "$ROOTDIR/bin/uspec" "$path" 2>&1 &
18
+ pid="$!"
19
19
  else
20
- "$ROOTDIR/bin/uspec" "$path" > "tmp/$name.output" 2>&1 &
20
+ echo running directly
21
+ "$ROOTDIR/bin/uspec" "$path" 2>&1 &
22
+ pid="$!"
21
23
  fi
22
24
 
23
- pid=$!
24
25
  sleep 1
25
- kill $pid
26
- cat "tmp/$name.output"
27
- rm -v "tmp/$name.output"
26
+ kill "$pid"
@@ -4,6 +4,7 @@ rescue LoadError => err
4
4
  nil
5
5
  end
6
6
  require 'open3'
7
+ require 'stringio'
7
8
 
8
9
  require_relative '../lib/uspec'
9
10
  extend Uspec
@@ -24,3 +25,37 @@ def capture
24
25
 
25
26
  output
26
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
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.4.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-22 00:00:00.000000000 Z
11
+ date: 2024-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: that_object_is_so_basic
@@ -49,7 +49,8 @@ files:
49
49
  - example_specs/spec_helper.rb
50
50
  - lib/uspec.rb
51
51
  - lib/uspec/cli.rb
52
- - lib/uspec/dsl.rb
52
+ - lib/uspec/define.rb
53
+ - lib/uspec/harness.rb
53
54
  - lib/uspec/result.rb
54
55
  - lib/uspec/spec.rb
55
56
  - lib/uspec/stats.rb
data/lib/uspec/dsl.rb DELETED
@@ -1,66 +0,0 @@
1
- require_relative "result"
2
- require_relative "spec"
3
-
4
- module Uspec
5
- class DSL
6
-
7
- def initialize cli
8
- @__uspec_cli = cli
9
- end
10
-
11
- def __uspec_cli
12
- @__uspec_cli
13
- end
14
-
15
- def __uspec_stats
16
- @__uspec_cli.stats
17
- end
18
-
19
- def spec description, &block
20
- state = 0
21
- print ' -- ', description
22
-
23
- if block then
24
- begin
25
- state = 1
26
- raw_result = ::Uspec::Spec.new(self, description, &block).__uspec_block
27
- state = 2
28
- rescue Exception => raw_result
29
- state = 3
30
- end
31
- end
32
-
33
- result = Result.new description, raw_result, caller
34
-
35
- unless block then
36
- state = 4
37
- result.pending!
38
- end
39
-
40
- if result.success?
41
- __uspec_stats.success << result
42
- elsif result.pending?
43
- __uspec_stats.pending << result
44
- else
45
- __uspec_stats.failure << result
46
- end
47
-
48
- print ': ', result.pretty, "\n"
49
- rescue => error
50
- state = 5
51
- message = <<-MSG
52
- #{error.class} : #{error.message}
53
-
54
- Uspec encountered an internal error, please report this bug: https://github.com/acook/uspec/issues/new
55
-
56
- \t#{error.backtrace.join "\n\t"}
57
- MSG
58
- puts
59
- warn message
60
- __uspec_stats.failure << Uspec::Result.new(message, error, caller)
61
- ensure
62
- __uspec_cli.handle_interrupt! result.raw
63
- return [state, error, result, raw_result]
64
- end
65
- end
66
- end