Return-Path: Delivered-To: apmail-incubator-buildr-commits-archive@locus.apache.org Received: (qmail 81280 invoked from network); 22 Aug 2008 22:55:20 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 22 Aug 2008 22:55:20 -0000 Received: (qmail 79771 invoked by uid 500); 22 Aug 2008 22:55:19 -0000 Delivered-To: apmail-incubator-buildr-commits-archive@incubator.apache.org Received: (qmail 79744 invoked by uid 500); 22 Aug 2008 22:55:19 -0000 Mailing-List: contact buildr-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: buildr-dev@incubator.apache.org Delivered-To: mailing list buildr-commits@incubator.apache.org Received: (qmail 79735 invoked by uid 99); 22 Aug 2008 22:55:19 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 22 Aug 2008 15:55:19 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 22 Aug 2008 22:54:30 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 06234238889E; Fri, 22 Aug 2008 15:55:00 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r688213 - in /incubator/buildr/trunk: addon/buildr/cobertura.rb spec/cobertura_spec.rb spec/sandbox.rb spec/test_coverage_spec.rb Date: Fri, 22 Aug 2008 22:54:59 -0000 To: buildr-commits@incubator.apache.org From: lacton@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080822225500.06234238889E@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: lacton Date: Fri Aug 22 15:54:59 2008 New Revision: 688213 URL: http://svn.apache.org/viewvc?rev=688213&view=rev Log: BUILDR-127: Tests for Cobertura addon The tests are divided between those that are specific to Cobertura (cobertura_spec.rb) and those that should be true for most test coverage tool (test_coverage_spec.rb). Changed 'sandbox.rb' so that loading the Cobertura addon does not affect other tests (i.e., the sandbox removes the Cobertura callbacks). Otherwise, some tests fail for wrong reasons. Added: incubator/buildr/trunk/spec/cobertura_spec.rb incubator/buildr/trunk/spec/test_coverage_spec.rb Modified: incubator/buildr/trunk/addon/buildr/cobertura.rb incubator/buildr/trunk/spec/sandbox.rb Modified: incubator/buildr/trunk/addon/buildr/cobertura.rb URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/addon/buildr/cobertura.rb?rev=688213&r1=688212&r2=688213&view=diff ============================================================================== --- incubator/buildr/trunk/addon/buildr/cobertura.rb (original) +++ incubator/buildr/trunk/addon/buildr/cobertura.rb Fri Aug 22 15:54:59 2008 @@ -42,7 +42,7 @@ class << self REQUIRES = ["net.sourceforge.cobertura:cobertura:jar:1.9", "log4j:log4j:jar:1.2.9", - "asm:asm:jar:2.2.1", "asm:asm-tree:jar:2.2.1", "oro:oro:jar:2.0.8"] + "asm:asm:jar:2.2.1", "asm:asm-tree:jar:2.2.1", "oro:oro:jar:2.0.8"] unless const_defined?('REQUIRES') def requires() @requires ||= Buildr.artifacts(REQUIRES).each(&:invoke).map(&:to_s) Added: incubator/buildr/trunk/spec/cobertura_spec.rb URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/cobertura_spec.rb?rev=688213&view=auto ============================================================================== --- incubator/buildr/trunk/spec/cobertura_spec.rb (added) +++ incubator/buildr/trunk/spec/cobertura_spec.rb Fri Aug 22 15:54:59 2008 @@ -0,0 +1,78 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with this +# work for additional information regarding copyright ownership. The ASF +# licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + + +require File.join(File.dirname(__FILE__), 'spec_helpers') +require File.join(File.dirname(__FILE__), 'test_coverage_spec') +Sandbox.require_addon File.join(File.dirname(__FILE__), '../addon', 'buildr/cobertura') + +Buildr::Cobertura::requires + + +describe Buildr::Cobertura do + before do + # Reloading the addon because the sandbox removes all its actions + load File.expand_path('../addon/buildr/cobertura.rb') + @tool_module = Buildr::Cobertura + end + + it_should_behave_like 'test coverage tool' + + describe 'project-specific' do + + describe 'data file' do + it 'should have a default value' do + define('foo').cobertura.data_file.should point_to_path('reports/cobertura.ser') + end + + it 'should be overridable' do + define('foo') { cobertura.data_file = path_to('target/data.cobertura') } + project('foo').cobertura.data_file.should point_to_path('target/data.cobertura') + end + + it 'should be created during instrumentation' do + write 'src/main/java/Foo.java', 'public class Foo {}' + define('foo') + task('foo:cobertura:instrument').invoke + file(project('foo').cobertura.data_file).should exist + end + end + + describe 'instrumentation' do + before do + ['Foo', 'Bar'].each { |cls| write File.join('src/main/java', "#{cls}.java"), "public class #{cls} {}" } + end + + it 'should instrument only included classes' do + define('foo') { cobertura.include 'Foo' } + task("foo:cobertura:instrument").invoke + Dir.chdir('target/instrumented/classes') { Dir.glob('*').sort.should == ['Foo.class'] } + end + + it 'should not instrument excluded classes' do + define('foo') { cobertura.exclude 'Foo' } + task("foo:cobertura:instrument").invoke + Dir.chdir('target/instrumented/classes') { Dir.glob('*').sort.should == ['Bar.class'] } + end + + it 'should instrument classes that are included but not excluded' do + write 'src/main/java/Baz.java', 'public class Baz {}' + define('foo') { cobertura.include('Ba').exclude('ar') } + task("foo:cobertura:instrument").invoke + Dir.chdir('target/instrumented/classes') { Dir.glob('*').sort.should == ['Baz.class'] } + end + end + end +end \ No newline at end of file Modified: incubator/buildr/trunk/spec/sandbox.rb URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/sandbox.rb?rev=688213&r1=688212&r2=688213&view=diff ============================================================================== --- incubator/buildr/trunk/spec/sandbox.rb (original) +++ incubator/buildr/trunk/spec/sandbox.rb Fri Aug 22 15:54:59 2008 @@ -40,6 +40,16 @@ spec.before(:each) { sandbox } spec.after(:each) { reset } end + + # Require an addon without letting its callbacks pollute the Project class. + def require_addon addon_require_path + project_callbacks_without_addon = Project.class_eval { @callbacks }.dup + begin + require addon_require_path + ensure + Project.class_eval { @callbacks = project_callbacks_without_addon } + end + end end @tasks = Buildr.application.tasks.collect do |original| @@ -75,6 +85,7 @@ # Later on we'll want to lose all the on_define created during the test. @_sandbox[:on_define] = Project.class_eval { (@on_define || []).dup } + @_sandbox[:callbacks] = Project.class_eval { (@callbacks || []).dup } @_sandbox[:layout] = Layout.default.clone # Create a local repository we can play with. However, our local repository will be void @@ -105,6 +116,8 @@ Project.clear on_define = @_sandbox[:on_define] Project.class_eval { @on_define = on_define } + callbacks = @_sandbox[:callbacks] + Project.class_eval { @callbacks = callbacks } Layout.default = @_sandbox[:layout].clone $LOAD_PATH.replace @_sandbox[:load_path] Added: incubator/buildr/trunk/spec/test_coverage_spec.rb URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/test_coverage_spec.rb?rev=688213&view=auto ============================================================================== --- incubator/buildr/trunk/spec/test_coverage_spec.rb (added) +++ incubator/buildr/trunk/spec/test_coverage_spec.rb Fri Aug 22 15:54:59 2008 @@ -0,0 +1,221 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with this +# work for additional information regarding copyright ownership. The ASF +# licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + + +require File.join(File.dirname(__FILE__), 'spec_helpers') + +module TestCoverageHelper + def write_test options + write File.join(options[:in], "#{options[:for]}Test.java"), + "public class #{options[:for]}Test extends junit.framework.TestCase { public void test#{options[:for]}() { new #{options[:for]}(); } }" + end + + # Rspec matcher using file glob patterns. + class FileNamePatternMatcher + def initialize(pattern) + @expected_pattern = pattern + @pattern_matcher = lambda { |filename| File.fnmatch? pattern, filename } + end + + def matches?(directory) + @actual_filenames = Dir[File.join(directory,'*')] + @actual_filenames.any? &@pattern_matcher + end + + def failure_message + "Expected to find at least one element matching '#{@expected_pattern}' among #{@actual_filenames.inspect}, but found none" + end + + def negative_failure_message + "Expected to find no element matching '#{@expected_pattern}' among #{@actual_filenames.inspect}, but found matching element(s) #{@actual_filenames.select(&@pattern_matcher).inspect}" + end + end + + def have_files_matching pattern + FileNamePatternMatcher.new pattern + end +end + +describe 'test coverage tool', :shared=>true do + include TestCoverageHelper + + def toolname + @tool_module.name.split('::').last.downcase + end + + describe 'project-specific' do + + before do + write 'src/main/java/Foo.java', 'public class Foo {}' + write_test :for=>'Foo', :in=>'src/test/java' + end + + def test_coverage_config + project('foo').send(toolname) + end + + describe 'clean' do + before { define('foo') } + + it 'should remove the instrumented directory' do + mkdir_p test_coverage_config.instrumented_dir.to_s + task('foo:clean').invoke + file(test_coverage_config.instrumented_dir).should_not exist + end + + it 'should remove the reporting directory' do + mkdir_p test_coverage_config.report_dir + task('foo:clean').invoke + file(test_coverage_config.report_dir).should_not exist + end + end + + describe 'instrumented directory' do + it 'should have a default value' do + define('foo') + test_coverage_config.instrumented_dir.should point_to_path('target/instrumented/classes') + end + + it 'should be overridable' do + toolname = toolname() + define('foo') { send(toolname).instrumented_dir = path_to('target/coverage/classes') } + test_coverage_config.instrumented_dir.should point_to_path('target/coverage/classes') + end + + it 'should be created during instrumentation' do + define('foo') + task("foo:#{toolname}:instrument").invoke + file(test_coverage_config.instrumented_dir).should exist + end + end + + describe 'instrumentation' do + def instrumented_dir + file(test_coverage_config.instrumented_dir) + end + + it 'should happen after compile' do + define('foo') + lambda { task("foo:#{toolname}:instrument").invoke }.should run_task('foo:compile') + end + + it 'should put classes from compile.target in the instrumented directory' do + define('foo') + task("foo:#{toolname}:instrument").invoke + Dir.entries(instrumented_dir.to_s).should == Dir.entries(project('foo').compile.target.to_s) + end + + it 'should touch instrumented directory if anything instrumented' do + a_long_time_ago = Time.now - 10 + define('foo') + mkpath instrumented_dir.to_s + File.utime(a_long_time_ago, a_long_time_ago, instrumented_dir.to_s) + task("foo:#{toolname}:instrument").invoke + instrumented_dir.timestamp.should be_close(Time.now, 2) + end + + it 'should not touch instrumented directory if nothing instrumented' do + a_long_time_ago = Time.now - 10 + define('foo').compile.invoke + mkpath instrumented_dir.to_s + [project('foo').compile.target, instrumented_dir].map(&:to_s).each { |dir| File.utime(a_long_time_ago, a_long_time_ago, dir) } + task("foo:#{toolname}:instrument").invoke + instrumented_dir.timestamp.should be_close(a_long_time_ago, 2) + end + end + + describe 'testing classpath' do + it 'should give priority to instrumented classes over non-instrumented ones' do + define('foo') + depends = project('foo').test.dependencies + depends.index(test_coverage_config.instrumented_dir).should < depends.index(project('foo').compile.target) + end + + it 'should have the test coverage tools artifacts' do + define('foo') + @tool_module.requires.each { |artifact| project('foo').test.dependencies.should include(artifact) } + end + end + + describe 'html report' do + it 'should have html files' do + define('foo') + task("foo:#{toolname}:html").invoke + test_coverage_config.report_to(:html).should have_files_matching('*.html') + end + + it 'should contain full source code, including comments' do + write 'src/main/java/Foo.java', + 'public class Foo { /* This comment is a TOKEN to check that test coverage reports include the source code */ }' + define('foo') + task("foo:#{toolname}:html").invoke + htlm_report_contents = Dir[File.join(test_coverage_config.report_dir, '**/*.html')].map{|path|File.open(path).read}.join + htlm_report_contents.should =~ /TOKEN/ + end + end + end + + describe 'cross-project' do + describe 'reporting' do + before do + write 'src/main/java/Foo.java', 'public class Foo {}' + write 'bar/src/main/java/Bar.java', 'public class Bar {}' + write_test :for=>'Bar', :in=>'bar/src/test/java' + define('foo') { define('bar')} + end + + it 'should have a default target' do + @tool_module.report_to.should point_to_path(File.join('reports', toolname)) + end + + describe 'in html' do + it 'should be a defined task' do + Rake::Task.task_defined?("#{toolname}:html").should be(true) + end + + it 'should happen after project instrumentation and testing' do + lambda { task("#{toolname}:html").invoke }.should run_tasks(["foo:#{toolname}:instrument", 'foo:bar:test']) + end + + it 'should have html files' do + task("#{toolname}:html").invoke + @tool_module.report_to(:html).should have_files_matching('*.html') + end + + it 'should contain full source code, including comments' do + write 'bar/src/main/java/Bar.java', + 'public class Bar { /* This comment is a TOKEN to check that test coverage reports include the source code */ }' + task("#{toolname}:html").invoke + htlm_report_contents = Dir[File.join(@tool_module.report_to(:html), '**/*.html')].map{|path|File.open(path).read}.join + htlm_report_contents.should =~ /TOKEN/ + end + end + end + + describe 'clean' do + it 'should remove the report directory' do + define('foo') + mkdir_p @tool_module.report_to + task("#{toolname}:clean").invoke + file(@tool_module.report_to).should_not exist + end + + it 'should be called when calling global clean' do + define('foo') + lambda { task('clean').invoke }.should run_task("#{toolname}:clean") + end + end + end +end \ No newline at end of file