PageRenderTime 40ms CodeModel.GetById 13ms app.highlight 25ms RepoModel.GetById 0ms app.codeStats 0ms

/spec/thingfish/mixins_spec.rb

https://bitbucket.org/laika/thingfish
Ruby | 360 lines | 255 code | 101 blank | 4 comment | 46 complexity | 7c2b5e8cb619fbd5488a502b4520b7d9 MD5 | raw file
Possible License(s): BSD-3-Clause
  1#!/usr/bin/env ruby
  2
  3BEGIN {
  4	require 'pathname'
  5	basedir = Pathname.new( __FILE__ ).dirname.parent.parent
  6
  7	libdir = basedir + "lib"
  8
  9	$LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
 10	$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
 11}
 12
 13require 'rspec'
 14require 'stringio'
 15
 16require 'spec/lib/helpers'
 17
 18require 'thingfish/mixins'
 19require 'thingfish/handler'
 20require 'thingfish/testconstants'
 21
 22
 23#####################################################################
 24###	C O N T E X T S
 25#####################################################################
 26
 27describe ThingFish::Loggable do
 28
 29	context "including class" do
 30		before(:each) do
 31			@logfile = StringIO.new('')
 32			ThingFish.logger = Logger.new( @logfile )
 33
 34			@test_class = Class.new do
 35				include ThingFish::Loggable
 36
 37				def log_test_message( level, msg )
 38					self.log.send( level, msg )
 39				end
 40
 41				def logdebug_test_message( msg )
 42					self.log_debug.debug( msg )
 43				end
 44			end
 45			@obj = @test_class.new
 46		end
 47
 48
 49		it "is able to output to the log via its #log method" do
 50			@obj.log_test_message( :debug, "debugging message" )
 51			@logfile.rewind
 52			@logfile.read.should =~ /debugging message/
 53		end
 54
 55		it "is able to output to the log via its #log_debug method" do
 56			@obj.logdebug_test_message( "sexydrownwatch" )
 57			@logfile.rewind
 58			@logfile.read.should =~ /sexydrownwatch/
 59		end
 60	end
 61
 62
 63	context "including class with custom formats" do
 64		before(:each) do
 65			@logfile = StringIO.new('')
 66			@logger = ThingFish.logger = Logger.new( @logfile )
 67
 68			formatter = ThingFish::LogFormatter.new( @logger, '%7$s', 'D: %7$s' )
 69			@logger.formatter = formatter
 70
 71			@test_class = Class.new do
 72				include ThingFish::Loggable
 73
 74				def log_test_message( level, msg )
 75					self.log.send( level, msg )
 76				end
 77
 78				def logdebug_test_message( msg )
 79					self.log_debug.debug( msg )
 80				end
 81			end
 82			@obj = @test_class.new
 83		end
 84
 85
 86		it "is able to output to the log via its #log method" do
 87			@logger.should_receive( :level ).and_return( Logger::INFO )
 88			@obj.log_test_message( :info, 'scoby pancakes' )
 89			@logfile.rewind
 90			@logfile.read.should == 'scoby pancakes'
 91		end
 92
 93		it "is able to output to the log via its #log_debug method" do
 94			@obj.logdebug_test_message( 'poop tubes' )
 95			@logfile.rewind
 96			@logfile.read.should == 'D: poop tubes'
 97		end
 98	end
 99
