buildr 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,34 +1,22 @@
1
1
  require "core/build"
2
2
  require "java/compile"
3
+ require "java/ant"
4
+ require "core/help"
5
+
3
6
 
4
7
  module Buildr
5
8
  module Java
6
9
 
7
- # The JUnit task executes JUnit test cases.
8
- #
9
- # The task requires one or more paths that contain the test cases (see #from),
10
- # in addition to any classpath dependencies (see #with). From the test case
11
- # directories it picks all classes that match the inclusion pattern and none
12
- # that match the exclusion pattern and executes these in order. See #include
13
- # for more information.
14
- class JUnitTask < Rake::Task
10
+ # *Deprecated:* Use the test task directly instead of calling test.junit.
11
+ class JUnitTask < Rake::Task #:nodoc:
15
12
 
13
+ # The classpath used for running the tests. Includes the compile classpath,
14
+ # compiled classes (target). For everything else, add by calling #with.
16
15
  attr_accessor :classpath
17
16
 
18
17
  def initialize(*args) #:nodoc:
19
18
  super
20
- @classpath = []
21
- @paths = []
22
- @include = ["*Test", "*Suite"]
23
- @exclude = []
24
- @options = {}
25
- enhance do |task|
26
- unless test_cases.empty?
27
- puts "Running tests in #{name}" if verbose
28
- passed, failed = Java.junit(test_cases, @options.merge(:classpath=>classpath + @paths))
29
- fail "The following tests failed:\n#{failed.join("\n")}" unless failed.empty?
30
- end
31
- end
19
+ @parent = Rake::Task["#{name.split(":")[0...-1].join(":")}"]
32
20
  end
33
21
 
34
22
  # :call-seq:
@@ -48,7 +36,7 @@ module Buildr
48
36
  # Use these suffixes for your test and test suite classes respectively, to distinguish them
49
37
  # from stubs, helper classes, etc.
50
38
  def include(*classes)
51
- @include += classes
39
+ @parent.include *classes
52
40
  self
53
41
  end
54
42
 
@@ -58,7 +46,7 @@ module Buildr
58
46
  # Exclude the specified test cases. This method accepts multiple arguments and returns self.
59
47
  # See #include for the type of arguments you can use.
60
48
  def exclude(*classes)
61
- @exclude += classes
49
+ @parent.exclude *classes
62
50
  self
63
51
  end
64
52
 
@@ -67,7 +55,6 @@ module Buildr
67
55
  #
68
56
  # Specify one or more directories that include test cases.
69
57
  def from(*files)
70
- @paths += files
71
58
  self
72
59
  end
73
60
 
@@ -77,77 +64,121 @@ module Buildr
77
64
  # Specify artifacts (specs, tasks, files, etc) to include in the classpath when running
78
65
  # the test cases.
79
66
  def with(*files)
80
- @classpath |= Buildr.artifacts(files.flatten).uniq
67
+ (@parent.options[:classpath] ||= []).concat files.flatten
81
68
  self
82
69
  end
83
70
 
84
71
  # Returns the JUnit options.
85
- attr_reader :options
72
+ def options()
73
+ @parent.options
74
+ end
86
75
 
87
76
  # :call-seq:
88
77
  # using(options) => self
89
78
  #
90
- # Sets the JUnit options from a hash and returns self. Right now supports passing properties to JUnit.
79
+ # Sets the JUnit options from a hash and returns self. Right now supports passing :properties to JUnit,
80
+ # and :java_args to the JVM.
91
81
  #
92
82
  # For example:
93
83
  # test.junit.using :properties=>{ "root"=>base_dir }
94
84
  def using(options)
95
- options.each { |k,v| @options[k.to_sym] = v }
85
+ @parent.using options
96
86
  self
97
87
  end
98
88
 
99
- private
100
-
101
- def test_finding_pattern()
102
- "*{Test,TestCase,Suite,TestSuite}"
103
- end
104
-
105
- def test_cases()
106
- unless @cases
107
- @cases = @paths.map do |path|
108
- base = Pathname.new(path.to_s)
109
- FileList["#{path}/**/#{test_finding_pattern}.class"].
110
- map { |file| Pathname.new(file).relative_path_from(base).to_s.ext("").gsub(File::SEPARATOR, ".") }.
111
- select { |name| @include.any? { |pattern| File.fnmatch(pattern, name) } }.
112
- reject { |name| @exclude.any? { |pattern| File.fnmatch(pattern, name) } }
113
- end.flatten.sort
114
- end
115
- @cases
116
- end
117
-
118
89
  end
119
90
 
120
91
 
121
92
  # The test task controls the entire test lifecycle.
122
93
  #
123
- # You can use the test task in three ways. You can access and configure specific
124
- # test tasks, e.g. enhance the #compile task, or run code during #setup/#teardown.
94
+ # You can use the test task in three ways. You can access and configure specific test tasks,
95
+ # e.g. enhance the #compile task, or run code during #setup/#teardown.
125
96
  #
126
- # You can use convenient methods that handle the most common settings. For example,
127
- # add classpath dependencies using #with, or include only specific test cases
128
- # using #include.
97
+ # You can use convenient methods that handle the most common settings. For example, add classpath
98
+ # dependencies using #with, or include only specific test cases using #include.
99
+ #
100
+ # You can also enhance this task directly. This task will first execute the #compile task, followed
101
+ # by the #setup task, run the unit tests, any other enhancements, and end by executing #teardown.
129
102
  #
130
- # You can also enhance this task directly. This task will first execute the #compile
131
- # task, followed by the #setup task and #junit task, then any of your enhancements,
132
- # and end by executing #teardown.
103
+ # Unit tests are fun from classed compiled by the test.compile class that match the TEST_FILE_PATTERN
104
+ # (i.e. MyClassTest, MyClassTestSuite, etc). The test framework is determined by setting one of the
105
+ # test framework options to true, for example:
106
+ # test.unsing :testng
133
107
  class TestTask < Rake::Task
134
108
 
109
+ class << self
110
+
111
+ # Used by the local test and integration tasks to
112
+ # a) Find the local project(s),
113
+ # b) Find all its sub-projects and narrow down to those that have either unit or integration tests,
114
+ # c) Run all the (either unit or integration) tests, and
115
+ # d) Ignore failure if necessary.
116
+ def run_local_tests(integration) #:nodoc:
117
+ Project.local_projects do |project|
118
+ # !(foo ^ bar) tests for equality and accepts nil as false (and select is less obfuscated than reject on ^).
119
+ projects = ([project] + project.projects).select { |project| !(project.test.options[:integration] ^ integration) }
120
+ projects.each do |project|
121
+ puts "Testing #{project.name}" if verbose
122
+ begin
123
+ project.test.invoke
124
+ rescue
125
+ raise unless Buildr.options.test == :all
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ # Used by the test/integration rule to only run tests that match the specified names.
132
+ def only_run(tests) #:nodoc:
133
+ tests = tests.map { |name| name =~ /\*/ ? name : "*#{name}*" }
134
+ # Since the test case may reside in a sub-project, we need to set the include/exclude pattern on
135
+ # all sub-projects, but only invoke test on the local project.
136
+ Project.projects.each { |project| project.test.instance_eval { @include = tests ; @exclude.clear } }
137
+ end
138
+ end
139
+
140
+ # List of supported test framework, first one being a default. Test frameworks are added by
141
+ # including them in TestTask (e.g. JUnit, TestNG).
142
+ TEST_FRAMEWORKS = []
143
+
144
+ # Default options already set on each test task.
145
+ DEFAULT_OPTIONS = { :fail_on_failure=>true }
146
+
147
+ # JMock version..
148
+ JMOCK_VERSION = "1.2.0"
149
+ # JMock specification.
150
+ JMOCK_REQUIRES = "jmock:jmock:jar:#{JMOCK_VERSION}"
151
+
152
+ # The classpath used for running the tests. Includes the compiled classes (compile.target) and
153
+ # their classpath dependencies. Will also include anything you pass to #with, shared between the
154
+ # testing compile and run classpath dependencies.
155
+ attr_reader :classpath
156
+
135
157
  def initialize(*args) #:nodoc:
136
158
  super
137
- enhance do
138
- compile.invoke
139
- setup.invoke
140
- junit.invoke
141
- enhance { teardown.invoke }
159
+ @classpath = []
160
+ @include = []
161
+ @exclude = []
162
+ parent = Rake::Task["^test"] if name[":"] # Only if in namespace
163
+ @options = parent && parent.respond_to?(:options) ? parent.options.clone : DEFAULT_OPTIONS.clone
164
+ enhance { run_tests }
165
+ end
166
+
167
+ def execute() #:nodoc:
168
+ setup.invoke
169
+ begin
170
+ super
171
+ @project.task("test:junit").invoke # In case someone enhanced it
172
+ rescue RuntimeError
173
+ raise if options[:fail_on_failure]
174
+ ensure
175
+ teardown.invoke
142
176
  end
143
177
  end
144
178
 
145
- # :call-seq:
146
- # prepare(*prereqs) => task
147
- # prepare(*prereqs) { |task| .. } => task
148
- #
149
- # Executes before the #compile task to prepare any source files used during compilation.
179
+ # *Deprecated* Add a prerequisite to the compile task instead.
150
180
  def prepare(*prereqs, &block)
181
+ warn_deprecated "Add a prerequisite to the compile task instead of using the prepare task."
151
182
  @project.task("test:prepare").enhance prereqs, &block
152
183
  end
153
184
 
@@ -176,22 +207,9 @@ module Buildr
176
207
  @project.task("test:resources").enhance prereqs, &block
177
208
  end
178
209
 
179
- # :call-seq:
180
- # junit() => JUnitTask
181
- #
182
- # Returns the JUnit task. This task executes JUnit test cases, from classes compiled by
183
- # the test task.
184
- #
185
- # By default it includes all classes with the suffix Test or Suite, and excludes all other classes.
186
- # Use the Test/Suite suffix for classes that implement test cases, avoid this suffix for other
187
- # classes (e.g. stubs, helper objects).
188
- #
189
- # You can also include only specific test cases, or exclude otherwise included test cases
190
- # using #include and #exclude.
191
- #
192
- # The Java property baseDir is set to the classes directory, you can use it to pick up test resources
193
- # that you cannot access using getResources().
210
+ # *Deprecated* Use the test task directly instead of calling test.junit.
194
211
  def junit()
212
+ warn_deprecated "Use the test task directly instead of calling test.junit."
195
213
  @project.task("test:junit")
196
214
  end
197
215
 
@@ -211,41 +229,323 @@ module Buildr
211
229
  #
212
230
  # Returns the teardown task. The teardown task is executed at the end of the test task.
213
231
  def teardown(*prereqs, &block)
214
- @project.task("test.teardown").enhance prereqs, &block
232
+ @project.task("test:teardown").enhance prereqs, &block
215
233
  end
216
234
 
217
235
  # :call-seq:
218
236
  # with(*specs) => self
219
237
  #
220
238
  # Specify artifacts (specs, tasks, files, etc) to include in the classpath when compiling
221
- # and running test cases. Unless you need to limit specific classpath dependencies, use
222
- # this instead of calling test.compile and test.junit individually.
239
+ # and running test cases.
223
240
  def with(*artifacts)
241
+ @classpath |= Buildr.artifacts(artifacts.flatten).uniq
224
242
  compile.with artifacts
225
- junit.with artifacts
243
+ self
244
+ end
245
+
246
+ # Returns various test options.
247
+ attr_reader :options
248
+
249
+ # :call-seq:
250
+ # using(options) => self
251
+ #
252
+ # Sets various test options and returns self. Accepts a hash of options, or symbols (a symbol sets that
253
+ # option to true). For example:
254
+ # test.using :testng, :properties=>{ "url"=>"http://localhost:8080" }
255
+ #
256
+ # Currently supports the following options:
257
+ # * :properties -- System properties.
258
+ # * :java_args -- Java arguments when forking a new JVM.
259
+ # * :fail_on_failure -- True to fail on test failure (default is true).
260
+ def using(*args)
261
+ args.pop.each { |key, value| @options[key.to_sym] = value } if Hash === args.last
262
+ args.each { |key| @options[key.to_sym] = true }
226
263
  self
227
264
  end
228
265
 
229
266
  # :call-seq:
230
267
  # include(*classes) => self
231
268
  #
232
- # See JUnitTask#include.
269
+ # Include only the specified test cases. Unless specified, the default is to include
270
+ # all test cases. This method accepts multiple arguments and returns self.
271
+ #
272
+ # Test cases are specified using the fully qualified class name. You can also use file-like
273
+ # patterns (glob) to specify collection of classes. For example:
274
+ # test.include "com.example.FirstTest"
275
+ # test.include "com.example.*"
276
+ # test.include "com.example.Module*"
277
+ # test.include "*.{First,Second}Test"
278
+ #
279
+ # By default, all classes that have a name ending with Test or Suite are included.
280
+ # Use these suffixes for your test and test suite classes respectively, to distinguish them
281
+ # from stubs, helper classes, etc.
233
282
  def include(*classes)
