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 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, if the second parameter to
12
- `Cutest.run` is true, it is also loaded inside an anonymous module. Once a
13
- failure is found in a file, the rest of the file is skipped and the error is
14
- reported. This way, running your test suite feels faster.
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. Another shift in design is that one dot is shown after a file is
126
- examined, and not the usual one-dot-per-assertion. And finally, the execution
127
- of a file stops one the first failure is found.
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
- else
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.1.5"
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
- require "batch"
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
- class Cutest < Batch
4
- VERSION = "0.1.5"
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
- def report_error(_, error)
7
- $stderr.puts "#{error}\n"
8
- end
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
- def self.run(files, anonymous = false)
11
- each(files) do |file|
12
- read, write = IO.pipe
30
+ begin
31
+ load(file)
13
32
 
14
- fork do
15
- read.close
33
+ rescue LoadError, SyntaxError
34
+ puts ["\n", error([file, $!.message])]
35
+ exit
16
36
 
17
- begin
18
- load(file, anonymous)
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
- rescue Cutest::AssertionFailed => e
21
- write.puts e.message
43
+ stdout.close
22
44
 
23
- rescue Exception => e
24
- error = [e.message]
25
- error += e.backtrace.take_while { |line| !line.index(__FILE__) }
45
+ Process.wait
26
46
 
27
- write.puts ">> #{cutest[:test]}"
28
- write.puts error.join("\n")
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
- write.close
60
+ puts
61
+ end
33
62
 
34
- Process.wait
63
+ def self.error(e)
64
+ "\033[01;36mException: \033[01;33m#{e}\033[00m"
65
+ end
35
66
 
36
- output = read.read
37
- raise Cutest::AssertionFailed.new(output) unless output.empty?
38
- read.close
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
- file, line = caller[1].split(":")
140
- code = File.readlines(file)[line.to_i - 1]
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 Cutest::AssertionFailed.new(msg)
171
+ raise exception
144
172
  end
145
173
  end
data/test/assert_raise.rb CHANGED
@@ -4,7 +4,6 @@ test "catches the right exception" do
4
4
  end
5
5
  end
6
6
 
7
-
8
7
  test "raises if the expectation is not met" do
9
8
  assert_raise(Cutest::AssertionFailed) do
10
9
  assert_raise(RuntimeError) do
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 = " 0% .\n100% \n"
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: false
4
+ prerelease: true
5
5
  segments:
6
- - 0
7
6
  - 1
8
- - 5
9
- version: 0.1.5
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-09-24 00:00:00 -03:00
19
+ date: 2010-10-10 00:00:00 -03:00
19
20
  default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
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
- - 0
83
- version: "0"
69
+ - 1
70
+ - 3
71
+ - 1
72
+ version: 1.3.1
84
73
  requirements: []
85
74
 
86
75
  rubyforge_project: