blob: 2979ee6cf5d37163dd20f1956496b7810be4d469 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with this
# work for additional information regarding copyright ownership. The ASF
# licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
# This file gets loaded twice when running 'spec spec/*' and not with pleasant results,
# so ignore the second attempt to load it.
unless defined?(SpecHelpers)
require 'rubygems'
# For testing we use the gem requirements specified on the buildr.gemspec
spec = Gem::Specification.load(File.expand_path('../buildr.gemspec', File.dirname(__FILE__)))
# Dependency.version_requirements deprecated in rubygems 1.3.6
spec.dependencies.
select {|dep| dep.type == :runtime || (dep.type == :development && dep.name.to_s =~ /^rspec.*/) }.
each { |dep| gem dep.name, (dep.respond_to?(:requirement) ? dep.requirement.to_s : dep.version_requirements.to_s) }
# Make sure to load from these paths first, we don't want to load any
# code from Gem library.
$LOAD_PATH.unshift File.expand_path('../lib', File.dirname(__FILE__)),
File.expand_path('../addon', File.dirname(__FILE__))
# Buildr uses autoload extensively, but autoload when running specs creates
# a problem -- we sandbox $LOADED_FEATURES, so we end up autoloading the same
# module twice. This turns autoload into a require, which is not the right
# thing, but will do for now.
def autoload(symbol, path)
require path
end
require 'buildr'
# load ecj
require 'buildr/java/ecj'
#Make ecj appear as a compiler that doesn't apply:
class Buildr::Compiler::Ecj
class << self
def applies_to?(project, task)
false
end
end
end
# Give a chance for plugins to do a few things before requiring the sandbox.
include SandboxHook if defined?(SandboxHook)
require File.expand_path('sandbox', File.dirname(__FILE__))
module SpecHelpers
include Checks::Matchers
[:info, :warn, :error, :puts].each do |severity|
::Object.class_eval do
define_method severity do |*args|
$messages ||= {}
$messages[severity] ||= []
$messages[severity].push(*args)
end
end
end
class << Buildr.application
alias :deprecated_without_capture :deprecated
def deprecated(message)
verbose(true) { deprecated_without_capture message }
end
end
class MessageWithSeverityMatcher
def initialize(severity, message)
@severity = severity
@expect = message
end
def matches?(target)
$messages = {@severity => []}
target.call
return Regexp === @expect ? $messages[@severity].join('\n') =~ @expect : $messages[@severity].include?(@expect.to_s)
end
def failure_message
"Expected #{@severity} #{@expect.inspect}, " +
($messages[@severity].empty? ? "no #{@severity} issued" : "found #{$messages[@severity].inspect}")
end
def negative_failure_message
"Found unexpected #{$messages[@severity].inspect}"
end
end
# Test if an info message was shown. You can use a string or regular expression.
#
# For example:
# lambda { info 'ze test' }.should show_info(/ze test/)
def show_info(message)
MessageWithSeverityMatcher.new :info, message
end
# Test if a warning was shown. You can use a string or regular expression.
#
# For example:
# lambda { warn 'ze test' }.should show_warning(/ze test/)
def show_warning(message)
MessageWithSeverityMatcher.new :warn, message
end
# Test if an error message was shown. You can use a string or regular expression.
#
# For example:
# lambda { error 'ze test' }.should show_error(/ze test/)
def show_error(message)
MessageWithSeverityMatcher.new :error, message
end
# Test if any message was shown (puts). You can use a string or regular expression.
#
# For example:
# lambda { puts 'ze test' }.should show(/ze test/)
def show(message)
MessageWithSeverityMatcher.new :puts, message
end
# Yields a block that should try exiting the application.
# Accepts
#
# For example:
# test_exit(1) { puts "Hello" ; exit(1) }.should show("Hello")
#
def test_exit(status = nil)
return lambda {
begin
yield
raise "Exit was not called!"
rescue SystemExit => e
raise "Exit status incorrect! Expected: #{status}, got #{e.status}" if status && (e.status != status)
end
}
end
class ::Rake::Task
alias :execute_without_a_record :execute
def execute(args)
$executed ||= []
$executed << name
execute_without_a_record args
end
end
class InvokeMatcher
def initialize(*tasks)
@expecting = tasks.map { |task| [task].flatten.map(&:to_s) }
end
def matches?(target)
$executed = []
target.call
return false unless all_ran?
return !@but_not.any_ran? if @but_not
return true
end
def failure_message
return @but_not.negative_failure_message if all_ran? && @but_not
"Expected the tasks #{expected} to run, but #{remaining} did not run, or not in the order we expected them to." +
" Tasks that ran: #{$executed.inspect}"
end
def negative_failure_message
if all_ran?
"Expected the tasks #{expected} to not run, but they all ran."
else
"Expected the tasks #{expected} to not run, and all but #{remaining} ran."
end
end
def but_not(*tasks)
@but_not = InvokeMatcher.new(*tasks)
self
end
protected
def expected
@expecting.map { |tests| tests.join('=>') }.join(', ')
end
def remaining
@remaining.map { |tests| tests.join('=>') }.join(', ')
end
def all_ran?
@remaining ||= $executed.inject(@expecting) do |expecting, executed|
expecting.map { |tasks| tasks.first == executed ? tasks[1..-1] : tasks }.reject(&:empty?)
end
@remaining.empty?
end
def any_ran?
all_ran?
@remaining.size < @expecting.size
end
end
# Tests that all the tasks ran, in the order specified. Can also be used to test that some
# tasks and not others ran.
#
# Takes a list of arguments. Each argument can be a task name, matching only if that task ran.
# Each argument can be an array of task names, matching only if all these tasks ran in that order.
# So run_tasks('foo', 'bar') expects foo and bar to run in any order, but run_task(['foo', 'bar'])
# expects foo to run before bar.
#
# You can call but_not on the matchers to specify that certain tasks must not execute.
#
# For example:
# # Either task
# lambda { task('compile').invoke }.should run_tasks('compile', 'resources')
# # In that order
# lambda { task('build').invoke }.should run_tasks(['compile', 'test'])
# # With exclusion
# lambda { task('build').invoke }.should run_tasks('compile').but_not('install')
def run_tasks(*tasks)
InvokeMatcher.new *tasks
end
# Tests that a task ran. Similar to run_tasks, but accepts a single task name.
#
# For example:
# lambda { task('build').invoke }.should run_task('test')
def run_task(task)
InvokeMatcher.new [task]
end
class UriPathMatcher
def initialize(re)
@expression = re
end
def matches?(uri)
@uri = uri
uri.path =~ @expression
end
def description
"URI with path matching #{@expression} vs #{@uri}"
end
def failure_message_for_should
"expected #{description}"
end
end
# Matches a parsed URI's path against the given regular expression
def uri(re)
UriPathMatcher.new(re)
end
class AbsolutePathMatcher
def initialize(path)
@expected = File.expand_path(path.to_s)
end
def matches?(path)
@provided = File.expand_path(path.to_s)
@provided == @expected
end
def failure_message
"Expected path #{@expected}, but found path #{@provided}"
end
def negative_failure_message
"Expected a path other than #{@expected}"
end
end
def point_to_path(path)
AbsolutePathMatcher.new(path)
end
# Value covered by range. For example:
# (1..5).should cover(3)
RSpec::Matchers.define :cover do |actual|
match do |range|
actual >= range.min && actual <= range.max
end
end
def suppress_stdout
stdout = $stdout
#$stdout = StringIO.new
begin
yield
ensure
$stdout = stdout
end
end
def dryrun
Buildr.application.options.dryrun = true
begin
suppress_stdout { yield }
ensure
Buildr.application.options.dryrun = false
end
end
# We run tests with tracing off. Then things break. And we need to figure out what went wrong.
# So just use trace() as you would use verbose() to find and squash the bug.
def trace(value = nil)
old_value = Buildr.application.options.trace
Buildr.application.options.trace = value unless value.nil?
if block_given?
begin
yield
ensure
Buildr.application.options.trace = old_value
end
end
Buildr.application.options.trace
end
# Change the Buildr original directory, faking invocation from a different directory.
def in_original_dir(dir)
begin
original_dir = Buildr.application.original_dir
Buildr.application.instance_eval { @original_dir = File.expand_path(dir) }
yield
ensure
Buildr.application.instance_eval { @original_dir = original_dir }
end
end
# Buildr's define method creates a project definition but does not evaluate it
# (that happens once the buildfile is loaded), and we include Buildr's define in
# the test context so we can use it without prefixing with Buildr. This just patches
# define to evaluate the project definition before returning it.
def define(name, properties = nil, &block) #:yields:project
Project.define(name, properties, &block).tap { |project| project.invoke }
end
end
# Allow using matchers within the project definition.
class Buildr::Project
include ::RSpec::Matchers, SpecHelpers
end
::RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
# Make all Buildr methods accessible from test cases, and add various helper methods.
config.include Buildr
config.include SpecHelpers
# Sandbox Buildr for each test.
config.include Sandbox
end
end