lib/java/test.rb in buildr-1.1.3 vs lib/java/test.rb in buildr-1.2.0
- old
+ new
@@ -1,36 +1,24 @@
require "core/build"
require "java/compile"
+require "java/ant"
+require "core/help"
+
module Buildr
module Java
- # The JUnit task executes JUnit test cases.
- #
- # The task requires one or more paths that contain the test cases (see #from),
- # in addition to any classpath dependencies (see #with). From the test case
- # directories it picks all classes that match the inclusion pattern and none
- # that match the exclusion pattern and executes these in order. See #include
- # for more information.
- class JUnitTask < Rake::Task
+ # *Deprecated:* Use the test task directly instead of calling test.junit.
+ class JUnitTask < Rake::Task #:nodoc:
+ # The classpath used for running the tests. Includes the compile classpath,
+ # compiled classes (target). For everything else, add by calling #with.
attr_accessor :classpath
def initialize(*args) #:nodoc:
super
- @classpath = []
- @paths = []
- @include = ["*Test", "*Suite"]
- @exclude = []
- @options = {}
- enhance do |task|
- unless test_cases.empty?
- puts "Running tests in #{name}" if verbose
- passed, failed = Java.junit(test_cases, @options.merge(:classpath=>classpath + @paths))
- fail "The following tests failed:\n#{failed.join("\n")}" unless failed.empty?
- end
- end
+ @parent = Rake::Task["#{name.split(":")[0...-1].join(":")}"]
end
# :call-seq:
# include(*classes) => self
#
@@ -46,110 +34,153 @@
#
# By default, all classes that have a name ending with Test or Suite are included.
# Use these suffixes for your test and test suite classes respectively, to distinguish them
# from stubs, helper classes, etc.
def include(*classes)
- @include += classes
+ @parent.include *classes
self
end
# :call-seq:
# exclude(*classes) => self
#
# Exclude the specified test cases. This method accepts multiple arguments and returns self.
# See #include for the type of arguments you can use.
def exclude(*classes)
- @exclude += classes
+ @parent.exclude *classes
self
end
# :call-seq:
# from(*paths) => self
#
# Specify one or more directories that include test cases.
def from(*files)
- @paths += files
self
end
# :call-seq:
# with(*specs) => self
#
# Specify artifacts (specs, tasks, files, etc) to include in the classpath when running
# the test cases.
def with(*files)
- @classpath |= Buildr.artifacts(files.flatten).uniq
+ (@parent.options[:classpath] ||= []).concat files.flatten
self
end
# Returns the JUnit options.
- attr_reader :options
+ def options()
+ @parent.options
+ end
# :call-seq:
# using(options) => self
#
- # Sets the JUnit options from a hash and returns self. Right now supports passing properties to JUnit.
+ # Sets the JUnit options from a hash and returns self. Right now supports passing :properties to JUnit,
+ # and :java_args to the JVM.
#
# For example:
# test.junit.using :properties=>{ "root"=>base_dir }
def using(options)
- options.each { |k,v| @options[k.to_sym] = v }
+ @parent.using options
self
end
- private
-
- def test_finding_pattern()
- "*{Test,TestCase,Suite,TestSuite}"
- end
-
- def test_cases()
- unless @cases
- @cases = @paths.map do |path|
- base = Pathname.new(path.to_s)
- FileList["#{path}/**/#{test_finding_pattern}.class"].
- map { |file| Pathname.new(file).relative_path_from(base).to_s.ext("").gsub(File::SEPARATOR, ".") }.
- select { |name| @include.any? { |pattern| File.fnmatch(pattern, name) } }.
- reject { |name| @exclude.any? { |pattern| File.fnmatch(pattern, name) } }
- end.flatten.sort
- end
- @cases
- end
-
end
# The test task controls the entire test lifecycle.
#
- # You can use the test task in three ways. You can access and configure specific
- # test tasks, e.g. enhance the #compile task, or run code during #setup/#teardown.
+ # You can use the test task in three ways. You can access and configure specific test tasks,
+ # e.g. enhance the #compile task, or run code during #setup/#teardown.
#
- # You can use convenient methods that handle the most common settings. For example,
- # add classpath dependencies using #with, or include only specific test cases
- # using #include.
+ # You can use convenient methods that handle the most common settings. For example, add classpath
+ # dependencies using #with, or include only specific test cases using #include.
#
- # You can also enhance this task directly. This task will first execute the #compile
- # task, followed by the #setup task and #junit task, then any of your enhancements,
- # and end by executing #teardown.
+ # You can also enhance this task directly. This task will first execute the #compile task, followed
+ # by the #setup task, run the unit tests, any other enhancements, and end by executing #teardown.
+ #
+ # Unit tests are fun from classed compiled by the test.compile class that match the TEST_FILE_PATTERN
+ # (i.e. MyClassTest, MyClassTestSuite, etc). The test framework is determined by setting one of the
+ # test framework options to true, for example:
+ # test.unsing :testng
class TestTask < Rake::Task
+ class << self
+
+ # Used by the local test and integration tasks to
+ # a) Find the local project(s),
+ # b) Find all its sub-projects and narrow down to those that have either unit or integration tests,
+ # c) Run all the (either unit or integration) tests, and
+ # d) Ignore failure if necessary.
+ def run_local_tests(integration) #:nodoc:
+ Project.local_projects do |project|
+ # !(foo ^ bar) tests for equality and accepts nil as false (and select is less obfuscated than reject on ^).
+ projects = ([project] + project.projects).select { |project| !(project.test.options[:integration] ^ integration) }
+ projects.each do |project|
+ puts "Testing #{project.name}" if verbose
+ begin
+ project.test.invoke
+ rescue
+ raise unless Buildr.options.test == :all
+ end
+ end
+ end
+ end
+
+ # Used by the test/integration rule to only run tests that match the specified names.
+ def only_run(tests) #:nodoc:
+ tests = tests.map { |name| name =~ /\*/ ? name : "*#{name}*" }
+ # Since the test case may reside in a sub-project, we need to set the include/exclude pattern on
+ # all sub-projects, but only invoke test on the local project.
+ Project.projects.each { |project| project.test.instance_eval { @include = tests ; @exclude.clear } }
+ end
+ end
+
+ # List of supported test framework, first one being a default. Test frameworks are added by
+ # including them in TestTask (e.g. JUnit, TestNG).
+ TEST_FRAMEWORKS = []
+
+ # Default options already set on each test task.
+ DEFAULT_OPTIONS = { :fail_on_failure=>true }
+
+ # JMock version..
+ JMOCK_VERSION = "1.2.0"
+ # JMock specification.
+ JMOCK_REQUIRES = "jmock:jmock:jar:#{JMOCK_VERSION}"
+
+ # The classpath used for running the tests. Includes the compiled classes (compile.target) and
+ # their classpath dependencies. Will also include anything you pass to #with, shared between the
+ # testing compile and run classpath dependencies.
+ attr_reader :classpath
+
def initialize(*args) #:nodoc:
super
- enhance do
- compile.invoke
- setup.invoke
- junit.invoke
- enhance { teardown.invoke }
+ @classpath = []
+ @include = []
+ @exclude = []
+ parent = Rake::Task["^test"] if name[":"] # Only if in namespace
+ @options = parent && parent.respond_to?(:options) ? parent.options.clone : DEFAULT_OPTIONS.clone
+ enhance { run_tests }
+ end
+
+ def execute() #:nodoc:
+ setup.invoke
+ begin
+ super
+ @project.task("test:junit").invoke # In case someone enhanced it
+ rescue RuntimeError
+ raise if options[:fail_on_failure]
+ ensure
+ teardown.invoke
end
end
- # :call-seq:
- # prepare(*prereqs) => task
- # prepare(*prereqs) { |task| .. } => task
- #
- # Executes before the #compile task to prepare any source files used during compilation.
+ # *Deprecated* Add a prerequisite to the compile task instead.
def prepare(*prereqs, &block)
+ warn_deprecated "Add a prerequisite to the compile task instead of using the prepare task."
@project.task("test:prepare").enhance prereqs, &block
end
# :call-seq:
# compile(*sources) => CompileTask
@@ -174,26 +205,13 @@
# Executes by the #compile task to copy resource files over. See Project#resources.
def resources(*prereqs, &block)
@project.task("test:resources").enhance prereqs, &block
end
- # :call-seq:
- # junit() => JUnitTask
- #
- # Returns the JUnit task. This task executes JUnit test cases, from classes compiled by
- # the test task.
- #
- # By default it includes all classes with the suffix Test or Suite, and excludes all other classes.
- # Use the Test/Suite suffix for classes that implement test cases, avoid this suffix for other
- # classes (e.g. stubs, helper objects).
- #
- # You can also include only specific test cases, or exclude otherwise included test cases
- # using #include and #exclude.
- #
- # The Java property baseDir is set to the classes directory, you can use it to pick up test resources
- # that you cannot access using getResources().
+ # *Deprecated* Use the test task directly instead of calling test.junit.
def junit()
+ warn_deprecated "Use the test task directly instead of calling test.junit."
@project.task("test:junit")
end
# :call-seq:
# setup(*prereqs) => task
@@ -209,45 +227,327 @@
# teardown(*prereqs) => task
# teardown(*prereqs) { |task| .. } => task
#
# Returns the teardown task. The teardown task is executed at the end of the test task.
def teardown(*prereqs, &block)
- @project.task("test.teardown").enhance prereqs, &block
+ @project.task("test:teardown").enhance prereqs, &block
end
# :call-seq:
# with(*specs) => self
#
# Specify artifacts (specs, tasks, files, etc) to include in the classpath when compiling
- # and running test cases. Unless you need to limit specific classpath dependencies, use
- # this instead of calling test.compile and test.junit individually.
+ # and running test cases.
def with(*artifacts)
+ @classpath |= Buildr.artifacts(artifacts.flatten).uniq
compile.with artifacts
- junit.with artifacts
self
end
+ # Returns various test options.
+ attr_reader :options
+
# :call-seq:
+ # using(options) => self
+ #
+ # Sets various test options and returns self. Accepts a hash of options, or symbols (a symbol sets that
+ # option to true). For example:
+ # test.using :testng, :properties=>{ "url"=>"http://localhost:8080" }
+ #
+ # Currently supports the following options:
+ # * :properties -- System properties.
+ # * :java_args -- Java arguments when forking a new JVM.
+ # * :fail_on_failure -- True to fail on test failure (default is true).
+ def using(*args)
+ args.pop.each { |key, value| @options[key.to_sym] = value } if Hash === args.last
+ args.each { |key| @options[key.to_sym] = true }
+ self
+ end
+
+ # :call-seq:
# include(*classes) => self
#
- # See JUnitTask#include.
+ # Include only the specified test cases. Unless specified, the default is to include
+ # all test cases. This method accepts multiple arguments and returns self.
+ #
+ # Test cases are specified using the fully qualified class name. You can also use file-like
+ # patterns (glob) to specify collection of classes. For example:
+ # test.include "com.example.FirstTest"
+ # test.include "com.example.*"
+ # test.include "com.example.Module*"
+ # test.include "*.{First,Second}Test"
+ #
+ # By default, all classes that have a name ending with Test or Suite are included.
+ # Use these suffixes for your test and test suite classes respectively, to distinguish them
+ # from stubs, helper classes, etc.
def include(*classes)
- junit.include *classes
+ @include += classes
self
end
# :call-seq:
# exclude(*classes) => self
#
- # See JUnitTask#exclude.
+ # Exclude the specified test cases. This method accepts multiple arguments and returns self.
+ # See #include for the type of arguments you can use.
def exclude(*classes)
- junit.exclude *classes
+ @exclude += classes
self
end
+ # :call-seq:
+ # classes() => strings
+ #
+ # List of test classes to run. Determined by finding all the test classes in the target directory,
+ # and reducing based on the include/exclude patterns.
+ def classes()
+ base = Pathname.new(compile.target.to_s)
+ patterns = self.class.const_get("#{framework.to_s.upcase}_TESTS_PATTERN").to_a
+ FileList[patterns.map { |pattern| "#{base}/**/#{pattern}.class" }].
+ map { |file| Pathname.new(file).relative_path_from(base).to_s.ext("").gsub(File::SEPARATOR, ".") }.
+ select { |name| include?(name) }.flatten.sort
+ end
+
+ # List of failed test classes. Set after running the tests.
+ attr_reader :failed_tests
+
+ # :call-seq:
+ # include?(name) => boolean
+ #
+ # Returns true if the specified class name matches the inclusion/exclusion pattern. Used to determine
+ # which tests to execute.
+ def include?(name)
+ (@include.empty? || @include.any? { |pattern| File.fnmatch(pattern, name) }) &&
+ !@exclude.any? { |pattern| File.fnmatch(pattern, name) }
+ end
+
+ # :call-seq:
+ # requires() => classpath
+ #
+ # Returns the classpath for the selected test frameworks. Necessary for compiling and running test cases.
+ def requires()
+ self.class.const_get("#{framework.to_s.upcase}_REQUIRES").to_a + [JMOCK_REQUIRES]
+ end
+
+ # :call-seq:
+ # framework() => symbol
+ #
+ # Returns the test framework, e.g. :junit, :testng.
+ def framework()
+ @framework ||= TEST_FRAMEWORKS.detect { |name| options[name] } || TEST_FRAMEWORKS.first
+ end
+
+ # :call-seq:
+ # report_to() => file
+ #
+ # Test frameworks that can produce reports, will write them to this directory.
+ #
+ # This is framework dependent, so unless you use the default test framework, call this method
+ # after setting the test framework.
+ def report_to()
+ @report_to ||= file(@project.path_to(:reports, "#{framework}")=>self)
+ end
+
+ protected
+
+ # :call-seq:
+ # run_tests()
+ #
+ # Runs the test cases using the selected test framework. Executes as part of the task.
+ def run_tests()
+ classes = self.classes
+ if classes.empty?
+ @failed_tests = []
+ else
+ puts "Running tests in #{@project.name}" if verbose
+ @failed_tests = send("#{framework}_run",
+ :classes => classes,
+ :classpath => @classpath + [compile.target],
+ :properties => { "baseDir" => compile.target.to_s }.merge(options[:properties] || {}),
+ :java_args => options[:java_args])
+ unless @failed_tests.empty?
+ warn "The following tests failed:\n#{@failed_tests.join("\n")}" if verbose
+ fail "Tests failed!"
+ end
+ end
+ end
+
end
+
+ # The JUnit test framework. This is the default test framework, but you can force it by
+ # adding the following to your project:
+ # test.using :testng
+ #
+ # You can use the report method to control the junit:report task.
+ module JUnit
+
+ # Used by the junit:report task. Access through JUnit#report if you want to set various
+ # options for that task, for example:
+ # JUnit.report.frames = false
+ class Report
+
+ # Ant-Trax required for running the JUnitReport task.
+ Java.rjb.onload { |rjb| rjb.classpath << "org.apache.ant:ant-trax:jar:#{Ant::VERSION}" }
+
+ # Parameters passed to the Ant JUnitReport task.
+ attr_reader :params
+ # True (default) to produce a report using frames, false to produce a single-page report.
+ attr_accessor :frames
+ # Directory for the report style (defaults to using the internal style).
+ attr_accessor :style_dir
+ # Target directory for generated report.
+ attr_accessor :target
+
+ def initialize()
+ @params = {}
+ @frames = true
+ @target = "reports/junit"
+ end
+
+ # :call-seq:
+ # generate(projects, target?)
+ #
+ # Generates a JUnit report for these projects (must run JUnit tests first) into the
+ # target directory. You can specify a target, or let it pick the default one from the
+ # target attribute.
+ def generate(projects, target = @target.to_s)
+ html_in = File.join(target, "html")
+ rm_rf html_in ; mkpath html_in
+
+ Buildr.ant("junit-report") do |ant|
+ ant.junitreport :todir=>target do
+ projects.select { |project| project.test.framework == :junit }.
+ map { |project| project.test.report_to.to_s }.select { |path| File.exist?(path) }.
+ each { |path| ant.fileset(:dir=>path) { ant.include :name=>"TEST-*.xml" } }
+ options = { :format=>frames ? "frames" : "noframes" }
+ options[:styledir] = style_dir if style_dir
+ ant.report options.merge(:todir=>html_in) do
+ params.each { |key, value| ant.param :name=>key, :expression=>value }
+ end
+ end
+ end
+ end
+
+ end
+
+ # JUnit version number.
+ JUNIT_VERSION = "4.3.1"
+ # JUnit specification.
+ JUNIT_REQUIRES = "junit:junit:jar:#{JUNIT_VERSION}"
+ # Pattern for selecting JUnit test classes. Regardless of include/exclude patterns, only classes
+ # that match this pattern are used.
+ JUNIT_TESTS_PATTERN = [ "Test*", "*Test" ]
+
+ # Ant-JUnit requires for JUnit and JUnit reports tasks.
+ Java.rjb.onload { |rjb| rjb.classpath << "org.apache.ant:ant-junit:jar:#{Ant::VERSION}" }
+
+ class << self
+
+ # :call-seq:
+ # report()
+ #
+ # Returns the Report object used by the junit:report task. You can use this object to set
+ # various options that affect your report, for example:
+ # JUnit.report.frames = false
+ # JUnit.report.params["title"] = "My App"
+ def report()
+ @report ||= Report.new
+ end
+
+ def included(mod)
+ mod::TEST_FRAMEWORKS << :junit
+ end
+ private :included
+
+ end
+
+ private
+
+ def junit_run(args)
+ rm_rf report_to.to_s ; mkpath report_to.to_s
+ # Use Ant to execute the Junit tasks, gives us performance and reporting.
+ Buildr.ant("junit") do |ant|
+ ant.junit :printsummary=>"withOutAndErr" do
+ ant.classpath :path=>args[:classpath].map(&:to_s).each { |path| file(path).invoke }.join(File::PATH_SEPARATOR)
+ args[:properties].each { |key, value| ant.sysproperty :key=>key, :value=>value }
+ ant.formatter :type=>"plain"
+ ant.formatter :type=>"xml"
+ ant.batchtest :todir=>report_to.to_s, :failureproperty=>"failed" do
+ ant.fileset :dir=>compile.target.to_s do
+ args[:classes].each { |cls| ant.include :name=>cls.gsub(".", "/").ext("class") }
+ end
+ end
+ end
+ return [] unless ant.project.getProperty("failed")
+ end
+ # But Ant doesn't tell us what went kaput, so we'll have to parse the test files.
+ args[:classes].inject([]) do |failed, name|
+ if report = File.read(File.join(report_to.to_s, "TEST-#{name}.txt")) rescue nil
+ # The second line (if exists) is the status line and we scan it for its values.
+ status = (report.split("\n")[1] || "").scan(/(run|failures|errors):\s*(\d+)/i).
+ inject(Hash.new(0)) { |hash, pair| hash[pair[0].downcase.to_sym] = pair[1].to_i ; hash }
+ failed << name if status[:failures] > 0 || status[:errors] > 0
+ end
+ failed
+ end
+ end
+
+ namespace "junit" do
+ desc "Generate JUnit tests report in #{report.target}"
+ task("report") do |task|
+ report.generate Project.projects
+ puts "Generated JUnit tests report in #{report.target}"
+ end
+ end
+
+ task("clean") { rm_rf report.target.to_s }
+
+ end
+
+
+ # The TestNG test framework. Use by adding the following to your project:
+ # test.using :testng
+ module TestNG
+
+ # TestNG version number.
+ TESTNG_VERSION = "5.5"
+ # TestNG specification.
+ TESTNG_REQUIRES = "org.testng:testng:jar:jdk15:#{TESTNG_VERSION}"
+ # Pattern for selecting TestNG test classes. Regardless of include/exclude patterns, only classes
+ # that match this pattern are used.
+ TESTNG_TESTS_PATTERN = [ "Test*", "*Test", "*TestCase" ]
+
+ class << self
+
+ def included(mod)
+ mod::TEST_FRAMEWORKS << :testng
+ end
+ private :included
+
+ end
+
+ private
+
+ def testng_run(args)
+ cmd_args = [ "org.testng.TestNG", "-sourcedir", compile.sources.join(";"), "-suitename", @project.name ]
+ cmd_args << "-d" << report_to.to_s
+ cmd_options = args.only(:classpath, :properties, :java_args)
+ args[:classes].inject([]) do |failed, test|
+ begin
+ Buildr.java cmd_args, "-testclass", test, cmd_options.merge(:name=>test)
+ failed
+ rescue
+ failed << test
+ end
+ end
+ end
+
+ end
+
+ class TestTask ; include JUnit ; include TestNG ; end
+
end
class Project
@@ -267,97 +567,194 @@
#
# You can also enhance this task directly. This method accepts a list of arguments
# that are used as prerequisites and an optional block that will be executed by the
# test task.
#
- # This task will first execute the test.compile task, followed by the test.setup
- # task and test.junit task, then any of your enhancements, and end by executing
- # test.teardown.
+ # This task compiles the project and the test cases (in that order) before running any tests.
+ # It execute the setup task, runs all the test cases, any enhancements, and ends with the
+ # teardown tasks.
def test(*prereqs, &block)
task("test").enhance prereqs, &block
end
end
- # Global task compiles all projects.
- desc "Run all test cases"
- Project.local_task("test") { |name| "Running tests in #{name}" }
Project.on_define do |project|
# Define a recursive test task, and pass it a reference to the project so it can discover all other tasks.
Java::TestTask.define_task("test")
project.test.instance_eval { instance_variable_set :@project, project }
- project.recursive_task("test")
+ #project.recursive_task("test")
# Similar to the regular resources task but using different paths.
resources = Java::ResourcesTask.define_task("test:resources")
- resources.filter.from project.path_to("src/test/resources")
+ project.path_to("src/test/resources").tap { |dir| resources.filter.from dir if File.exist?(dir) }
# Similar to the regular compile task but using different paths.
- compile = Java::CompileTask.define_task("test:compile"=>[project.compile, project.test.prepare, project.test.resources])
+ compile = Java::CompileTask.define_task("test:compile"=>[project.compile, task("test:prepare"), project.test.resources])
project.path_to("src/test/java").tap { |dir| compile.from dir if File.exist?(dir) }
compile.into project.path_to(:target, "test-classes")
resources.filter.into compile.target
+ project.test.enhance [compile]
# Define the JUnit task here, otherwise we get a normal task.
Java::JUnitTask.define_task("test:junit")
# Define these tasks once, otherwise we may get a namespace error.
project.test.setup ; project.test.teardown
- # Include the JUnit, Mock and other commonly used dependencies.
- project.test.with Java::JUNIT_REQUIRES
project.enhance do |project|
- # Copy the regular compile classpath over, and also include the generated classes.
- project.test.with project.compile.classpath, project.compile.target
- project.test.junit.with project.test.compile.classpath
- # Tell the JUnit task where to pick the test cases from.
- project.test.junit.from project.test.compile.target
- project.test.junit.options[:properties] ||= {}
- project.test.junit.options.tap do |options|
- options[:properties] ||= {}
- options[:properties]["baseDir"] ||= project.test.compile.target.to_s
+ # Copy the regular compile classpath over, and also include the generated classes, both of which
+ # can be used in the test cases. And don't forget the classpath required by the test framework (e.g. JUnit).
+ project.test.with project.compile.classpath, project.compile.target, project.test.requires
+ project.clean do
+ verbose(false) do
+ rm_rf project.test.compile.target.to_s
+ rm_rf project.test.report_to.to_s
+ end
end
- project.clean { verbose(false) { rm_rf project.test.compile.target.to_s } }
end
end
- # This rule takes a suffix and runs that test case in the current project. For example;
- # rake test:MyTest
- # will run the test case class com.example.MyTest, if found in the current project.
- rule /^test:.*$/ do |task|
- test = task.name.scan(/test:(.*)/)[0][0]
- # Glob if no glob pattern used.
- test = "*#{test}*" unless test =~ /\*/
- if test =~ /\{.+\}/
- # Unfortunately, fnmatch doesn't do {foo,bar}, so we have to expand those ourselves.
- tests = test[/\{.+\}/][1...-1].split(",").map { |name| test.sub(/\{.+\}/, name) }
- else
- tests = [test]
- end
- # Since the test case may reside in a sub-project, we need to set the include/exclude pattern on
- # all sub-projects, but only invoke test on the local project.
- Project.projects.each { |project| project.test.junit.instance_eval { @include = tests ; @exclude.clear } }
- Project.local_projects.each { |project| project.test.invoke }
- end
-
class Options
# Runs test cases after the build when true (default). This forces test cases to execute
# after the build, including when running build related tasks like install, deploy and release.
#
- # You can skip test cases by turning this option off directly, or by setting the environment
- # variable TEST to "no". For example:
- # rake build # With tests
- # rake build TEST=no # Without tests
+ # Set to false to not run any test cases. Set to :all to run all test cases, ignoring failures.
+ #
+ # This option is set from the environment variable "test", so you can also do:
+ # buildr # With tests
+ # buildr test=no # Without tests
+ # buildr test=all # Ignore failures
attr_accessor :test
+ def test() #:nodoc:
+ if @test.nil?
+ case value = ENV["TEST"] || ENV["test"]
+ when /^(no|off|false|skip)$/i
+ @test = false
+ when /^all$/i
+ @test = :all
+ when /^(yes|on|true)$/i, nil
+ @test = true
+ else
+ warn "Expecting the environment variable test to be 'no' or 'all', not sure what to do with #{value}, so I'm just going to run all the test cases and stop at failure."
+ @test = true
+ end
+ end
+ @test
+ end
+
end
- options.test = (ENV["TEST"] || ENV["test"]) !~ /(no|off|false)/
- task("build") do |task|
+ desc "Run all test cases"
+ task("test") { TestTask.run_local_tests false }
+
+ # This rule takes a suffix and runs that test case in the current project. For example;
+ # buildr test:MyTest
+ # will run the test case class com.example.MyTest, if found in the current project.
+ #
+ # If you want to run multiple test cases, separate tham with a comma. You can also use glob
+ # (* and ?) patterns to match multiple tests, e.g. com.example.* to run all test cases in
+ # a given package. If you don't specify a glob pattern, asterisks are added for you.
+ rule /^test:.*$/ do |task|
+ TestTask.only_run task.name.scan(/test:(.*)/)[0][0].split(",")
+ task("test").invoke
+ end
+
+ task "build" do |task|
# Make sure this happens as the last action on the build, so all other enhancements
# are made to run before starting the test cases.
task.enhance do
- task("test").invoke if Buildr.options.test
+ task("test").invoke unless Buildr.options.test == false
end
+ end
+
+
+ # The integration tests task. Buildr has one such task (see Buildr#integration) that runs
+ # all tests marked with :integration=>true, and has a setup/teardown tasks separate from
+ # the unit tests.
+ class IntegrationTestsTask < Rake::Task
+
+ def initialize(*args) #:nodoc:
+ super
+ task "#{name}-setup"
+ task "#{name}-teardown"
+ enhance { puts "Running integration tests..." if verbose }
+ end
+
+ def execute() #:nodoc:
+ setup.invoke
+ begin
+ super
+ ensure
+ teardown.invoke
+ end
+ end
+
+ # :call-seq:
+ # setup(*prereqs) => task
+ # setup(*prereqs) { |task| .. } => task
+ #
+ # Returns the setup task. The setup task is executed before running the integration tests.
+ def setup(*prereqs, &block)
+ Rake::Task["rake:integration-setup"].enhance prereqs, &block
+ end
+
+ # :call-seq:
+ # teardown(*prereqs) => task
+ # teardown(*prereqs) { |task| .. } => task
+ #
+ # Returns the teardown task. The teardown task is executed after running the integration tests.
+ def teardown(*prereqs, &block)
+ Rake::Task["rake:integration-teardown"].enhance prereqs, &block
+ end
+
+ end
+
+ # :call-seq:
+ # integration() { |task| .... }
+ # integration() => IntegrationTestTask
+ #
+ # Use this method to return the integration tests task, or enhance it with a block to execute.
+ #
+ # There is one integration tests task you can execute directly, or as a result of running the package
+ # task (or tasks that depend on it, like install and deploy). It contains all the tests marked with
+ # :integration=>true, all other tests are considered unit tests and run by the test task before packaging.
+ # So essentially: build=>test=>packaging=>integration=>install/deploy.
+ #
+ # You add new test cases from projects that define integration tests using the regular test task,
+ # but with the following addition:
+ # test.using :integration
+ #
+ # Use this method to enhance the setup and teardown tasks that are executed before (and after) all
+ # integration tests are run, for example, to start a Web server or create a database.
+ def integration(*deps, &block)
+ Rake::Task["rake:integration"].enhance deps, &block
+ end
+
+ IntegrationTestsTask.define_task("integration") { TestTask.run_local_tests true }
+
+ # Similar to test:[pattern] but for integration tests.
+ rule /^integration:.*$/ do |task|
+ TestTask.only_run task.name.scan(/integration:(.*)/)[0][0].split(",")
+ task("integration").invoke
+ end
+
+ # Anything that comes after local packaging (install, deploy) executes the integration tests,
+ # which do not conflict with integration invoking the project's own packaging (package=>
+ # integration=>foo:package is not circular, just confusing to debug.)
+ task "package" do |task|
+ integration.invoke if Buildr.options.test && Rake.application.original_dir == Dir.pwd
+ end
+
+
+ task("help") do
+ puts
+ puts "To run a full build without running any test cases:"
+ puts " buildr test=no"
+ puts "To run specific test case:"
+ puts " buildr test:MyTest"
+ puts "To run integration tests:"
+ puts " buildr integration"
end
end