C0 code coverage information
Generated on Wed Oct 07 08:33:56 -0700 2009 with rcov 0.8.2.1
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.
1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements. See the NOTICE file distributed with this
3 # work for additional information regarding copyright ownership. The ASF
4 # licenses this file to you under the Apache License, Version 2.0 (the
5 # "License"); you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations under
14 # the License.
15
16
17 require 'buildr/core/project'
18 autoload :Spec, 'spec'
19
20
21 module Buildr
22 # Methods added to Project to allow checking the build.
23 module Checks
24
25 module Matchers #:nodoc:
26
27 class << self
28
29 # Define matchers that operate by calling a method on the tested object.
30 # For example:
31 # foo.should contain(bar)
32 # calls:
33 # foo.contain(bar)
34 def match_using(*names)
35 names.each do |name|
36 matcher = Class.new do
37 # Initialize with expected arguments (i.e. contain(bar) initializes with bar).
38 define_method(:initialize) { |*args| @expects = args }
39 # Matches against actual value (i.e. foo.should exist called with foo).
40 define_method(:matches?) do |actual|
41 @actual = actual
42 return actual.send("#{name}?", *@expects) if actual.respond_to?("#{name}?")
43 return actual.send(name, *@expects) if actual.respond_to?(name)
44 raise "You can't check #{actual}, it doesn't respond to #{name}."
45 end
46 # Some matchers have arguments, others don't, treat appropriately.
47 define_method :failure_message do
48 args = " " + @expects.map{ |arg| "'#{arg}'" }.join(", ") unless @expects.empty?
49 "Expected #{@actual} to #{name}#{args}"
50 end
51 define_method :negative_failure_message do
52 args = " " + @expects.map{ |arg| "'#{arg}'" }.join(", ") unless @expects.empty?
53 "Expected #{@actual} to not #{name}#{args}"
54 end
55 end
56 # Define method to create matcher.
57 define_method(name) { |*args| matcher.new(*args) }
58 end
59 end
60
61 end
62
63 # Define delegate matchers for exist and contain methods.
64 match_using :exist, :contain
65
66 end
67
68
69 # An expectation has subject, description and block. The expectation is validated by running the block,
70 # and can access the subject from the method #it. The description is used for reporting.
71 #
72 # The expectation is run by calling #run_against. You can share expectations by running them against
73 # different projects (or any other context for that matter).
74 #
75 # If the subject is missing, it is set to the argument of #run_against, typically the project itself.
76 # If the description is missing, it is set from the project. If the block is missing, the default behavior
77 # prints "Pending" followed by the description. You can use this to write place holders and fill them later.
78 class Expectation
79
80 attr_reader :description, :subject, :block
81
82 # :call-seq:
83 # initialize(subject, description?) { .... }
84 # initialize(description?) { .... }
85 #
86 # First argument is subject (returned from it method), second argument is description. If you omit the
87 # description, it will be set from the subject. If you omit the subject, it will be set from the object
88 # passed to run_against.
89 def initialize(*args, &block)
90 @description = args.pop if String === args.last
91 @subject = args.shift
92 raise ArgumentError, "Expecting subject followed by description, and either one is optional. Not quite sure what to do with this list of arguments." unless args.empty?
93 @block = block || lambda { info "Pending: #{description}" }
94 end
95
96 # :call-seq:
97 # run_against(context)
98 #
99 # Runs this expectation against the context object. The context object is different from the subject,
100 # but used as the subject if no subject specified (i.e. returned from the it method).
101 #
102 # This method creates a new context object modeled after the context argument, but a separate object
103 # used strictly for running this expectation, and used only once. The context object will pass methods
104 # to the context argument, so you can call any method, e.g. package(:jar).
105 #
106 # It also adds all matchers defined in Buildr and RSpec, and two additional methods:
107 # * it() -- Returns the subject.
108 # * description() -- Returns the description.
109 def run_against(context)
110 subject = @subject || context
111 description = @description ? "#{subject} #{@description}" : subject.to_s
112 # Define anonymous class and load it with:
113 # - All instance methods defined in context, so we can pass method calls to the context.
114 # - it() method to return subject, description() method to return description.
115 # - All matchers defined by Buildr and RSpec.
116 klass = Class.new
117 klass.instance_eval do
118 context.class.instance_methods.each do |method|
119 define_method(method) { |*args| context.send(method, *args) } unless instance_methods.include?(method)
120 end
121 define_method(:it) { subject }
122 define_method(:description) { description }
123 include Spec::Matchers
124 include Matchers
125 end
126
127 # Run the expectation. We only print the expectation name when tracing (to know they all ran),
128 # or when we get a failure.
129 begin
130 trace description
131 klass.new.instance_eval &@block
132 rescue Exception=>error
133 raise error.exception("#{description}\n#{error}").tap { |wrapped| wrapped.set_backtrace(error.backtrace) }
134 end
135 end
136
137 end
138
139
140 include Extension
141
142 before_define do |project|
143 # The check task can do any sort of interesting things, but the most important is running expectations.
144 project.task("check") do |task|
145 project.expectations.inject(true) do |passed, expect|
146 begin
147 expect.run_against project
148 passed
149 rescue Exception=>ex
150 if verbose
151 error ex.backtrace.select { |line| line =~ /#{Buildr.application.buildfile}/ }.join("\n")
152 error ex
153 end
154 false
155 end
156 end or fail "Checks failed for project #{project.name} (see errors above)."
157 end
158 project.task("package").enhance do |task|
159 # Run all actions before checks.
160 task.enhance { project.task("check").invoke }
161 end
162 end
163
164
165 # :call-seq:
166 # check(description) { ... }
167 # check(subject, description) { ... }
168 #
169 # Adds an expectation. The expectation is run against the project by the check task, executed after packaging.
170 # You can access any package created by the project.
171 #
172 # An expectation is written using a subject, description and block to validate the expectation. For example:
173 #
174 # For example:
175 # check package(:jar), "should exist" do
176 # it.should exist
177 # end
178 # check package(:jar), "should contain a manifest" do
179 # it.should contain("META-INF/MANIFEST.MF")
180 # end
181 # check package(:jar).path("com/acme"), "should contain classes" do
182 # it.should_not be_empty
183 # end
184 # check package(:jar).entry("META-INF/MANIFEST"), "should be a recent license" do
185 # it.should contain(/Copyright (C) 2007/)
186 # end
187 #
188 # If you omit the subject, the project is used as the subject. If you omit the description, the subject is
189 # used as description.
190 #
191 # During development you can write placeholder expectations by omitting the block. This will simply report
192 # the expectation as pending.
193 def check(*args, &block)
194 expectations << Checks::Expectation.new(*args, &block)
195 end
196
197 # :call-seq:
198 # expectations() => Expectation*
199 #
200 # Returns a list of expectations (see #check).
201 def expectations()
202 @expectations ||= []
203 end
204
205 end
206
207 end
208
209
210 module Rake #:nodoc:
211 class FileTask
212
213 # :call-seq:
214 # exist?() => boolean
215 #
216 # Returns true if this file exists.
217 def exist?()
218 File.exist?(name)
219 end
220
221 # :call-seq:
222 # empty?() => boolean
223 #
224 # Returns true if file/directory is empty.
225 def empty?()
226 File.directory?(name) ? Dir.glob("#{name}/*").empty? : File.read(name).empty?
227 end
228
229 # :call-seq:
230 # contain?(pattern*) => boolean
231 # contain?(file*) => boolean
232 #
233 # For a file, returns true if the file content matches against all the arguments. An argument may be
234 # a string or regular expression.
235 #
236 # For a directory, return true if the directory contains the specified files. You can use relative
237 # file names and glob patterns (using *, **, etc).
238 def contain?(*patterns)
239 if File.directory?(name)
240 patterns.map { |pattern| "#{name}/#{pattern}" }.all? { |pattern| !Dir[pattern].empty? }
241 else
242 contents = File.read(name)
243 patterns.map { |pattern| Regexp === pattern ? pattern : Regexp.new(Regexp.escape(pattern.to_s)) }.
244 all? { |pattern| contents =~ pattern }
245 end
246 end
247
248 end
249 end
250
251
252 class Buildr::Project
253 include Buildr::Checks
254 end
Generated using the rcov code coverage analysis tool for Ruby
version 0.8.2.1.