234
- junit.include *classes
283
+ @include += classes
235
284
  self
236
285
  end
237
286
 
238
287
  # :call-seq:
239
288
  # exclude(*classes) => self
240
289
  #
241
- # See JUnitTask#exclude.
290
+ # Exclude the specified test cases. This method accepts multiple arguments and returns self.
291
+ # See #include for the type of arguments you can use.
242
292
  def exclude(*classes)
243
- junit.exclude *classes
293
+ @exclude += classes
244
294
  self
245
295
  end
246
296
 
297
+ # :call-seq:
298
+ # classes() => strings
299
+ #
300
+ # List of test classes to run. Determined by finding all the test classes in the target directory,
301
+ # and reducing based on the include/exclude patterns.
302
+ def classes()
303
+ base = Pathname.new(compile.target.to_s)
304
+ patterns = self.class.const_get("#{framework.to_s.upcase}_TESTS_PATTERN").to_a
305
+ FileList[patterns.map { |pattern| "#{base}/**/#{pattern}.class" }].
306
+ map { |file| Pathname.new(file).relative_path_from(base).to_s.ext("").gsub(File::SEPARATOR, ".") }.
307
+ select { |name| include?(name) }.flatten.sort
308
+ end
309
+
310
+ # List of failed test classes. Set after running the tests.
311
+ attr_reader :failed_tests
312
+
313
+ # :call-seq:
314
+ # include?(name) => boolean
315
+ #
316
+ # Returns true if the specified class name matches the inclusion/exclusion pattern. Used to determine
317
+ # which tests to execute.
318
+ def include?(name)
319
+ (@include.empty? || @include.any? { |pattern| File.fnmatch(pattern, name) }) &&
320
+ !@exclude.any? { |pattern| File.fnmatch(pattern, name) }
321
+ end
322
+
323
+ # :call-seq:
324
+ # requires() => classpath
325
+ #
326
+ # Returns the classpath for the selected test frameworks. Necessary for compiling and running test cases.
327
+ def requires()
328
+ self.class.const_get("#{framework.to_s.upcase}_REQUIRES").to_a + [JMOCK_REQUIRES]
329
+ end
330
+
331
+ # :call-seq:
332
+ # framework() => symbol
333
+ #
334
+ # Returns the test framework, e.g. :junit, :testng.
335
+ def framework()
336
+ @framework ||= TEST_FRAMEWORKS.detect { |name| options[name] } || TEST_FRAMEWORKS.first
337
+ end
338
+
339
+ # :call-seq:
340
+ # report_to() => file
341
+ #
342
+ # Test frameworks that can produce reports, will write them to this directory.
343
+ #
344
+ # This is framework dependent, so unless you use the default test framework, call this method
345
+ # after setting the test framework.
346
+ def report_to()
347
+ @report_to ||= file(@project.path_to(:reports, "#{framework}")=>self)
348
+ end
349
+
350
+ protected
351
+
352
+ # :call-seq:
353
+ # run_tests()
354
+ #
355
+ # Runs the test cases using the selected test framework. Executes as part of the task.
356
+ def run_tests()
357
+ classes = self.classes
358
+ if classes.empty?
359
+ @failed_tests = []
360
+ else
361
+ puts "Running tests in #{@project.name}" if verbose
362
+ @failed_tests = send("#{framework}_run",
363
+ :classes => classes,
364
+ :classpath => @classpath + [compile.target],
365
+ :properties => { "baseDir" => compile.target.to_s }.merge(options[:properties] || {}),
366
+ :java_args => options[:java_args])
367
+ unless @failed_tests.empty?
368
+ warn "The following tests failed:\n#{@failed_tests.join("\n")}" if verbose
369
+ fail "Tests failed!"
370
+ end
371
+ end
372
+ end
373
+
247
374
  end
248
375
 
