buildr 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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