C0 code coverage information

Generated on Fri Jul 11 15:55:29 -0700 2008 with rcov 0.7.0


Code reported as executed by Ruby looks like this...
and this: this line is also marked as covered.
Lines considered as run by rcov, but not reported by Ruby, look like this,
and this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not executed.
Name Total lines Lines of code Total coverage Code coverage
lib/openid/store/filesystem.rb 271 215
85.6% 
83.7% 
  1 require 'fileutils'
  2 require 'pathname'
  3 require 'tempfile'
  4 
  5 require 'openid/util'
  6 require 'openid/store/interface'
  7 require 'openid/association'
  8 
  9 module OpenID
 10   module Store
 11     class Filesystem < Interface
 12       @@FILENAME_ALLOWED = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-".split("")
 13 
 14       # Create a Filesystem store instance, putting all data in +directory+.
 15       def initialize(directory)
 16         p_dir = Pathname.new(directory)
 17         @nonce_dir = p_dir.join('nonces')
 18         @association_dir = p_dir.join('associations')
 19         @temp_dir = p_dir.join('temp')
 20 
 21         self.ensure_dir(@nonce_dir)
 22         self.ensure_dir(@association_dir)
 23         self.ensure_dir(@temp_dir)
 24       end
 25 
 26       # Create a unique filename for a given server url and handle. The
 27       # filename that is returned will contain the domain name from the
 28       # server URL for ease of human inspection of the data dir.
 29       def get_association_filename(server_url, handle)
 30         unless server_url.index('://')
 31           raise ArgumentError, "Bad server URL: #{server_url}"
 32         end
 33 
 34         proto, rest = server_url.split('://', 2)
 35         domain = filename_escape(rest.split('/',2)[0])
 36         url_hash = safe64(server_url)
 37         if handle
 38           handle_hash = safe64(handle)
 39         else
 40           handle_hash = ''
 41         end
 42         filename = [proto,domain,url_hash,handle_hash].join('-')
 43         @association_dir.join(filename)
 44       end
 45 
 46       # Store an association in the assoc directory
 47       def store_association(server_url, association)
 48         assoc_s = association.serialize
 49         filename = get_association_filename(server_url, association.handle)
 50         f, tmp = mktemp
 51 
 52         begin
 53           begin
 54             f.write(assoc_s)
 55             f.fsync
 56           ensure
 57             f.close
 58           end
 59 
 60           begin
 61             File.rename(tmp, filename)
 62           rescue Errno::EEXIST
 63 
 64             begin
 65               File.unlink(filename)
 66             rescue Errno::ENOENT
 67               # do nothing
 68             end
 69 
 70             File.rename(tmp, filename)
 71           end
 72 
 73         rescue
 74           self.remove_if_present(tmp)
 75           raise
 76         end
 77       end
 78 
 79       # Retrieve an association
 80       def get_association(server_url, handle=nil)
 81         # the filename with empty handle is the prefix for the associations
 82         # for a given server url
 83         filename = get_association_filename(server_url, handle)
 84         if handle
 85           return _get_association(filename)
 86         end
 87         assoc_filenames = Dir.glob(filename.to_s + '*')
 88 
 89         assocs = assoc_filenames.collect do |f|
 90           _get_association(f)
 91         end
 92 
 93         assocs = assocs.find_all { |a| not a.nil? }
 94         assocs = assocs.sort_by { |a| a.issued }
 95 
 96         return nil if assocs.empty?
 97         return assocs[-1]
 98       end
 99 