376
+
377
+ # The JUnit test framework. This is the default test framework, but you can force it by
378
+ # adding the following to your project:
379
+ # test.using :testng
380
+ #
381
+ # You can use the report method to control the junit:report task.
382
+ module JUnit
383
+
384
+ # Used by the junit:report task. Access through JUnit#report if you want to set various
385
+ # options for that task, for example:
386
+ # JUnit.report.frames = false
387
+ class Report
388
+
389
+ # Ant-Trax required for running the JUnitReport task.
390
+ Java.rjb.onload { |rjb| rjb.classpath << "org.apache.ant:ant-trax:jar:#{Ant::VERSION}" }
391
+
392
+ # Parameters passed to the Ant JUnitReport task.
393
+ attr_reader :params
394
+ # True (default) to produce a report using frames, false to produce a single-page report.
395
+ attr_accessor :frames
396
+ # Directory for the report style (defaults to using the internal style).
397
+ attr_accessor :style_dir
398
+ # Target directory for generated report.
399
+ attr_accessor :target
400
+
401
+ def initialize()
402
+ @params = {}
403
+ @frames = true
404
+ @target = "reports/junit"
405
+ end
406
+
407
+ # :call-seq:
408
+ # generate(projects, target?)
409
+ #
410
+ # Generates a JUnit report for these projects (must run JUnit tests first) into the
411
+ # target directory. You can specify a target, or let it pick the default one from the
412
+ # target attribute.
413
+ def generate(projects, target = @target.to_s)
414
+ html_in = File.join(target, "html")
415
+ rm_rf html_in ; mkpath html_in
416
+
417
+ Buildr.ant("junit-report") do |ant|
418
+ ant.junitreport :todir=>target do
419
+ projects.select { |project| project.test.framework == :junit }.
420
+ map { |project| project.test.report_to.to_s }.select { |path| File.exist?(path) }.
421
+ each { |path| ant.fileset(:dir=>path) { ant.include :name=>"TEST-*.xml" } }
422
+ options = { :format=>frames ? "frames" : "noframes" }
423
+ options[:styledir] = style_dir if style_dir
424
+ ant.report options.merge(:todir=>html_in) do
425
+ params.each { |key, value| ant.param :name=>key, :expression=>value }
426
+ end
427
+ end
428
+ end
429
+ end
430
+
431
+ end
432
+
433
+ # JUnit version number.
434
+ JUNIT_VERSION = "4.3.1"
435
+ # JUnit specification.
436
+ JUNIT_REQUIRES = "junit:junit:jar:#{JUNIT_VERSION}"
437
+ # Pattern for selecting JUnit test classes. Regardless of include/exclude patterns, only classes
438
+ # that match this pattern are used.
439
+ JUNIT_TESTS_PATTERN = [ "Test*", "*Test" ]
440
+
441
+ # Ant-JUnit requires for JUnit and JUnit reports tasks.
442
+ Java.rjb.onload { |rjb| rjb.classpath << "org.apache.ant:ant-junit:jar:#{Ant::VERSION}" }
443
+
444
+ class << self
445
+
446
+ # :call-seq:
447
+ # report()
448
+ #
449
+ # Returns the Report object used by the junit:report task. You can use this object to set
450
+ # various options that affect your report, for example:
451
+ # JUnit.report.frames = false
452
+ # JUnit.report.params["title"] = "My App"
453
+ def report()
454
+ @report ||= Report.new
455
+ end
456
+
457
+ def included(mod)
458
+ mod::TEST_FRAMEWORKS << :junit
459
+ end
460
+ private :included
461
+
462
+ end
463
+
464
+ private
465
+
466
+ def junit_run(args)
467
+ rm_rf report_to.to_s ; mkpath report_to.to_s
468
+ # Use Ant to execute the Junit tasks, gives us performance and reporting.
469
+ Buildr.ant("junit") do |ant|
470
+ ant.junit :printsummary=>"withOutAndErr" do
471
+ ant.classpath :path=>args[:classpath].map(&:to_s).each { |path| file(path).invoke }.join(File::PATH_SEPARATOR)
472
+ args[:properties].each { |key, value| ant.sysproperty :key=>key, :value=>value }
473
+ ant.formatter :type=>"plain"
474
+ ant.formatter :type=>"xml"
475
+ ant.batchtest :todir=>report_to.to_s, :failureproperty=>"failed" do
476
+ ant.fileset :dir=>compile.target.to_s do
477
+ args[:classes].each { |cls| ant.include :name=>cls.gsub(".", "/").ext("class") }
478
+ end
479
+ end
480
+ end
481
+ return [] unless ant.project.getProperty("failed")
482
+ end
483
+ # But Ant doesn't tell us what went kaput, so we'll have to parse the test files.
484
+ args[:classes].inject([]) do |failed, name|
485
+ if report = File.read(File.join(report_to.to_s, "TEST-#{name}.txt")) rescue nil
486
+ # The second line (if exists) is the status line and we scan it for its values.
487
+ status = (report.split("\n")[1] || "").scan(/(run|failures|errors):\s*(\d+)/i).
488
+ inject(Hash.new(0)) { |hash, pair| hash[pair[0].downcase.to_sym] = pair[1].to_i ; hash }
489
+ failed << name if status[:failures] > 0 || status[:errors] > 0
490
+ end
491
+ failed
492
+ end
493
+ end
494
+
495
+ namespace "junit" do
496
+ desc "Generate JUnit tests report in #{report.target}"
497
+ task("report") do |task|
498
+ report.generate Project.projects
499
+ puts "Generated JUnit tests report in #{report.target}"
500
+ end
501
+ end
502
+
503
+ task("clean") { rm_rf report.target.to_s }
504
+
505
+ end
506
+
507
+
508
+ # The TestNG test framework. Use by adding the following to your project:
509
+ # test.using :testng
510
+ module TestNG
511
+
512
+ # TestNG version number.
513
+ TESTNG_VERSION = "5.5"
514
+ # TestNG specification.
515
+ TESTNG_REQUIRES = "org.testng:testng:jar:jdk15:#{TESTNG_VERSION}"
516
+ # Pattern for selecting TestNG test classes. Regardless of include/exclude patterns, only classes
517
+ # that match this pattern are used.
518
+ TESTNG_TESTS_PATTERN = [ "Test*", "*Test", "*TestCase" ]
519
+
520
+ class << self
521
+
522
+ def included(mod)
523
+ mod::TEST_FRAMEWORKS << :testng
524
+ end
525
+ private :included
526
+
527
+ end
528
+
529
+ private
530
+
531
+ def testng_run(args)
532
+ cmd_args = [ "org.testng.TestNG", "-sourcedir", compile.sources.join(";"), "-suitename", @project.name ]
533
+ cmd_args << "-d" << report_to.to_s
534
+ cmd_options = args.only(:classpath, :properties, :java_args)
535
+ args[:classes].inject([]) do |failed, test|
536
+ begin
537
+ Buildr.java cmd_args, "-testclass", test, cmd_options.merge(:name=>test)
538
+ failed
539
+ rescue
540
+ failed << test
541
+ end
542
+ end
543
+ end
544
+
545
+ end
546
+
547
+ class TestTask ; include JUnit ; include TestNG ; end
548
+
249
549
  end
