require "core/build" require "java/compile" 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 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 end # :call-seq: # include(*classes) => self # # 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) @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 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 self end # Returns the JUnit options. attr_reader :options # :call-seq: # using(options) => self # # Sets the JUnit options from a hash and returns self. Right now supports passing properties to JUnit. # # For example: # test.junit.using :properties=>{ "root"=>base_dir } def using(options) options.each { |k,v| @options[k.to_sym] = v } self end private def test_cases() unless @cases @cases = @paths.map do |path| base = Pathname.new(path.to_s) FileList["#{path}/**/*.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 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. class TestTask < Rake::Task def initialize(*args) #:nodoc: super enhance do compile.invoke setup.invoke junit.invoke enhance { 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. def prepare(*prereqs, &block) @project.task("test:prepare").enhance prereqs, &block end # :call-seq: # compile(*sources) => CompileTask # compile(*sources) { |task| .. } => CompileTask # # The compile task is similar to the Project's compile task. However, it compiles all # files found in the src/java/test directory into the target/test-classes directory. # This task is executed by the test task before running any test cases. # # Once the project definition is complete, all classpath dependencies from the regular # compile task are copied over, so you only need to specify classpath dependencies # specific to your test cases. You can do so by calling #with on the test task. # The classpath dependencies used here are also copied over to the junit task. def compile(*sources, &block) @project.task("test:compile").from(sources).enhance &block end # :call-seq: # resources(*prereqs) => ResourcesTask # resources(*prereqs) { |task| .. } => ResourcesTask # # 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(). def junit() @project.task("test:junit") end # :call-seq: # setup(*prereqs) => task # setup(*prereqs) { |task| .. } => task # # Returns the setup task. The setup task is executed at the beginning of the test task, # after compiling the test files. def setup(*prereqs, &block) @project.task("test:setup").enhance prereqs, &block end # :call-seq: # 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 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. def with(*artifacts) compile.with artifacts junit.with artifacts self end # :call-seq: # include(*classes) => self # # See JUnitTask#include. def include(*classes) junit.include *classes self end # :call-seq: # exclude(*classes) => self # # See JUnitTask#exclude. def exclude(*classes) junit.exclude *classes self end end end class Project # :call-seq: # test(*prereqs) => TestTask # test(*prereqs) { |task| .. } => TestTask # # Returns the test task. 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 by calling test.compile, setup for # the test cases by enhancing test.setup and so forth. # # You can use convenient methods that handle the most common settings. For example, # add classpath dependencies using test.with, or include only specific test cases # using test.include. # # 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. 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") # 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") # 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]) 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 # 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 end 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] Project.projects.select { |project| project.base_dir == Rake.application.original_dir }. map { |project| project.test }.each { |task| task.junit.instance_eval { @include = ["*#{test}"] ; @exclude.clear } }. each(&: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 attr_accessor :test end options.test = (ENV["TEST"] || ENV["test"]) !~ /(no|off|false)/ 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 end end end