shindo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/README.rdoc +112 -0
  4. data/Rakefile +57 -0
  5. data/VERSION +1 -0
  6. data/bin/shindo +42 -0
  7. data/lib/shindo.rb +240 -0
  8. metadata +70 -0
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
@@ -0,0 +1,112 @@
1
+ = shindo
2
+
3
+ Simple depth first ruby testing, watch and learn.
4
+
5
+ == Writing tests
6
+
7
+ There are two commands you should know, 'tests' and 'test'. Tests help you group similar calls to test, but their return value is ignored. The return value of test, on the other hand, determines success or failure.
8
+
9
+ A successful test should return true. (yay)
10
+
11
+ Shindo.tests {
12
+ test('something really important') { true }
13
+ }
14
+
15
+ A failing test should return false or nil. (boo)
16
+
17
+ Shindo.tests {
18
+ test('something (hopefully) less important') { false }
19
+ }
20
+
21
+ A pending test shouldn't even have a block (meh)
22
+
23
+ Shindo.tests {
24
+ test('something that hasn't been gotten around to yet')
25
+ }
26
+
27
+ Grouping tests
28
+
29
+ Shindo.tests {
30
+ tests('foo/bar') {
31
+ test('foo') { true }
32
+ test('bar') { true }
33
+ }
34
+ }
35
+
36
+ You can also have descriptions for Shindo.tests, and write that last one like this
37
+
38
+ Shindo.tests('foo/bar') {
39
+ test('foo') { true }
40
+ test('bar') { false }
41
+ }
42
+
43
+ Nest tests as deeply as you would like.
44
+
45
+ Then, if you want to get real fancy you can also tag your tests, to help narrow down which ones to run
46
+
47
+ Shindo.tests {
48
+ tests('foo/bar', ['foo', 'bar']) {
49
+ test('foo') { true }
50
+ test('bar') { true }
51
+ }
52
+ }
53
+
54
+ Or if you can narrow down even more tightly
55
+
56
+ Shindo.tests {
57
+ tests('foo/bar') {
58
+ test('foo', ['foo']) { true }
59
+ test('bar', ['bar']) { true }
60
+ }
61
+ }
62
+
63
+ == Running tests
64
+
65
+ Run tests with the shindo command, the easiest is to specify a file name:
66
+
67
+ shindo some_test_file.rb
68
+
69
+ You can also give directories and it will run all .rb files inside (recurses through subdirectories)
70
+
71
+ shindo some_test_directory
72
+
73
+ That leaves tags, which use +/-. So run just tests with the 'foo' tag:
74
+
75
+ shindo some_test_directory +foo
76
+
77
+ Or those without the 'bar' tag:
78
+
79
+ shindo some_test_directory -bar
80
+
81
+ Or combine to run everything with a foo tag, but no bar tag
82
+
83
+ shindo some_test_directory +foo -bar
84
+
85
+ == Command line tools
86
+
87
+ When tests fail you'll get lots of options to help you debug the problem, just enter '?' at the prompt to see your options.
88
+
89
+ == Copyright
90
+
91
+ (The MIT License)
92
+
93
+ Copyright (c) 2009 {geemus (Wesley Beary)}[http://github.com/geemus]
94
+
95
+ Permission is hereby granted, free of charge, to any person obtaining
96
+ a copy of this software and associated documentation files (the
97
+ "Software"), to deal in the Software without restriction, including
98
+ without limitation the rights to use, copy, modify, merge, publish,
99
+ distribute, sublicense, and/or sell copies of the Software, and to
100
+ permit persons to whom the Software is furnished to do so, subject to
101
+ the following conditions:
102
+
103
+ The above copyright notice and this permission notice shall be
104
+ included in all copies or substantial portions of the Software.
105
+
106
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
107
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
108
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
109
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
110
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
111
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
112
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.add_dependency('annals')
8
+ gem.name = "shindo"
9
+ gem.summary = %Q{ruby testing}
10
+ gem.description = %Q{Simple depth first ruby testing}
11
+ gem.email = "me@geemus.com"
12
+ gem.homepage = "http://github.com/geemus/shindo"
13
+ gem.authors = ["geemus (Wesley Beary)"]
14
+ gem.rubyforge_project = "shindo"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/*_test.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/*_test.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ if File.exist?('VERSION')
48
+ version = File.read('VERSION')
49
+ else
50
+ version = ""
51
+ end
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "shindo #{version}"
55
+ rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'shindo')
3
+
4
+ files = []
5
+ tags = []
6
+ for argument in ARGV
7
+ if argument.match(/^[\+\-]/)
8
+ tags << argument
9
+ else
10
+ path = File.expand_path(argument)
11
+ if File.directory?(path)
12
+ files |= Dir.glob(File.join(path, '**', '*.rb'))
13
+ else
14
+ files << path
15
+ end
16
+ end
17
+ end
18
+
19
+ success = true
20
+ def run_in_thread(files, tags)
21
+ tests = Thread.new {
22
+ Thread.current[:tags] = tags
23
+ for file in files
24
+ load(file)
25
+ end
26
+ }
27
+ tests.join
28
+ if tests[:reload]
29
+ run_in_thread(files, tags)
30
+ else
31
+ success = success && tests[:success]
32
+ end
33
+ end
34
+ run_in_thread(files, tags)
35
+
36
+ at_exit {
37
+ if success
38
+ exit(0)
39
+ else
40
+ exit(1)
41
+ end
42
+ }
@@ -0,0 +1,240 @@
1
+ require 'rubygems'
2
+ require 'annals'
3
+
4
+ module Shindo
5
+
6
+ def self.tests(header = nil, &block)
7
+ STDOUT.sync = true
8
+ Shindo::Tests.new(header, &block)
9
+ end
10
+
11
+ class Tests
12
+
13
+ attr_accessor :backtrace
14
+
15
+ def initialize(header, tags = [], &block)
16
+ @afters = []
17
+ @annals = Annals.new
18
+ @befores = []
19
+ @description_stack = []
20
+ @if_tagged = Thread.current[:tags].select {|tag| tag.match(/^\+/)}
21
+ @unless_tagged = Thread.current[:tags].select {|tag| tag.match(/^\-/)}.map {|tag| tag[1..-1]}
22
+ @indent = 1
23
+ @success = true
24
+ @tag_stack = []
25
+ Thread.current[:reload] = false
26
+ print("\n")
27
+ tests(header, &block)
28
+ print("\n")
29
+ if @success
30
+ Thread.current[:success] = false
31
+ else
32
+ Thread.current[:success] = false
33
+ end
34
+ end
35
+
36
+ def after(&block)
37
+ @afters[-1].push(block)
38
+ end
39
+
40
+ def before(&block)
41
+ @befores[-1].push(block)
42
+ end
43
+
44
+ def full_description
45
+ "#{@description_stack.compact.join(' ')}#{full_tags}"
46
+ end
47
+
48
+ def full_tags
49
+ unless @tag_stack.flatten.empty?
50
+ " [#{@tag_stack.flatten.join(', ')}]"
51
+ end
52
+ end
53
+
54
+ def green_line(content)
55
+ print_line(content, "\e[32m")
56
+ end
57
+
58
+ def indent(&block)
59
+ @indent += 1
60
+ yield
61
+ @indent -= 1
62
+ end
63
+
64
+ def indentation
65
+ ' ' * @indent
66
+ end
67
+
68
+ def print_line(content, color = nil)
69
+ if color && STDOUT.tty?
70
+ content = "#{color}#{content}\e[0m"
71
+ end
72
+ print("#{indentation}#{content}\n")
73
+ end
74
+
75
+ def prompt(&block)
76
+ print("#{indentation}Action? [c,i,q,r,t,#,?]? ")
77
+ choice = STDIN.gets.strip
78
+ print("\n")
79
+ case choice
80
+ when 'c'
81
+ return
82
+ when 'i'
83
+ print_line('Starting interactive session...')
84
+ if @irb.nil?
85
+ require 'irb'
86
+ ARGV.clear # Avoid passing args to IRB
87
+ IRB.setup(nil)
88
+ @irb = IRB::Irb.new(nil)
89
+ IRB.conf[:MAIN_CONTEXT] = @irb.context
90
+ IRB.conf[:PROMPT][:TREST] = {}
91
+ end
92
+ for key, value in IRB.conf[:PROMPT][:SIMPLE]
93
+ IRB.conf[:PROMPT][:TREST][key] = "#{indentation}#{value}"
94
+ end
95
+ @irb.context.prompt_mode = :TREST
96
+ @irb.context.workspace = IRB::WorkSpace.new(block.binding)
97
+ begin
98
+ @irb.eval_input
99
+ rescue SystemExit
100
+ end
101
+ when 'q'
102
+ Thread.current[:success] = false
103
+ Thread.exit
104
+ when 'r'
105
+ print("Reloading...\n")
106
+ Thread.current[:reload] = true
107
+ Thread.exit
108
+ when 't'
109
+ indent {
110
+ if @annals.lines.empty?
111
+ print_line('no backtrace available')
112
+ else
113
+ index = 1
114
+ for line in @annals.lines
115
+ print_line("#{' ' * (2 - index.to_s.length)}#{index} #{line}")
116
+ index += 1
117
+ end
118
+ end
119
+ }
120
+ print("\n")
121
+ when '?'
122
+ print_line('c - ignore this error and continue')
123
+ print_line('i - interactive mode')
124
+ print_line('q - quit Shindo')
125
+ print_line('r - reload and run the tests again')
126
+ print_line('t - display backtrace')
127
+ print_line('# - enter a number of a backtrace line to see its context')
128
+ print_line('? - display help')
129
+ when /\d/
130
+ index = choice.to_i - 1
131
+ if backtrace.lines[index]
132
+ indent {
133
+ print_line("#{@annals.lines[index]}: ")
134
+ indent {
135
+ print("\n")
136
+ current_line = @annals.buffer[index]
137
+ File.open(current_line[:file], 'r') do |file|
138
+ data = file.readlines
139
+ current = current_line[:line]
140
+ min = [0, current - (@annals.max / 2)].max
141
+ max = [current + (@annals.max / 2), data.length].min
142
+ min.upto(current - 1) do |line|
143
+ print_line("#{line} #{data[line].rstrip}")
144
+ end
145
+ yellow_line("#{current} #{data[current].rstrip}")
146
+ (current + 1).upto(max - 1) do |line|
147
+ print_line("#{line} #{data[line].rstrip}")
148
+ end
149
+ end
150
+ print("\n")
151
+ }
152
+ }
153
+ else
154
+ red_line("#{choice} is not a valid backtrace line, please try again.")
155
+ end
156
+ else
157
+ red_line("#{choice} is not a valid choice, please try again.")
158
+ end
159
+ red_line("- #{full_description}")
160
+ prompt(&block)
161
+ end
162
+
163
+ def red_line(content)
164
+ print_line(content, "\e[31m")
165
+ end
166
+
167
+ def tests(description, tags = [], &block)
168
+ @tag_stack.push([*tags])
169
+ @befores.push([])
170
+ @afters.push([])
171
+
172
+ print_line(description || 'Shindo.tests')
173
+ if block_given?
174
+ indent { instance_eval(&block) }
175
+ end
176
+
177
+ @afters.pop
178
+ @befores.pop
179
+ @tag_stack.pop
180
+ end
181
+
182
+ def test(description, tags = [], &block)
183
+ @description_stack.push(description)
184
+ @tag_stack.push([*tags])
185
+
186
+ # if the test includes tags and discludes ^tags, evaluate it
187
+ if (@if_tagged.empty? || !(@if_tagged & @tag_stack.flatten).empty?) &&
188
+ (@unless_tagged.empty? || (@unless_tagged & @tag_stack.flatten).empty?)
189
+ if block_given?
190
+ for before in @befores.flatten.compact
191
+ before.call
192
+ end
193
+
194
+ @annals.start
195
+ begin
196
+ success = instance_eval(&block)
197
+ @annals.stop
198
+ rescue => error
199
+ success = false
200
+ file, line, method = error.backtrace.first.split(':')
201
+ if method
202
+ method << "in #{method[4...-1]} " # get method from "in `foo'"
203
+ else
204
+ method = ''
205
+ end
206
+ method << "! #{error.message} (#{error.class})"
207
+ @annals.stop
208
+ @annals.unshift(:file => file, :line => line.to_i, :method => method)
209
+ end
210
+ @success = @success && success
211
+ if success
212
+ green_line("+ #{full_description}")
213
+ else
214
+ red_line("- #{full_description}")
215
+ if STDOUT.tty?
216
+ prompt(&block)
217
+ end
218
+ end
219
+
220
+ for after in @afters.flatten.compact
221
+ after.call
222
+ end
223
+ else
224
+ yellow_line("* #{full_description}")
225
+ end
226
+ else
227
+ print_line("_ #{full_description}")
228
+ end
229
+
230
+ @tag_stack.pop
231
+ @description_stack.pop
232
+ end
233
+
234
+ def yellow_line(content)
235
+ print_line(content, "\e[33m")
236
+ end
237
+
238
+ end
239
+
240
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shindo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - geemus (Wesley Beary)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-07 00:00:00 -07:00
13
+ default_executable: shindo
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: annals
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Simple depth first ruby testing
26
+ email: me@geemus.com
27
+ executables:
28
+ - shindo
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.rdoc
33
+ files:
34
+ - .document
35
+ - .gitignore
36
+ - README.rdoc
37
+ - Rakefile
38
+ - VERSION
39
+ - bin/shindo
40
+ - lib/shindo.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/geemus/shindo
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --charset=UTF-8
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project: shindo
65
+ rubygems_version: 1.3.5
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: ruby testing
69
+ test_files: []
70
+