100end
101
102describe ThingFish::StaticResourcesHandler do
103
104	context "mixed into a class" do
105		before(:each) do
106			@test_class = Class.new( ThingFish::Handler ) do
107				include ThingFish::StaticResourcesHandler
108			end
109		end
110
111
112		it "has a 'static_resources_dir' class method" do
113			@test_class.should respond_to( :static_resources_dir)
114		end
115
116		it "has a 'static_resources_dir' of 'static' by default" do
117			@test_class.static_resources_dir.should == 'static'
118		end
119	end
120
121	context "mixed into a handler class that has set the static resources dir" do
122		before(:each) do
123			@test_class = Class.new( ThingFish::Handler ) do
124				include ThingFish::StaticResourcesHandler
125				static_resources_dir "static-content"
126			end
127		end
128
129
130		it "should have the specified static_resources_dir" do
131			@test_class.static_resources_dir.should == 'static-content'
132		end
133	end
134
135	context "mixed into an instance of a handler class" do
136
137		before(:each) do
138			@test_class = Class.new( ThingFish::Handler ) do
139				include ThingFish::StaticResourcesHandler
140				static_resources_dir "static-content"
141			end
142			@test_handler = @test_class.new( '/glah' )
143		end
144
145
146		it "registers another handler at its own location when registered" do
147			daemon = mock( "daemon" ).as_null_object
148			urimap = mock( "urimap" )
149			daemon.stub!( :urimap ).and_return( urimap )
150			urimap.should_receive( :register_first ).with( '/glah', duck_type(:on_startup, :process) )
151
152			@test_handler.on_startup( daemon )
153		end
154	end
155
156end
157
158describe ThingFish::ResourceLoader do
159
160	it "adds a #get_resource method to including classes" do
161		klass = Class.new { include ThingFish::ResourceLoader }
162		obj = klass.new
163		obj.should respond_to( :get_resource )
164	end
165
166
167	context "mixed into a class" do
168
169		before( :all ) do
170			ThingFish.reset_logger
171			ThingFish.logger.level = Logger::FATAL
172
173			@resdir = make_tempdir()
174			@resdir.mkpath
175
176			@tmpfile = Tempfile.new( 'test.txt', @resdir )
177			@tmpfile.print( TEST_RESOURCE_CONTENT )
178			@tmpfile.close
179			@tmpname = Pathname.new( @tmpfile.path ).basename
180
181			@klass = Class.new {
182				include ThingFish::ResourceLoader
183				public :get_resource, :resource_exists?, :resource_directory?
184				def initialize( resdir )
185					@resource_dir = resdir
186				end
187			}
188		end
189
190		after( :all ) do
191			@resdir.rmtree
192			ThingFish.reset_logger
193		end
194
195		before( :each ) do
196			@obj = @klass.new( @resdir )
197		end
198
199		it "should know what its resource directory is" do
200			@obj.resource_dir.should == @resdir
201		end
202
203		it "is able to load stuff from its resources dir" do
204		    @obj.get_resource( @tmpname ).should == TEST_RESOURCE_CONTENT
205		end
206
207		it "can test for the existance of a resource" do
208			@obj.resource_exists?( @tmpname ).should be_true()
209		end
210
211		it "can test for the existance of a resource directory" do
212			@obj.resource_directory?( @tmpname ).should be_false()
213			dir = (@resdir + 'testdirectory')
214			@obj.resource_directory?( dir.basename ).should be_false()
215			dir.mkpath
216			@obj.resource_directory?( dir.basename ).should be_true()
217		end
218
219	end
220
221end
222
223
224describe ThingFish::AbstractClass do
225
226	context "mixed into a class" do
227		it "will cause the including class to hide its ::new method" do
228			testclass = Class.new { include ThingFish::AbstractClass }
229
230			expect {
231				testclass.new
232			}.to raise_error( NoMethodError, /private/ )
233		end
234
235	end
236
237
238	context "mixed into a superclass" do
239
240		before(:each) do
241			testclass = Class.new {
242				include ThingFish::AbstractClass
243				virtual :test_method
244			}
245			subclass = Class.new( testclass )
246			@instance = subclass.new
247		end
248
249
250		it "raises a NotImplementedError when unimplemented API methods are called" do
251			expect {
252				@instance.test_method
253			}.to raise_error( NotImplementedError, /does not provide an implementation of/ )
254		end
255
256		it "declares the virtual methods so that they can be used with arguments under Ruby 1.9" do
257			expect {
258				@instance.test_method( :some, :arguments )
259			}.to raise_error( NotImplementedError, /does not provide an implementation of/ )
260		end
261
262	end
263
264end
265
266
267describe ThingFish::NumericConstantMethods do
268
269	context "mixed into Numerics" do
270
271		SECONDS_IN_A_MINUTE    = 60
272		SECONDS_IN_AN_HOUR     = SECONDS_IN_A_MINUTE * 60
273		SECONDS_IN_A_DAY       = SECONDS_IN_AN_HOUR * 24
274		SECONDS_IN_A_WEEK      = SECONDS_IN_A_DAY * 7
275		SECONDS_IN_A_FORTNIGHT = SECONDS_IN_A_WEEK * 2
276		SECONDS_IN_A_MONTH     = SECONDS_IN_A_DAY * 30
277		SECONDS_IN_A_YEAR      = Integer( SECONDS_IN_A_DAY * 365.25 )
278
279		it "can calculate the number of seconds for various units of time" do
280			1.second.should == 1
281			14.seconds.should == 14
282
283			1.minute.should == SECONDS_IN_A_MINUTE
284			18.minutes.should == SECONDS_IN_A_MINUTE * 18
285
286			1.hour.should == SECONDS_IN_AN_HOUR
287			723.hours.should == SECONDS_IN_AN_HOUR * 723
288
289			1.day.should == SECONDS_IN_A_DAY
290			3.days.should == SECONDS_IN_A_DAY * 3
291
292			1.week.should == SECONDS_IN_A_WEEK
293			28.weeks.should == SECONDS_IN_A_WEEK * 28
294
295			1.fortnight.should == SECONDS_IN_A_FORTNIGHT
296			31.fortnights.should == SECONDS_IN_A_FORTNIGHT * 31
297
298			1.month.should == SECONDS_IN_A_MONTH
299			67.months.should == SECONDS_IN_A_MONTH * 67
300
301			1.year.should == SECONDS_IN_A_YEAR
302			13.years.should == SECONDS_IN_A_YEAR * 13
303		end
304
305
306		it "can calulate various time offsets" do
307			starttime = Time.now
308
309			1.second.after( starttime ).should == starttime + 1
310			18.seconds.from_now.should be_within( 10.seconds ).of( starttime + 18 )
311
312			1.second.before( starttime ).should == starttime - 1
313			3.hours.ago.should be_within( 10.seconds ).of( starttime - 10800 )
314		end
315
316
317
318		BYTES_IN_A_KILOBYTE = 1024
319		BYTES_IN_A_MEGABYTE = BYTES_IN_A_KILOBYTE * 1024
320		BYTES_IN_A_GIGABYTE = BYTES_IN_A_MEGABYTE * 1024
321		BYTES_IN_A_TERABYTE = BYTES_IN_A_GIGABYTE * 1024
322		BYTES_IN_A_PETABYTE = BYTES_IN_A_TERABYTE * 1024
323		BYTES_IN_AN_EXABYTE = BYTES_IN_A_PETABYTE * 1024
324
325		it "can calulate the number of bytes for various data sizes" do
326			1.byte.should == 1
327			4.bytes.should == 4
328
329			1.kilobyte.should == BYTES_IN_A_KILOBYTE
330			22.kilobytes.should == BYTES_IN_A_KILOBYTE * 22
331
332			1.megabyte.should == BYTES_IN_A_MEGABYTE
333			116.megabytes.should == BYTES_IN_A_MEGABYTE * 116
334
335			1.gigabyte.should == BYTES_IN_A_GIGABYTE
336			14.gigabytes.should == BYTES_IN_A_GIGABYTE * 14
337
338			1.terabyte.should == BYTES_IN_A_TERABYTE
339			88.terabytes.should == BYTES_IN_A_TERABYTE * 88
340
341			1.petabyte.should == BYTES_IN_A_PETABYTE
342			34.petabytes.should == BYTES_IN_A_PETABYTE * 34
343
344			1.exabyte.should == BYTES_IN_AN_EXABYTE
345			6.exabytes.should == BYTES_IN_AN_EXABYTE * 6
346		end
347
348
349		it "can display integers as human readable filesize values" do
350			234.size_suffix.should == "234b"
351			3492.size_suffix.should == "3.4K"
352			3492425.size_suffix.should == "3.3M"
353			9833492425.size_suffix.should == "9.2G"
354		end
355
356	end
357
358end
359
360