250
550
 
251
551
 
@@ -269,71 +569,46 @@ module Buildr
269
569
  # that are used as prerequisites and an optional block that will be executed by the
270
570
  # test task.
271
571
  #
272
- # This task will first execute the test.compile task, followed by the test.setup
273
- # task and test.junit task, then any of your enhancements, and end by executing
274
- # test.teardown.
572
+ # This task compiles the project and the test cases (in that order) before running any tests.
573
+ # It execute the setup task, runs all the test cases, any enhancements, and ends with the
574
+ # teardown tasks.
275
575
  def test(*prereqs, &block)
276
576
  task("test").enhance prereqs, &block
277
577
  end
278
578
 
279
579
  end
280
580
 
281
- # Global task compiles all projects.
282
- desc "Run all test cases"
283
- Project.local_task("test") { |name| "Running tests in #{name}" }
284
581
 
285
582
  Project.on_define do |project|
286
583
  # Define a recursive test task, and pass it a reference to the project so it can discover all other tasks.
287
584
  Java::TestTask.define_task("test")
288
585
  project.test.instance_eval { instance_variable_set :@project, project }
289
- project.recursive_task("test")
586
+ #project.recursive_task("test")
290
587
  # Similar to the regular resources task but using different paths.
291
588
  resources = Java::ResourcesTask.define_task("test:resources")
292
- resources.filter.from project.path_to("src/test/resources")
589
+ project.path_to("src/test/resources").tap { |dir| resources.filter.from dir if File.exist?(dir) }
293
590
  # Similar to the regular compile task but using different paths.
294
- compile = Java::CompileTask.define_task("test:compile"=>[project.compile, project.test.prepare, project.test.resources])
591
+ compile = Java::CompileTask.define_task("test:compile"=>[project.compile, task("test:prepare"), project.test.resources])
295
592
  project.path_to("src/test/java").tap { |dir| compile.from dir if File.exist?(dir) }
296
593
  compile.into project.path_to(:target, "test-classes")
297
594
  resources.filter.into compile.target
595
+ project.test.enhance [compile]
298
596
  # Define the JUnit task here, otherwise we get a normal task.
299
597
  Java::JUnitTask.define_task("test:junit")
300
598
  # Define these tasks once, otherwise we may get a namespace error.
301
599
  project.test.setup ; project.test.teardown
302
- # Include the JUnit, Mock and other commonly used dependencies.
303
- project.test.with Java::JUNIT_REQUIRES
304
600
 
305
601
  project.enhance do |project|
306
- # Copy the regular compile classpath over, and also include the generated classes.
307
- project.test.with project.compile.classpath, project.compile.target
308
- project.test.junit.with project.test.compile.classpath
309
- # Tell the JUnit task where to pick the test cases from.
310
- project.test.junit.from project.test.compile.target
311
- project.test.junit.options[:properties] ||= {}
312
- project.test.junit.options.tap do |options|
313
- options[:properties] ||= {}
314
- options[:properties]["baseDir"] ||= project.test.compile.target.to_s
315
- end
316
- project.clean { verbose(false) { rm_rf project.test.compile.target.to_s } }
317
- end
318
- end
319
-
320
- # This rule takes a suffix and runs that test case in the current project. For example;
321
- # rake test:MyTest
322
- # will run the test case class com.example.MyTest, if found in the current project.
323
- rule /^test:.*$/ do |task|
324
- test = task.name.scan(/test:(.*)/)[0][0]
325
- # Glob if no glob pattern used.
326
- test = "*#{test}*" unless test =~ /\*/
327
- if test =~ /\{.+\}/
328
- # Unfortunately, fnmatch doesn't do {foo,bar}, so we have to expand those ourselves.
329
- tests = test[/\{.+\}/][1...-1].split(",").map { |name| test.sub(/\{.+\}/, name) }
330
- else
331
- tests = [test]
602
+ # Copy the regular compile classpath over, and also include the generated classes, both of which
603
+ # can be used in the test cases. And don't forget the classpath required by the test framework (e.g. JUnit).
604
+ project.test.with project.compile.classpath, project.compile.target, project.test.requires
605
+ project.clean do
606
+ verbose(false) do
607
+ rm_rf project.test.compile.target.to_s
608
+ rm_rf project.test.report_to.to_s
609
+ end
610
+ end
332
611
  end
333
- # Since the test case may reside in a sub-project, we need to set the include/exclude pattern on
334
- # all sub-projects, but only invoke test on the local project.
335
- Project.projects.each { |project| project.test.junit.instance_eval { @include = tests ; @exclude.clear } }
336
- Project.local_projects.each { |project| project.test.invoke }
337
612
  end
338
613
 
339
614
 
@@ -342,22 +617,144 @@ module Buildr
342
617
  # Runs test cases after the build when true (default). This forces test cases to execute
343
618
  # after the build, including when running build related tasks like install, deploy and release.
344
619
  #
345
- # You can skip test cases by turning this option off directly, or by setting the environment
346
- # variable TEST to "no". For example:
347
- # rake build # With tests
348
- # rake build TEST=no # Without tests
620
+ # Set to false to not run any test cases. Set to :all to run all test cases, ignoring failures.
621
+ #
622
+ # This option is set from the environment variable "test", so you can also do:
623
+ # buildr # With tests
624
+ # buildr test=no # Without tests
625
+ # buildr test=all # Ignore failures
349
626
  attr_accessor :test
350
627
 
628
+ def test() #:nodoc:
629
+ if @test.nil?
630
+ case value = ENV["TEST"] || ENV["test"]
631
+ when /^(no|off|false|skip)$/i
632
+ @test = false
633
+ when /^all$/i
634
+ @test = :all
635
+ when /^(yes|on|true)$/i, nil
636
+ @test = true
637
+ else
638
+ 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."
639
+ @test = true
640
+ end
641
+ end
642
+ @test
643
+ end
644
+
351
645
  end
352
646
 
353
- options.test = (ENV["TEST"] || ENV["test"]) !~ /(no|off|false)/
354
647
 
355
- task("build") do |task|
648
+ desc "Run all test cases"
649
+ task("test") { TestTask.run_local_tests false }
650
+
651
+ # This rule takes a suffix and runs that test case in the current project. For example;
652
+ # buildr test:MyTest
653
+ # will run the test case class com.example.MyTest, if found in the current project.
654
+ #
655
+ # If you want to run multiple test cases, separate tham with a comma. You can also use glob
656
+ # (* and ?) patterns to match multiple tests, e.g. com.example.* to run all test cases in
657
+ # a given package. If you don't specify a glob pattern, asterisks are added for you.
658
+ rule /^test:.*$/ do |task|
659
+ TestTask.only_run task.name.scan(/test:(.*)/)[0][0].split(",")
660
+ task("test").invoke
661
+ end
662
+
663
+ task "build" do |task|
356
664
  # Make sure this happens as the last action on the build, so all other enhancements
357
665
  # are made to run before starting the test cases.
358
666
  task.enhance do
359
- task("test").invoke if Buildr.options.test
667
+ task("test").invoke unless Buildr.options.test == false
668
+ end
669
+ end
670
+
671
+
672
+ # The integration tests task. Buildr has one such task (see Buildr#integration) that runs
673
+ # all tests marked with :integration=>true, and has a setup/teardown tasks separate from
674
+ # the unit tests.
675
+ class IntegrationTestsTask < Rake::Task
676
+
677
+ def initialize(*args) #:nodoc:
678
+ super
679
+ task "#{name}-setup"
680
+ task "#{name}-teardown"
681
+ enhance { puts "Running integration tests..." if verbose }
360
682
  end
683
+
684
+ def execute() #:nodoc:
685
+ setup.invoke
686
+ begin
687
+ super
688
+ ensure
689
+ teardown.invoke
690
+ end
691
+ end
692
+
693
+ # :call-seq:
694
+ # setup(*prereqs) => task
695
+ # setup(*prereqs) { |task| .. } => task
696
+ #
697
+ # Returns the setup task. The setup task is executed before running the integration tests.
698
+ def setup(*prereqs, &block)
699
+ Rake::Task["rake:integration-setup"].enhance prereqs, &block
700
+ end
701
+
702
+ # :call-seq:
703
+ # teardown(*prereqs) => task
704
+ # teardown(*prereqs) { |task| .. } => task
705
+ #
706
+ # Returns the teardown task. The teardown task is executed after running the integration tests.
707
+ def teardown(*prereqs, &block)
708
+ Rake::Task["rake:integration-teardown"].enhance prereqs, &block
709
+ end
710
+
711
+ end
712
+
713
+ # :call-seq:
714
+ # integration() { |task| .... }
715
+ # integration() => IntegrationTestTask
716
+ #
717
+ # Use this method to return the integration tests task, or enhance it with a block to execute.
718
+ #
719
+ # There is one integration tests task you can execute directly, or as a result of running the package
720
+ # task (or tasks that depend on it, like install and deploy). It contains all the tests marked with
721
+ # :integration=>true, all other tests are considered unit tests and run by the test task before packaging.
722
+ # So essentially: build=>test=>packaging=>integration=>install/deploy.
723
+ #
724
+ # You add new test cases from projects that define integration tests using the regular test task,
725
+ # but with the following addition:
726
+ # test.using :integration
727
+ #
728
+ # Use this method to enhance the setup and teardown tasks that are executed before (and after) all
729
+ # integration tests are run, for example, to start a Web server or create a database.
730
+ def integration(*deps, &block)
731
+ Rake::Task["rake:integration"].enhance deps, &block
732
+ end
733
+
734
+ IntegrationTestsTask.define_task("integration") { TestTask.run_local_tests true }
735
+
736
+ # Similar to test:[pattern] but for integration tests.
737
+ rule /^integration:.*$/ do |task|
738
+ TestTask.only_run task.name.scan(/integration:(.*)/)[0][0].split(",")
739
+ task("integration").invoke
740
+ end
741
+
742
+ # Anything that comes after local packaging (install, deploy) executes the integration tests,
743
+ # which do not conflict with integration invoking the project's own packaging (package=>
744
+ # integration=>foo:package is not circular, just confusing to debug.)
745
+ task "package" do |task|
746
+ integration.invoke if Buildr.options.test && Rake.application.original_dir == Dir.pwd
747
+ end
748
+
749
+
750
+ task("help") do
751
+ puts
752
+ puts "To run a full build without running any test cases:"
753
+ puts " buildr test=no"
754
+ puts "To run specific test case:"
755
+ puts " buildr test:MyTest"
756
+ puts "To run integration tests:"
757
+ puts " buildr integration"
361
758
  end
362
759
 
363
760
  end