Narnach-minitest 0.2

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/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Wes Oldenbeuving
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,35 @@
1
+ == Minitest
2
+ Minitest is a simple autotester intended to be used with rSpec and rCov.
3
+ It can be used with 'plain' ruby projects and Ruby on Rails.
4
+
5
+ == Installation
6
+ === From gem
7
+ Use gem to install minitest. The gem is located on github.
8
+ sudo gem install narnach-minitest -s http://gems.github.com
9
+ === From git
10
+ From the project root, use rake to install:
11
+ git clone git://github.com/Narnach/minitest.git
12
+ cd minitest
13
+ rake install
14
+ This will build the gem and install it for you.
15
+
16
+ == Usage
17
+ Minitest has the following command line options:
18
+ recent:: Only specs for files modified within the last hour are executed on the first run. All files are still monitored for changes, though.
19
+ profile:: Force rspec output format to 'profile', combined with coloured output and unified diffs.
20
+
21
+ == Examples
22
+ When you start working on a codebase, it makes sense to run all specs:
23
+ minitest
24
+ When you resume work on a codebase you recently worked on, it might be useful skip running the specs for unchanged files on the first run:
25
+ minitest recent
26
+ When you want to see which specs are slow and could use optimizing:
27
+ minitest profile
28
+ You can also combine options:
29
+ minitest recent profile
30
+
31
+ == About
32
+ Author:: Wes 'Narnach' Oldenbeuving (narnach@gmail.com)
33
+ Website:: http://www.github.com/Narnach/minitest
34
+ Copyright:: Copyright (c) 2008 Wes Oldenbeuving
35
+ License:: MIT license. See MIT-LICENSE (in the gem directory) for license details.
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ require "rake"
2
+ require "rake/clean"
3
+ require "rake/gempackagetask"
4
+ require 'rubygems'
5
+ require 'lib/minitest'
6
+
7
+ ################################################################################
8
+ ### Gem
9
+ ################################################################################
10
+
11
+ begin
12
+ # Parse gemspec using the github safety level.
13
+ data = File.read('minitest.gemspec')
14
+ spec = nil
15
+ Thread.new { spec = eval("$SAFE = 3\n%s" % data)}.join
16
+
17
+ # Create the gem tasks
18
+ Rake::GemPackageTask.new(spec) do |package|
19
+ package.gem_spec = spec
20
+ end
21
+ rescue Exception => e
22
+ printf "WARNING: Error caught (%s): %s\n", e.class.name, e.message
23
+ end
24
+
25
+ desc 'Package and install the gem for the current version'
26
+ task :install => :gem do
27
+ system "sudo gem install -l pkg/minitest-%s.gem" % spec.version
28
+ end
29
+
30
+ namespace :gem do
31
+ desc 'Re-generate the gemspec'
32
+ task :gemspec do
33
+ files = Dir["*.rdoc"] + %w( Rakefile MIT-LICENSE ) + Dir["{spec,lib,bin}/**/*"]
34
+ tests = Dir["spec/**/*"]
35
+ data = File.read('minitest.gemspec.template')
36
+ files_string = "[%s]" % files.sort.uniq.map{|f| "'%s'" % f}.join(", ")
37
+ tests_string = "[%s]" % tests.sort.uniq.map{|f| "'%s'" % f}.join(", ")
38
+ data.gsub!(':FILES:', files_string)
39
+ data.gsub!(':TEST_FILES:', tests_string)
40
+ File.open('minitest.gemspec','wb') { |f| f.puts(data) }
41
+ puts data
42
+ end
43
+ end
44
+
data/bin/minitest ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
3
+ require 'minitest'
4
+
5
+ minitest = Minitest.new
6
+ minitest.recent = (ARGV.include? "recent")
7
+ minitest.spec_opts = "--format profile --colour --diff unified" if ARGV.include? "profile"
8
+ minitest.start
@@ -0,0 +1,76 @@
1
+ # Dirmonitor's purpose is to serve as a file monitoring helper for Minitest.
2
+ # Its intended functionality is:
3
+ # - Keep track of new files in monitored directories.
4
+ # - Keep track of changed files in monitored directories.
5
+ # - Link these files to their specs, so Minitest can run the specs.
6
+ class DirMonitor
7
+ attr_reader :known_files, :dirs
8
+
9
+ # Setup a new DirMonitor.
10
+ # Directories can be provided in a number of ways:
11
+ # DirMonitor.new 'lib', 'app'
12
+ # DirMonitor.new :lib, :app
13
+ # DirMonitor.new %w[lib app]
14
+ # Each of these examples will result in the same list of directories being stored for use.
15
+ def initialize(*dirs)
16
+ @dirs = dirs.flatten.map{|dir| dir.to_s}
17
+ @known_files = []
18
+ end
19
+
20
+ # Scan for all files in the directories and their sub-directories.
21
+ # The results are stored as a single array in @known_files.
22
+ def scan
23
+ results = []
24
+ dirs.each do |dir|
25
+ files_in_dir = Dir.glob(File.join(dir,'**','*'))
26
+ results.concat(files_in_dir)
27
+ end
28
+ @known_files = results
29
+ end
30
+
31
+ # Scan for new files.
32
+ # All new file names are yielded.
33
+ def scan_new(&block) # :yields: file
34
+ old_known_files = @known_files
35
+ scan
36
+ new_files = known_files - old_known_files
37
+ new_files.each do |new_file|
38
+ block.call(new_file)
39
+ end
40
+ end
41
+
42
+ # Scans for new files, like scan_new does, but yields the name of both the file and spec.
43
+ # spec_for is used to determine what the name of the file's spec _should_ be.
44
+ # Does not yield a file/spec name when the spec does not exist.
45
+ def scan_new_with_spec(&block) # :yields: file, spec
46
+ scan_new do |file|
47
+ spec = spec_for(file)
48
+ block.call(file, spec) if File.exists?(spec)
49
+ end
50
+ end
51
+
52
+ # Find the (theoretical) spec file name for a given file.
53
+ # The assumptions are:
54
+ # - All specs reside in the 'spec' directory.
55
+ # - All specs file names have the suffix '_spec.rb', instead of only the '.rb' extension.
56
+ # - The file name for a non-ruby file spec simply has '_spec.rb' suffixed to the entire file name.
57
+ # The returned file name does not necessarily have to exist.
58
+ def spec_for(file)
59
+ base = File.basename(file)
60
+ extension = File.extname(base)
61
+ dir = File.dirname(file)
62
+ dir_array = dir.split('/')
63
+ if extension == '.rb' and dir_array.first=='spec'
64
+ return file
65
+ end
66
+ if extension == '.rb'
67
+ base_without_extension = base[0, base.size - extension.size]
68
+ spec_file = base_without_extension + '_spec' + extension
69
+ else
70
+ spec_file = base + '_spec.rb'
71
+ end
72
+ dir_array[0]='spec'
73
+ spec_dir = dir_array.join('/')
74
+ return File.join(spec_dir, spec_file)
75
+ end
76
+ end
data/lib/minitest.rb ADDED
@@ -0,0 +1,204 @@
1
+ # = Minitest
2
+ # The default usage of Minitest is this:
3
+ # minitest = Minitest.new
4
+ # minitest.start
5
+ # This will do the following:
6
+ # - Lookup all spec files in the spec/ directory.
7
+ # - Lookup all possible associated files in the lib/ and app/ directories.
8
+ # - Remember the mtimes (last modification times) of all relevant files.
9
+ # - Every second, check all known relevant files: if their mtime changes, run rspec on their spec file.
10
+ # - Run rcov (code coverage tester) on all specs when exiting (Press ctrl-C on send SIGINT to the process)
11
+ class Minitest
12
+ attr_reader :file_mtime
13
+ attr_reader :file_spec
14
+ attr_accessor :rcov_ignores
15
+ attr_accessor :recent
16
+ attr_accessor :recent_time
17
+ attr_accessor :source_dirs
18
+ attr_accessor :source_extensions
19
+ attr_accessor :spec_cmd
20
+ attr_accessor :spec_opts
21
+
22
+ DEFAULT_RCOV_IGNORES = %w[spec/ db/ plugins/ vendor/ config/]
23
+ DEFAULT_RECENT_TIME = 3600
24
+ DEFAULT_SOURCE_DIRS = %w[lib app]
25
+ DEFAULT_SOURCE_EXTENSIONS = %w[rb haml rhtml erb]
26
+
27
+ def initialize
28
+ @file_spec = {} # Map files to their specs
29
+ @file_mtime = {} # Map files to their mtimes
30
+ @need_testing = [] # Specs that need testing
31
+ @first_run = true
32
+ @active = false
33
+ end
34
+
35
+ def active?
36
+ @active == true
37
+ end
38
+
39
+ def first_run?
40
+ @first_run == true
41
+ end
42
+
43
+ # Partial filepaths to exclude from rcov output
44
+ def rcov_ignores
45
+ ignores = @rcov_ignores || DEFAULT_RCOV_IGNORES
46
+ ignores << spec_cmd
47
+ ignores.join(",")
48
+ end
49
+
50
+ # Command line string to run rcov for all monitored specs.
51
+ def rcov
52
+ "#{rcov_cmd} -T --exclude \"#{rcov_ignores}\" -Ilib #{spec_cmd} -- " + self.unique_specs.join(" ")
53
+ end
54
+
55
+ def recent?
56
+ @recent == true
57
+ end
58
+
59
+ # Maximum amount of seconds since a file has been changed for it to count as
60
+ # recently changed.
61
+ def recent_time
62
+ @recent_time || DEFAULT_RECENT_TIME
63
+ end
64
+
65
+ # Command line string to run rspec for an array of specs. Defaults to all specs.
66
+ def rspec(specs=self.unique_specs)
67
+ "#{spec_cmd} #{specs.join(" ")} #{spec_opts}"
68
+ end
69
+
70
+ def source_dirs
71
+ @source_dirs || DEFAULT_SOURCE_DIRS
72
+ end
73
+
74
+ def source_extensions
75
+ @source_extensions || DEFAULT_SOURCE_EXTENSIONS
76
+ end
77
+
78
+ def rcov_cmd
79
+ @rcov_cmd ||= find_rcov_cmd
80
+ end
81
+
82
+ # The command to use to run specs.
83
+ def spec_cmd
84
+ @spec_cmd ||= ( File.exist?('script/spec') ? 'script/spec' : find_spec_cmd )
85
+ end
86
+
87
+ def spec_opts
88
+ @spec_opts ||= ( File.exist?('spec/spec.opts') ? '-O spec/spec.opts' : '' )
89
+ end
90
+
91
+ def start
92
+ @active = true
93
+ find_specs
94
+
95
+ if self.file_spec.values.uniq.size == 0
96
+ puts "There are no specs to run."
97
+ return
98
+ end
99
+
100
+ need_testing = find_first_run_specs
101
+
102
+ trap_int_for_rcov
103
+ while active? do
104
+ if need_testing.size > 0
105
+ print "\nTesting files: #{need_testing.join(" ")}\n"
106
+ system rspec(need_testing)
107
+ end
108
+ sleep 1
109
+ need_testing = find_specs_to_check
110
+ end
111
+ end
112
+
113
+ def trap_int_for_rcov
114
+ Signal.trap("INT") do
115
+ print "\nNow we run rcov and we're done.\n\n"
116
+ puts rcov
117
+ system rcov
118
+ @active = false
119
+ end
120
+ end
121
+
122
+ def unique_specs
123
+ self.file_spec.values.uniq.sort
124
+ end
125
+
126
+ private
127
+
128
+ def find_first_run_specs
129
+ need_checking = self.file_mtime.keys.dup
130
+ specs_to_check = []
131
+ if first_run? and recent?
132
+ need_checking.reject! { |file| @file_mtime[file] < ( Time.now - recent_time ) }
133
+ if need_checking.size > 0
134
+ puts "This first run will only test files changed in the last hour. All other files are still monitored."
135
+ else
136
+ puts "No files were changed in the last hour, so no files are tested for the first run."
137
+ end
138
+ end
139
+ specs_to_check = need_checking.map {|f| @file_spec[f]}
140
+ specs_to_check.uniq!
141
+ specs_to_check.sort!
142
+ @first_run = false
143
+ return specs_to_check
144
+ end
145
+
146
+ def find_sources_for_spec(spec)
147
+ found_files = []
148
+ for dir in self.source_dirs
149
+ next unless spec[0,4]=='spec'
150
+
151
+ file = spec.dup
152
+ file[0,4]=dir
153
+ file.gsub!("_spec.",".")
154
+ candidates = self.source_extensions.map { |ext| file.gsub(/[^.]+\Z/, ext)}
155
+ candidates = candidates.select { |f| File.exist?(f) }
156
+ found_files += candidates
157
+ end
158
+ found_files.uniq!
159
+ return found_files
160
+ end
161
+
162
+ def find_rcov_cmd
163
+ `which rcov`.strip
164
+ end
165
+
166
+ def find_spec_cmd
167
+ `which spec`.strip
168
+ end
169
+
170
+ def find_specs
171
+ Dir.glob("spec/**/*_spec.rb").each do |spec|
172
+ # If a spec changes, run it again.
173
+ map_file_to_spec(spec,spec)
174
+ for file in find_sources_for_spec(spec)
175
+ map_file_to_spec(file,spec)
176
+ end
177
+ end
178
+ end
179
+
180
+ def find_specs_to_check
181
+ specs = []
182
+ @file_mtime.each do |file, old_mtime|
183
+ current_mtime = File.mtime(file)
184
+ if current_mtime != old_mtime
185
+ specs << @file_spec[file]
186
+ store_mtime file
187
+ end
188
+ end
189
+ specs.uniq!
190
+ specs.sort!
191
+ return specs
192
+ end
193
+
194
+ def map_file_to_spec(file,spec)
195
+ return if @file_mtime.has_key? file
196
+ @file_spec[file.dup]=spec.dup
197
+ store_mtime file
198
+ store_mtime spec
199
+ end
200
+
201
+ def store_mtime(file)
202
+ @file_mtime[file.dup] = File.mtime(file)
203
+ end
204
+ end
@@ -0,0 +1,107 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
2
+ require 'dir_monitor'
3
+
4
+ describe DirMonitor, ".new" do
5
+ it "should accept multiple directories" do
6
+ dm = DirMonitor.new('lib','app')
7
+ dm.dirs.should == ['lib','app']
8
+ end
9
+
10
+ it "should accept an array with multiple directories" do
11
+ dm = DirMonitor.new(%w[lib app])
12
+ dm.dirs.should == ['lib','app']
13
+ end
14
+
15
+ it "should have no known files" do
16
+ dm = DirMonitor.new('lib')
17
+ dm.known_files.should == []
18
+ end
19
+ end
20
+
21
+ describe DirMonitor, "#scan" do
22
+ it "should find all files in the directories" do
23
+ known_files1 = %w[lib/minitest.rb lib/dir_monitor.rb]
24
+ known_files2 = %w[app/another.rb app/files.rb app/more/things.txt]
25
+ Dir.should_receive(:glob).with('lib/**/*').and_return(known_files1)
26
+ Dir.should_receive(:glob).with('app/**/*').and_return(known_files2)
27
+ dm = DirMonitor.new('lib', 'app')
28
+ dm.scan
29
+ dm.known_files.should == known_files1 + known_files2
30
+ end
31
+ end
32
+
33
+ describe DirMonitor, "#scan_new" do
34
+ it "should yield the names of all new files" do
35
+ known_files = %w[lib/minitest.rb lib/dir_monitor.rb]
36
+ Dir.should_receive(:glob).with('lib/**/*').and_return(known_files)
37
+ dm = DirMonitor.new('lib')
38
+ yield_results = []
39
+ dm.scan_new do |file|
40
+ yield_results << file
41
+ end
42
+ yield_results.should == known_files
43
+ end
44
+
45
+ it "should not yield known file names" do
46
+ known_files = %w[lib/minitest.rb lib/dir_monitor.rb]
47
+ known_files2 = %w[lib/minitest.rb lib/dir_monitor2.rb]
48
+ Dir.should_receive(:glob).with('lib/**/*').and_return(known_files, known_files2)
49
+ dm = DirMonitor.new('lib')
50
+ dm.scan
51
+ yield_results = []
52
+ dm.scan_new do |file|
53
+ yield_results << file
54
+ end
55
+ yield_results.should == known_files2 - known_files
56
+ end
57
+ end
58
+
59
+ describe DirMonitor, "#spec_for" do
60
+ it "should find the spec for a given file" do
61
+ file = 'lib/dir_monitor.rb'
62
+ spec = 'spec/dir_monitor_spec.rb'
63
+ dm = DirMonitor.new
64
+ dm.spec_for(file).should == spec
65
+ end
66
+
67
+ it "should find the spec for non-ruby files" do
68
+ file = 'app/views/posts/post.html.haml'
69
+ spec = 'spec/views/posts/post.html.haml_spec.rb'
70
+ dm = DirMonitor.new
71
+ dm.spec_for(file).should == spec
72
+ end
73
+
74
+ it "should map specs to themselves" do
75
+ spec = 'spec/dir_monitor_spec.rb'
76
+ dm = DirMonitor.new
77
+ dm.spec_for(spec).should == spec
78
+ end
79
+ end
80
+
81
+ describe DirMonitor, "#scan_new_with_spec" do
82
+ it "should yield new files and their specs" do
83
+ file = 'lib/dir_monitor.rb'
84
+ spec = 'spec/dir_monitor_spec.rb'
85
+ Dir.should_receive(:glob).with('lib/**/*').and_return([file])
86
+ File.should_receive(:exists?).with(spec).and_return(true)
87
+ dm = DirMonitor.new 'lib'
88
+ results = []
89
+ dm.scan_new_with_spec do |new_file, new_spec|
90
+ results << {new_file => new_spec}
91
+ end
92
+ results.should == [{file=>spec}]
93
+ end
94
+
95
+ it "should not yield files with non-existent specs" do
96
+ file = 'lib/dir_monitor.rb'
97
+ spec = 'spec/dir_monitor_spec.rb'
98
+ Dir.should_receive(:glob).with('lib/**/*').and_return([file])
99
+ File.should_receive(:exists?).with(spec).and_return(false)
100
+ dm = DirMonitor.new 'lib'
101
+ results = []
102
+ dm.scan_new_with_spec do |new_file, new_spec|
103
+ results << {new_file => new_spec}
104
+ end
105
+ results.should == []
106
+ end
107
+ end
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
2
+ require 'minitest'
3
+
4
+ describe Minitest do
5
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,3 @@
1
+ --diff unified
2
+ --colour
3
+ --format specdoc
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: Narnach-minitest
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.2"
5
+ platform: ruby
6
+ authors:
7
+ - Wes Oldenbeuving
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-06-21 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">"
21
+ - !ruby/object:Gem::Version
22
+ version: 0.0.0
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: rcov
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">"
30
+ - !ruby/object:Gem::Version
31
+ version: 0.0.0
32
+ version:
33
+ description: Minitest is a simple autotester tool, which uses rSpec and rCov to test ruby and rails projects.
34
+ email: narnach@gmail.com
35
+ executables:
36
+ - minitest
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - README.rdoc
41
+ - MIT-LICENSE
42
+ files:
43
+ - MIT-LICENSE
44
+ - README.rdoc
45
+ - Rakefile
46
+ - bin/minitest
47
+ - lib/dir_monitor.rb
48
+ - lib/minitest.rb
49
+ - spec/dir_monitor_spec.rb
50
+ - spec/minitest_spec.rb
51
+ - spec/spec.opts
52
+ has_rdoc: true
53
+ homepage: http://www.github.com/Narnach/minitest
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --inline-source
57
+ - --line-numbers
58
+ - --main
59
+ - README.rdoc
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 1.8.0
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.0.1
78
+ signing_key:
79
+ specification_version: 2
80
+ summary: Minitest is a simple autotester tool.
81
+ test_files:
82
+ - spec/dir_monitor_spec.rb
83
+ - spec/minitest_spec.rb
84
+ - spec/spec.opts