configurability 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/rake/testing.rb ADDED
@@ -0,0 +1,187 @@
1
+ #
2
+ # Rake tasklib for testing tasks
3
+
4
+ #
5
+ # Authors:
6
+ # * Michael Granger <ged@FaerieMUD.org>
7
+ #
8
+
9
+ unless defined?( COVERAGE_MINIMUM )
10
+ if ENV['COVVERAGE_MINIMUM']
11
+ COVERAGE_MINIMUM = Float( ENV['COVERAGE_MINIMUM'] )
12
+ else
13
+ COVERAGE_MINIMUM = 85.0
14
+ end
15
+ end
16
+ SPEC_FILES = [] unless defined?( SPEC_FILES )
17
+ TEST_FILES = [] unless defined?( TEST_FILES )
18
+
19
+ COMMON_SPEC_OPTS = ['-Du'] unless defined?( COMMON_SPEC_OPTS )
20
+
21
+ COVERAGE_TARGETDIR = BASEDIR + 'coverage' unless defined?( COVERAGE_TARGETDIR )
22
+ RCOV_EXCLUDES = 'spec,tests,/Library/Ruby,/var/lib,/usr/local/lib' unless
23
+ defined?( RCOV_EXCLUDES )
24
+
25
+
26
+ desc "Run all defined tests"
27
+ task :test do
28
+ unless SPEC_FILES.empty?
29
+ log "Running specs"
30
+ Rake::Task['spec:quiet'].invoke
31
+ end
32
+
33
+ unless TEST_FILES.empty?
34
+ log "Running unit tests"
35
+ Rake::Task[:unittests].invoke
36
+ end
37
+ end
38
+
39
+
40
+ ### RSpec specifications
41
+ begin
42
+ gem 'rspec', '>= 1.1.3'
43
+
44
+ require 'spec'
45
+ require 'spec/rake/spectask'
46
+
47
+ ### Task: spec
48
+ desc "Run specs"
49
+ task :spec => 'spec:doc'
50
+
51
+ namespace :spec do
52
+ desc "Run rspec every time there's a change to one of the files"
53
+ task :autotest do
54
+ require 'autotest/rspec'
55
+
56
+ autotester = Autotest::Rspec.new
57
+ autotester.run
58
+ end
59
+
60
+ desc "Generate regular color 'doc' spec output"
61
+ Spec::Rake::SpecTask.new( :doc ) do |task|
62
+ task.spec_files = SPEC_FILES
63
+ task.spec_opts = COMMON_SPEC_OPTS + ['-f', 's', '-c']
64
+ end
65
+
66
+ desc "Generate spec output with profiling"
67
+ Spec::Rake::SpecTask.new( :profile ) do |task|
68
+ task.spec_files = SPEC_FILES
69
+ task.spec_opts = COMMON_SPEC_OPTS + ['-f', 'o']
70
+ end
71
+
72
+ desc "Generate quiet non-colored plain-text output"
73
+ Spec::Rake::SpecTask.new( :quiet ) do |task|
74
+ task.spec_files = SPEC_FILES
75
+ task.spec_opts = COMMON_SPEC_OPTS + ['-f', 'p']
76
+ end
77
+
78
+ desc "Generate HTML output"
79
+ Spec::Rake::SpecTask.new( :html ) do |task|
80
+ task.spec_files = SPEC_FILES
81
+ task.spec_opts = COMMON_SPEC_OPTS + ['-f', 'h']
82
+ end
83
+
84
+ end
85
+ rescue LoadError => err
86
+ task :no_rspec do
87
+ $stderr.puts "Specification tasks not defined: %s" % [ err.message ]
88
+ end
89
+
90
+ task :spec => :no_rspec
91
+ namespace :spec do
92
+ task :autotest => :no_rspec
93
+ task :doc => :no_rspec
94
+ task :profile => :no_rspec
95
+ task :quiet => :no_rspec
96
+ task :html => :no_rspec
97
+ end
98
+ end
99
+
100
+
101
+ ### Test::Unit tests
102
+ begin
103
+ require 'rake/testtask'
104
+
105
+ Rake::TestTask.new( :unittests ) do |task|
106
+ task.libs += [LIBDIR]
107
+ task.test_files = TEST_FILES
108
+ task.verbose = true
109
+ end
110
+
111
+ rescue LoadError => err
112
+ task :no_test do
113
+ $stderr.puts "Test tasks not defined: %s" % [ err.message ]
114
+ end
115
+
116
+ task :unittests => :no_rspec
117
+ end
118
+
119
+
120
+ ### RCov (via RSpec) tasks
121
+ begin
122
+ gem 'rcov'
123
+ gem 'rspec', '>= 1.1.3'
124
+
125
+ require 'spec'
126
+ require 'rcov'
127
+
128
+ ### Task: coverage (via RCov)
129
+ desc "Build test coverage reports"
130
+ unless SPEC_FILES.empty?
131
+ Spec::Rake::SpecTask.new( :coverage ) do |task|
132
+ task.spec_files = SPEC_FILES
133
+ task.libs += [LIBDIR]
134
+ task.spec_opts = ['-f', 'p', '-b']
135
+ task.rcov_opts = RCOV_OPTS
136
+ task.rcov = true
137
+ end
138
+ end
139
+
140
+
141
+ ### Task: rcov
142
+ task :rcov => :coverage
143
+
144
+ ### Other coverage tasks
145
+ namespace :coverage do
146
+ desc "Generate a detailed text coverage report"
147
+ Spec::Rake::SpecTask.new( :text ) do |task|
148
+ task.spec_files = SPEC_FILES
149
+ task.rcov_opts = RCOV_OPTS + ['--text-report']
150
+ task.rcov = true
151
+ end
152
+
153
+ desc "Show differences in coverage from last run"
154
+ Spec::Rake::SpecTask.new( :diff ) do |task|
155
+ task.spec_files = SPEC_FILES
156
+ task.spec_opts = ['-f', 'p', '-b']
157
+ task.rcov_opts = RCOV_OPTS - ['--save'] + ['--text-coverage-diff']
158
+ task.rcov = true
159
+ end
160
+
161
+ desc "Run RCov in 'spec-only' mode to check coverage from specs"
162
+ Spec::Rake::SpecTask.new( :speconly ) do |task|
163
+ task.spec_files = SPEC_FILES
164
+ task.rcov_opts = ['--exclude', RCOV_EXCLUDES, '--text-report', '--save']
165
+ task.rcov = true
166
+ end
167
+ end
168
+
169
+ CLOBBER.include( COVERAGE_TARGETDIR )
170
+
171
+ rescue LoadError => err
172
+ task :no_rcov do
173
+ $stderr.puts "Coverage tasks not defined: RSpec+RCov tasklib not available: %s" %
174
+ [ err.message ]
175
+ end
176
+
177
+ task :coverage => :no_rcov
178
+ task :clobber_coverage
179
+ task :rcov => :no_rcov
180
+ namespace :coverage do
181
+ task :text => :no_rcov
182
+ task :diff => :no_rcov
183
+ end
184
+ task :verify => :no_rcov
185
+ end
186
+
187
+
@@ -0,0 +1,64 @@
1
+ #####################################################################
2
+ ### S U B V E R S I O N T A S K S A N D H E L P E R S
3
+ #####################################################################
4
+
5
+ require 'rake/tasklib'
6
+
7
+ #
8
+ # Work around the inexplicable behaviour of the original RDoc::VerifyTask, which
9
+ # errors if your coverage isn't *exactly* the threshold.
10
+ #
11
+
12
+ # A task that can verify that the RCov coverage doesn't
13
+ # drop below a certain threshold. It should be run after
14
+ # running Spec::Rake::SpecTask.
15
+ class VerifyTask < Rake::TaskLib
16
+
17
+ COVERAGE_PERCENTAGE_PATTERN =
18
+ %r{<tt class='coverage_code'>(\d+\.\d+)%</tt>}
19
+
20
+ # Name of the task. Defaults to :verify_rcov
21
+ attr_accessor :name
22
+
23
+ # Path to the index.html file generated by RCov, which
24
+ # is the file containing the total coverage.
25
+ # Defaults to 'coverage/index.html'
26
+ attr_accessor :index_html
27
+
28
+ # Whether or not to output details. Defaults to true.
29
+ attr_accessor :verbose
30
+
31
+ # The threshold value (in percent) for coverage. If the
32
+ # actual coverage is not equal to this value, the task will raise an
33
+ # exception.
34
+ attr_accessor :threshold
35
+
36
+ def initialize( name=:verify )
37
+ @name = name
38
+ @index_html = 'coverage/index.html'
39
+ @verbose = true
40
+ yield self if block_given?
41
+ raise "Threshold must be set" if @threshold.nil?
42
+ define
43
+ end
44
+
45
+ def define
46
+ desc "Verify that rcov coverage is at least #{threshold}%"
47
+
48
+ task @name do
49
+ total_coverage = nil
50
+ if match = File.read( index_html ).match( COVERAGE_PERCENTAGE_PATTERN )
51
+ total_coverage = Float( match[1] )
52
+ else
53
+ raise "Couldn't find the coverage percentage in #{index_html}"
54
+ end
55
+
56
+ puts "Coverage: #{total_coverage}% (threshold: #{threshold}%)" if verbose
57
+ if total_coverage < threshold
58
+ raise "Coverage must be at least #{threshold}% but was #{total_coverage}%"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # vim: set nosta noet ts=4 sw=4:
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
+ }
11
+
12
+ require 'spec'
13
+ require 'spec/lib/helpers'
14
+
15
+ require 'configurability'
16
+
17
+
18
+ #####################################################################
19
+ ### C O N T E X T S
20
+ #####################################################################
21
+
22
+ describe Configurability do
23
+ include Configurability::SpecHelpers
24
+
25
+ before( :all ) do
26
+ setup_logging( :fatal )
27
+ end
28
+
29
+ after( :each ) do
30
+ Configurability.configurable_objects.clear
31
+ end
32
+
33
+
34
+ it "adds a method for registering an including module's config key" do
35
+ klass = Class.new do
36
+ extend Configurability
37
+ config_key :testconfig
38
+ end
39
+
40
+ klass.config_key.should == :testconfig
41
+ end
42
+
43
+ it "fetches config sections via a method with the config key name if the config " +
44
+ "responds_to? it" do
45
+ klass = Class.new do
46
+ extend Configurability
47
+ config_key :testconfig
48
+ end
49
+
50
+ config = stub( "configuration object" )
51
+ config.should_receive( :respond_to? ).with( :testconfig ).and_return( true )
52
+ config.should_receive( :testconfig ).and_return( :a_config_section )
53
+
54
+ klass.should_receive( :configure ).with( :a_config_section )
55
+ Configurability.configure_objects( config )
56
+ end
57
+
58
+ it "extends including classes instead of appending features to them" do
59
+ klass = Class.new do
60
+ include Configurability
61
+ config_key :testconfig
62
+ end
63
+
64
+ config = stub( "configuration object" )
65
+ config.should_receive( :respond_to? ).with( :testconfig ).and_return( true )
66
+ config.should_receive( :testconfig ).and_return( :a_config_section )
67
+
68
+ klass.should_receive( :configure ).with( :a_config_section )
69
+ Configurability.configure_objects( config )
70
+ end
71
+
72
+ it "fetches config sections via the index operator if the config doesn't respond " +
73
+ "directly to the section name, but does to the index operator" do
74
+ klass = Class.new do
75
+ extend Configurability
76
+ config_key :testconfig
77
+ end
78
+
79
+ config = stub( "configuration object" )
80
+ config.should_receive( :respond_to? ).with( :testconfig ).and_return( false )
81
+ config.should_receive( :respond_to? ).with( :[] ).and_return( true )
82
+ config.should_receive( :[] ).with( :testconfig ).and_return( :a_config_section )
83
+
84
+ klass.should_receive( :configure ).with( :a_config_section )
85
+ Configurability.configure_objects( config )
86
+ end
87
+
88
+ it "passes nil to the configure method if the config doesn't respond to the section " +
89
+ "name or the index operator" do
90
+ klass = Class.new do
91
+ extend Configurability
92
+ config_key :testconfig
93
+ end
94
+
95
+ config = stub( "configuration object" )
96
+ config.should_receive( :respond_to? ).with( :testconfig ).and_return( false )
97
+ config.should_receive( :respond_to? ).with( :[] ).and_return( false )
98
+
99
+ klass.should_receive( :configure ).with( nil )
100
+
101
+ Configurability.configure_objects( config )
102
+ end
103
+
104
+ it "tries the config key as a String if calling it with the Symbol returns nil" do
105
+ klass = Class.new do
106
+ extend Configurability
107
+ config_key :testconfig
108
+ end
109
+
110
+ config = stub( "configuration object" )
111
+ config.should_receive( :respond_to? ).with( :testconfig ).and_return( false )
112
+ config.should_receive( :respond_to? ).with( :[] ).and_return( true )
113
+ config.should_receive( :[] ).with( :testconfig ).and_return( nil )
114
+ config.should_receive( :[] ).with( 'testconfig' ).and_return( :a_config_section )
115
+
116
+ klass.should_receive( :configure ).with( :a_config_section )
117
+
118
+ Configurability.configure_objects( config )
119
+ end
120
+
121
+ it "can be used to configure plain objects, too" do
122
+ object = Object.new
123
+ object.extend( Configurability )
124
+ object.config_key = :testobjconfig
125
+
126
+ config = stub( "configuration object" )
127
+ config.should_receive( :respond_to? ).with( :testobjconfig ).and_return( true )
128
+ config.should_receive( :testobjconfig ).and_return( :a_config_section )
129
+
130
+ object.should_receive( :configure ).with( :a_config_section )
131
+
132
+ Configurability.configure_objects( config )
133
+ end
134
+
135
+ it "uses the object's name for its config key if it has one and hasn't specified a key " +
136
+ "directly" do
137
+ object = Object.new
138
+ def object.name; "testobjconfig"; end
139
+ object.extend( Configurability )
140
+
141
+ config = stub( "configuration object" )
142
+ config.should_receive( :respond_to? ).with( :testobjconfig ).and_return( true )
143
+ config.should_receive( :testobjconfig ).and_return( :a_config_section )
144
+
145
+ object.should_receive( :configure ).with( :a_config_section )
146
+
147
+ Configurability.configure_objects( config )
148
+ end
149
+
150
+ it "normalizes the object's name before using it" do
151
+ object = Object.new
152
+ def object.name; "Test Obj-Config"; end
153
+ object.extend( Configurability )
154
+
155
+ config = stub( "configuration object" )
156
+ config.should_receive( :respond_to? ).with( :test_obj_config ).and_return( true )
157
+ config.should_receive( :test_obj_config ).and_return( :a_config_section )
158
+
159
+ object.should_receive( :configure ).with( :a_config_section )
160
+
161
+ Configurability.configure_objects( config )
162
+ end
163
+
164
+ it "uses the object's class's name for its config key if it doesn't have a name and " +
165
+ "hasn't specified a key directly" do
166
+ object = Object.new
167
+ object.extend( Configurability )
168
+
169
+ config = stub( "configuration object" )
170
+ config.should_receive( :respond_to? ).with( :object ).and_return( true )
171
+ config.should_receive( :object ).and_return( :a_config_section )
172
+
173
+ object.should_receive( :configure ).with( :a_config_section )
174
+
175
+ Configurability.configure_objects( config )
176
+ end
177
+
178
+ it "uses only the last part of a class's name if it is namespaced" do
179
+ module My
180
+ class DbObject
181
+ extend Configurability
182
+ end
183
+ end
184
+
185
+ config = stub( "configuration object" )
186
+ config.should_receive( :respond_to? ).with( :dbobject ).and_return( true )
187
+ config.should_receive( :dbobject ).and_return( :a_config_section )
188
+
189
+ My::DbObject.should_receive( :configure ).with( :a_config_section )
190
+
191
+ Configurability.configure_objects( config )
192
+ end
193
+
194
+ it "uses the 'anonymous' key if the object doesn't have a name, and its class is " +
195
+ "anonymous, and it hasn't specified a key directly" do
196
+ objectclass = Class.new
197
+ object = objectclass.new
198
+ object.extend( Configurability )
199
+
200
+ config = stub( "configuration object" )
201
+ config.should_receive( :respond_to? ).with( :anonymous ).and_return( true )
202
+ config.should_receive( :anonymous ).and_return( :a_config_section )
203
+
204
+ object.should_receive( :configure ).with( :a_config_section )
205
+
206
+ Configurability.configure_objects( config )
207
+ end
208
+
209
+ end
210
+
211
+ # vim: set nosta noet ts=4 sw=4: