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