100       def _get_association(filename)
101         begin
102           assoc_file = File.open(filename, "r")
103         rescue Errno::ENOENT
104           return nil
105         else
106           begin
107             assoc_s = assoc_file.read
108           ensure
109             assoc_file.close
110           end
111 
112           begin
113             association = Association.deserialize(assoc_s)
114           rescue
115             self.remove_if_present(filename)
116             return nil
117           end
118 
119           # clean up expired associations
120           if association.expires_in == 0
121             self.remove_if_present(filename)
122             return nil
123           else
124             return association
125           end
126         end
127       end
128 
129       # Remove an association if it exists, otherwise do nothing.
130       def remove_association(server_url, handle)
131         assoc = get_association(server_url, handle)
132 
133         if assoc.nil?
134           return false
135         else
136           filename = get_association_filename(server_url, handle)
137           return self.remove_if_present(filename)
138         end
139       end
140 
141       # Return whether the nonce is valid
142       def use_nonce(server_url, timestamp, salt)
143         return false if (timestamp - Time.now.to_i).abs > Nonce.skew
144 
145         if server_url and !server_url.empty?
146           proto, rest = server_url.split('://',2)
147         else
148           proto, rest = '',''
149         end
150         raise "Bad server URL" unless proto && rest
151 
152         domain = filename_escape(rest.split('/',2)[0])
153         url_hash = safe64(server_url)
154         salt_hash = safe64(salt)
155 
156         nonce_fn = '%08x-%s-%s-%s-%s'%[timestamp, proto, domain, url_hash, salt_hash]
157 
158         filename = @nonce_dir.join(nonce_fn)
159 
160         begin
161           fd = File.new(filename, File::CREAT | File::EXCL | File::WRONLY, 0200)
162           fd.close
163           return true
164         rescue Errno::EEXIST
165           return false
166         end
167       end
168 
169       # Remove expired entries from the database. This is potentially expensive,
170       # so only run when it is acceptable to take time.
171       def cleanup
172         cleanup_associations
173         cleanup_nonces
174       end
175 
176       def cleanup_associations
177         association_filenames = Dir[@association_dir.join("*").to_s]
178         count = 0
179         association_filenames.each do |af|
180           begin
181             f = File.open(af, 'r')
182           rescue Errno::ENOENT
183             next
184           else
185             begin
186               assoc_s = f.read
187             ensure
188               f.close
189             end
190             begin
191               association = OpenID::Association.deserialize(assoc_s)
192             rescue StandardError
193               self.remove_if_present(af)
194               next
195             else
196               if association.expires_in == 0
197                 self.remove_if_present(af)
198                 count += 1
199               end
200             end
201           end
202         end
203         return count
204       end
205 
206       def cleanup_nonces
207         nonces = Dir[@nonce_dir.join("*").to_s]
208         now = Time.now.to_i
209 
210         count = 0
211         nonces.each do |filename|
212           nonce = filename.split('/')[-1]
213           timestamp = nonce.split('-', 2)[0].to_i(16)
214           nonce_age = (timestamp - now).abs
215           if nonce_age > Nonce.skew
216             self.remove_if_present(filename)
217             count += 1
218           end
219         end
220         return count
221       end
222 
223       protected
224 
225       # Create a temporary file and return the File object and filename.
226       def mktemp
227         f = Tempfile.new('tmp', @temp_dir)
228         [f, f.path]
229       end
230 
231       # create a safe filename from a url
232       def filename_escape(s)
233         s = '' if s.nil?
234         filename_chunks = []
235         s.split('').each do |c|
236           if @@FILENAME_ALLOWED.index(c)
237             filename_chunks << c
238           else
239             filename_chunks << sprintf("_%02X", c[0])
240           end
241         end
242         filename_chunks.join("")
243       end
244 
245       def safe64(s)
246         s = OpenID::CryptUtil.sha1(s)
247         s = OpenID::Util.to_base64(s)
248         s.gsub!('+', '_')
249         s.gsub!('/', '.')
250         s.gsub!('=', '')
251         return s
252       end
253 
254       # remove file if present in filesystem
255       def remove_if_present(filename)
256         begin
257           File.unlink(filename)
258         rescue Errno::ENOENT
259           return false
260         end
261         return true
262       end
263 
264       # ensure that a path exists
265       def ensure_dir(dir_name)
266         FileUtils::mkdir_p(dir_name)
267       end
268     end
269   end
270 end
271 

Generated using the rcov code coverage analysis tool for Ruby version 0.7.0.

Valid XHTML 1.0! Valid CSS!