cutest 0.1.5 → 1.0.0.beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +32 -24
- data/bin/cutest +52 -3
- data/cutest.gemspec +1 -2
- data/lib/cutest.rb +61 -33
- data/test/assert_raise.rb +0 -1
- data/test/run.rb +1 -33
- metadata +13 -24
data/README.markdown
CHANGED
@@ -8,10 +8,10 @@ Description
|
|
8
8
|
|
9
9
|
Run tests in separate processes to avoid shared state.
|
10
10
|
|
11
|
-
Each test file is run in a forked process and
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
Each test file is run in a forked process and. Once a failure is found in a
|
12
|
+
file, you get a debugger and the ability to fix the error in place. If you
|
13
|
+
choose to quit the debugger, the rest of the file is skipped. This way, doing
|
14
|
+
TDD is easier and running your test suite feels faster.
|
15
15
|
|
16
16
|
You can use the `scope` command around tests: it guarantees that no instance
|
17
17
|
variables are shared between tests.
|
@@ -50,11 +50,15 @@ In your Rakefile:
|
|
50
50
|
require "cutest"
|
51
51
|
|
52
52
|
task :test do
|
53
|
-
Cutest.run(Dir["test
|
53
|
+
Cutest.run(Dir["test/*.rb"])
|
54
54
|
end
|
55
55
|
|
56
56
|
task :default => :test
|
57
57
|
|
58
|
+
Or from the command line:
|
59
|
+
|
60
|
+
$ cutest test/*.rb
|
61
|
+
|
58
62
|
In your tests:
|
59
63
|
|
60
64
|
setup do
|
@@ -73,22 +77,6 @@ In your tests:
|
|
73
77
|
assert 23 == params[:a]
|
74
78
|
end
|
75
79
|
|
76
|
-
|
77
|
-
To run the tests:
|
78
|
-
|
79
|
-
$ rake
|
80
|
-
|
81
|
-
If you get an error, the report will look like this:
|
82
|
-
|
83
|
-
>> should preserve the original values from the setup
|
84
|
-
=> assert 24 == params[:a]
|
85
|
-
test/a_test.rb +14
|
86
|
-
|
87
|
-
|
88
|
-
Instead of a description of the error, you get to see the assertion
|
89
|
-
that failed along with the file and line number. Adding a debugger and
|
90
|
-
fixing the bug is left as an exercise for the reader.
|
91
|
-
|
92
80
|
An example working with a prepare block:
|
93
81
|
|
94
82
|
prepare do
|
@@ -122,9 +110,29 @@ And working with scopes:
|
|
122
110
|
The tests in these two examples will pass.
|
123
111
|
|
124
112
|
Unlike other testing frameworks, Cutest does not compile all the tests before
|
125
|
-
running them.
|
126
|
-
|
127
|
-
|
113
|
+
running them.
|
114
|
+
|
115
|
+
Handling errors
|
116
|
+
---------------
|
117
|
+
|
118
|
+
If you get an error when running the tests, you will see a debugger prompt:
|
119
|
+
|
120
|
+
Exception: Cutest::AssertionFailed
|
121
|
+
Type continue to retry or edit to modify the source
|
122
|
+
.Breakpoint 1 at test/setup.rb:14
|
123
|
+
[12, 16] in test/setup.rb
|
124
|
+
12
|
125
|
+
13 test "should preserve the original values from the setup" do |params|
|
126
|
+
=> 14 assert 24 == params[:a]
|
127
|
+
15 end
|
128
|
+
16
|
129
|
+
test/setup.rb:14
|
130
|
+
assert 24 == params[:a]
|
131
|
+
(rdb:1)
|
132
|
+
|
133
|
+
Instead of a getting a report of the error, you get to interact with it live
|
134
|
+
and even fix it: if you type `edit`, the file is opened in your editor. Once
|
135
|
+
you fix the code and save it, the debugger will reload it and retry.
|
128
136
|
|
129
137
|
Installation
|
130
138
|
------------
|
data/bin/cutest
CHANGED
@@ -3,7 +3,56 @@
|
|
3
3
|
require File.expand_path("../lib/cutest", File.dirname(__FILE__))
|
4
4
|
|
5
5
|
if ARGV.empty?
|
6
|
-
puts "usage: cutest file ..."
|
7
|
-
|
8
|
-
Cutest.run(Dir[*ARGV])
|
6
|
+
puts "usage: cutest [-r lib] [-v] file ..."
|
7
|
+
exit
|
9
8
|
end
|
9
|
+
|
10
|
+
class Cutest
|
11
|
+
|
12
|
+
# Clap: Command line argument parsing.
|
13
|
+
# http://github.com/soveran/clap
|
14
|
+
# http://rubygems.org/gems/clap
|
15
|
+
class Clap
|
16
|
+
VERSION = "0.0.1"
|
17
|
+
|
18
|
+
attr :argv
|
19
|
+
attr :opts
|
20
|
+
|
21
|
+
def self.run(args, opts)
|
22
|
+
new(args, opts).run
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(argv, opts)
|
26
|
+
@argv = argv.reverse.dup
|
27
|
+
@opts = opts
|
28
|
+
end
|
29
|
+
|
30
|
+
def run
|
31
|
+
args = []
|
32
|
+
|
33
|
+
while argv.any?
|
34
|
+
item = argv.pop
|
35
|
+
|
36
|
+
if opts[item]
|
37
|
+
|
38
|
+
# Call the lambda with N items from argv,
|
39
|
+
# where N is the lambda's arity.
|
40
|
+
opts[item].call(*argv.pop(opts[item].arity))
|
41
|
+
else
|
42
|
+
|
43
|
+
# Collect the items that don't correspond to
|
44
|
+
# flags.
|
45
|
+
args << item
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
args
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
files = Cutest::Clap.run ARGV,
|
55
|
+
"-r" => lambda { |file| require file },
|
56
|
+
"-v" => lambda { puts Cutest::VERSION }
|
57
|
+
|
58
|
+
Cutest.run(Dir[*files]) if files.any?
|
data/cutest.gemspec
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "cutest"
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "1.0.0.beta"
|
4
4
|
s.summary = "Forking tests."
|
5
5
|
s.description = "Run tests in separate processes to avoid shared state."
|
6
6
|
s.authors = ["Damian Janowski", "Michel Martens"]
|
7
7
|
s.email = ["djanowski@dimaion.com", "michel@soveran.com"]
|
8
8
|
s.homepage = "http://github.com/djanowski/cutest"
|
9
9
|
s.files = ["LICENSE", "README.markdown", "Rakefile", "lib/cutest.rb", "cutest.gemspec", "test/assert.rb", "test/assert_raise.rb", "test/prepare.rb", "test/run.rb", "test/scopes.rb", "test/setup.rb"]
|
10
|
-
s.add_dependency "batch", "~> 0.0.3"
|
11
10
|
s.executables.push "cutest"
|
12
11
|
end
|
data/lib/cutest.rb
CHANGED
@@ -1,48 +1,75 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require "ruby-debug"
|
3
|
+
rescue LoadError
|
4
|
+
puts "Cutest needs ruby-debug, but it couldn't be required."
|
5
|
+
puts "Please install ruby-debug or ruby-debug19 and try again."
|
6
|
+
exit
|
7
|
+
end
|
2
8
|
|
3
|
-
|
4
|
-
|
9
|
+
Debugger.settings[:autoeval] = 1
|
10
|
+
Debugger.settings[:autolist] = 1
|
11
|
+
Debugger.settings[:listsize] = 5
|
12
|
+
Debugger.settings[:reload_source_on_change] = 1
|
5
13
|
|
6
|
-
|
7
|
-
|
8
|
-
|
14
|
+
class Cutest
|
15
|
+
VERSION = "1.0.0.beta"
|
16
|
+
|
17
|
+
def self.run(files)
|
18
|
+
trap("INT") { exit }
|
19
|
+
trap("TERM") { exit }
|
20
|
+
|
21
|
+
files.each do |file|
|
22
|
+
Debugger.start do
|
23
|
+
|
24
|
+
loop do
|
25
|
+
stdin, stdout = IO.pipe
|
26
|
+
|
27
|
+
fork do
|
28
|
+
stdin.close
|
9
29
|
|
10
|
-
|
11
|
-
|
12
|
-
read, write = IO.pipe
|
30
|
+
begin
|
31
|
+
load(file)
|
13
32
|
|
14
|
-
|
15
|
-
|
33
|
+
rescue LoadError, SyntaxError
|
34
|
+
puts ["\n", error([file, $!.message])]
|
35
|
+
exit
|
16
36
|
|
17
|
-
|
18
|
-
|
37
|
+
rescue Exception
|
38
|
+
fn, ln = $!.backtrace.first.split(":")
|
39
|
+
stdout.write("#{fn}\n#{ln}\n#{error($!)}\n#{hint}")
|
40
|
+
end
|
41
|
+
end
|
19
42
|
|
20
|
-
|
21
|
-
write.puts e.message
|
43
|
+
stdout.close
|
22
44
|
|
23
|
-
|
24
|
-
error = [e.message]
|
25
|
-
error += e.backtrace.take_while { |line| !line.index(__FILE__) }
|
45
|
+
Process.wait
|
26
46
|
|
27
|
-
|
28
|
-
|
47
|
+
output = stdin.read
|
48
|
+
|
49
|
+
unless output.empty?
|
50
|
+
fn, ln, error, hint = output.split("\n")
|
51
|
+
Debugger.add_breakpoint(fn, ln.to_i)
|
52
|
+
puts ["\n", error, hint]
|
53
|
+
else
|
54
|
+
break
|
55
|
+
end
|
29
56
|
end
|
30
57
|
end
|
58
|
+
end
|
31
59
|
|
32
|
-
|
60
|
+
puts
|
61
|
+
end
|
33
62
|
|
34
|
-
|
63
|
+
def self.error(e)
|
64
|
+
"\033[01;36mException: \033[01;33m#{e}\033[00m"
|
65
|
+
end
|
35
66
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
67
|
+
def self.hint
|
68
|
+
"\033[01;36mType \033[0;33mcontinue\033[0;36m to retry " +
|
69
|
+
"or \033[0;33medit\033[0;36m to modify the source\033[00m"
|
40
70
|
end
|
41
71
|
|
42
72
|
class AssertionFailed < StandardError
|
43
|
-
def backtrace
|
44
|
-
[]
|
45
|
-
end
|
46
73
|
end
|
47
74
|
|
48
75
|
class Scope
|
@@ -121,6 +148,7 @@ private
|
|
121
148
|
# Assert that value is not nil or false.
|
122
149
|
def assert(value)
|
123
150
|
flunk unless value
|
151
|
+
print "."
|
124
152
|
end
|
125
153
|
|
126
154
|
# Assert that the block doesn't raise the expected exception.
|
@@ -130,16 +158,16 @@ private
|
|
130
158
|
rescue => exception
|
131
159
|
ensure
|
132
160
|
flunk unless exception.kind_of?(expected)
|
161
|
+
print "."
|
133
162
|
end
|
134
163
|
end
|
135
164
|
|
136
165
|
# Stop the tests and raise an error where the message is the last line
|
137
166
|
# executed before flunking.
|
138
167
|
def flunk
|
139
|
-
|
140
|
-
|
141
|
-
msg = ">> #{cutest[:test]}\n=> #{code.strip}\n #{file} +#{line}"
|
168
|
+
exception = Cutest::AssertionFailed.new
|
169
|
+
exception.set_backtrace([caller[1]])
|
142
170
|
|
143
|
-
raise
|
171
|
+
raise exception
|
144
172
|
end
|
145
173
|
end
|
data/test/assert_raise.rb
CHANGED
data/test/run.rb
CHANGED
@@ -11,7 +11,7 @@ ensure
|
|
11
11
|
end
|
12
12
|
|
13
13
|
test "output of successful run" do
|
14
|
-
expected = "
|
14
|
+
expected = "\n"
|
15
15
|
|
16
16
|
stdout, stderr = capture do
|
17
17
|
Cutest.run(Dir["test/fixtures/success.rb"])
|
@@ -19,35 +19,3 @@ test "output of successful run" do
|
|
19
19
|
|
20
20
|
assert stdout == expected
|
21
21
|
end
|
22
|
-
|
23
|
-
test "output of failed run" do
|
24
|
-
stdout, stderr = capture do
|
25
|
-
Cutest.run(Dir["test/fixtures/failure.rb"])
|
26
|
-
end
|
27
|
-
|
28
|
-
assert stdout == " 0% E\n100% \n"
|
29
|
-
assert stderr["\nSome errors occured:\n\n>> failed assertion\n=> assert false\n"]
|
30
|
-
assert stderr["test/fixtures/failure.rb +2\n\n"]
|
31
|
-
end
|
32
|
-
|
33
|
-
test "output of failed run on a custom assertion" do
|
34
|
-
|
35
|
-
stdout, stderr = capture do
|
36
|
-
Cutest.run(Dir["test/fixtures/fail_custom_assertion.rb"])
|
37
|
-
end
|
38
|
-
|
39
|
-
assert stdout == " 0% E\n100% \n"
|
40
|
-
assert stderr["\nSome errors occured:\n\n>> failed custom assertion\n=> assert_empty \"foo\"\n"]
|
41
|
-
assert stderr["test/fixtures/fail_custom_assertion.rb +6\n\n"]
|
42
|
-
end
|
43
|
-
|
44
|
-
test "output of failed run on an exception" do
|
45
|
-
stdout, stderr = capture do
|
46
|
-
Cutest.run(Dir["test/fixtures/exception.rb"])
|
47
|
-
end
|
48
|
-
|
49
|
-
assert stdout == " 0% E\n100% \n"
|
50
|
-
assert stderr["\nSome errors occured:\n\n>> some unhandled exception\nOops\n"]
|
51
|
-
assert stderr["test/fixtures/exception.rb:2:in `foo'\n"]
|
52
|
-
assert stderr["test/fixtures/exception.rb:6"]
|
53
|
-
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cutest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
4
|
+
prerelease: true
|
5
5
|
segments:
|
6
|
-
- 0
|
7
6
|
- 1
|
8
|
-
-
|
9
|
-
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- beta
|
10
|
+
version: 1.0.0.beta
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Damian Janowski
|
@@ -15,24 +16,10 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2010-
|
19
|
+
date: 2010-10-10 00:00:00 -03:00
|
19
20
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
22
|
-
name: batch
|
23
|
-
prerelease: false
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
- 0
|
32
|
-
- 3
|
33
|
-
version: 0.0.3
|
34
|
-
type: :runtime
|
35
|
-
version_requirements: *id001
|
21
|
+
dependencies: []
|
22
|
+
|
36
23
|
description: Run tests in separate processes to avoid shared state.
|
37
24
|
email:
|
38
25
|
- djanowski@dimaion.com
|
@@ -76,11 +63,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
63
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
64
|
none: false
|
78
65
|
requirements:
|
79
|
-
- - "
|
66
|
+
- - ">"
|
80
67
|
- !ruby/object:Gem::Version
|
81
68
|
segments:
|
82
|
-
-
|
83
|
-
|
69
|
+
- 1
|
70
|
+
- 3
|
71
|
+
- 1
|
72
|
+
version: 1.3.1
|
84
73
|
requirements: []
|
85
74
|
|
86
75
|
rubyforge_project:
|