buildr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lac...@apache.org
Subject svn commit: r688286 - in /incubator/buildr/trunk: CHANGELOG addon/buildr/emma.rb doc/pages/more_stuff.textile spec/emma_spec.rb spec/spec_helpers.rb
Date Sat, 23 Aug 2008 10:03:39 GMT
Author: lacton
Date: Sat Aug 23 03:03:38 2008
New Revision: 688286

URL: http://svn.apache.org/viewvc?rev=688286&view=rev
Log:
BUILDR-128: Emma support (test coverage tool similar to Cobertura)

Added:
    incubator/buildr/trunk/addon/buildr/emma.rb
    incubator/buildr/trunk/spec/emma_spec.rb
Modified:
    incubator/buildr/trunk/CHANGELOG
    incubator/buildr/trunk/doc/pages/more_stuff.textile
    incubator/buildr/trunk/spec/spec_helpers.rb

Modified: incubator/buildr/trunk/CHANGELOG
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/CHANGELOG?rev=688286&r1=688285&r2=688286&view=diff
==============================================================================
--- incubator/buildr/trunk/CHANGELOG (original)
+++ incubator/buildr/trunk/CHANGELOG Sat Aug 23 03:03:38 2008
@@ -1,6 +1,7 @@
 1.3.3 (Pending)
 * Added:  Growl notifications (OS X only).
 * Added:  error, info and trace methods.
+* Added:  BUILDR-128 Emma support
 * Change: Error reporting now shows 'buildr aborted!' (used to say rake),
           more of the stack trace without running --trace, and when running
           with supported terminal, error message is red.

Added: incubator/buildr/trunk/addon/buildr/emma.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/addon/buildr/emma.rb?rev=688286&view=auto
==============================================================================
--- incubator/buildr/trunk/addon/buildr/emma.rb (added)
+++ incubator/buildr/trunk/addon/buildr/emma.rb Sat Aug 23 03:03:38 2008
@@ -0,0 +1,236 @@
+# 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 'buildr/java'
+
+
+module Buildr
+
+  # Provides the <code>emma:html</code> and <code>emma:xml</code>
tasks.
+  # Require explicitly using <code>require "buildr/emma"</code>.
+  #
+  # You can generate emma reports for a single project 
+  # using the project name as prefix:
+  #
+  #   project_name:emma:html
+  #
+  # You can also specify which classes to include/exclude from instrumentation by 
+  # passing a class name regexp to the <code>emma.include</code> or 
+  # <code>emma.exclude</code> methods. 
+  # 
+  #   define 'someModule' do 
+  #      emma.include 'some.package.*'
+  #      emma.exclude 'some.foo.util.SimpleUtil'
+  #   end
+  module Emma
+
+    class << self
+
+      REQUIRES = ['emma:emma_ant:jar:2.0.5312', 'emma:emma:jar:2.0.5312'] unless const_defined?('REQUIRES')
+
+      def requires()
+        @requires ||= Buildr.artifacts(REQUIRES).each(&:invoke).map(&:to_s)
+      end
+
+      def report_to format=nil
+        File.expand_path('reports/emma')
+      end
+
+      def data_file()
+        File.join(report_to, 'coverage.es')
+      end
+
+      def ant
+          Buildr.ant 'emma' do |ant|
+            ant.taskdef :classpath=>requires.join(File::PATH_SEPARATOR), :resource=>'emma_ant.properties'
+            ant.emma :verbosity=>(Buildr.application.options.trace ? 'verbose' : 'warning')
do
+              yield ant
+            end
+          end
+      end
+    end
+    
+    class EmmaConfig # :nodoc:
+      
+      def initialize(project)
+        @project = project
+      end
+      
+      attr_reader :project
+      private :project
+
+      attr_writer :metadata_file, :coverage_file, :instrumented_dir, :report_dir
+      
+      def coverage_file
+        @coverage_file ||= File.join(report_dir, 'coverage.ec')
+      end
+
+      def metadata_file
+        @metadata_file ||= File.join(report_dir, 'coverage.em')
+      end
+
+      def instrumented_dir
+        @instrumented_dir ||= project.path_to(:target, :instrumented, :classes)
+      end
+
+      def report_dir
+        @report_dir ||= project.path_to(:reports, :emma)
+      end
+      
+      def report_to format
+        report_dir
+      end
+
+      # :call-seq:
+      #   project.emma.include(*classPatterns)
+      #
+      def include(*classPatterns)
+        includes.push(*classPatterns)
+        self
+      end
+      
+      def includes
+        @includeClasses ||= []
+      end
+
+      # :call-seq:
+      #   project.emma.exclude(*classPatterns)
+      #
+      def exclude(*classPatterns)
+        excludes.push(*classPatterns)
+        self
+      end
+
+      def excludes
+        @excludeClasses ||= []
+      end
+
+      def sources
+        project.compile.sources
+      end
+    end
+
+    module EmmaExtension # :nodoc:
+      include Buildr::Extension
+
+      def emma
+        @emma_config ||= EmmaConfig.new(self)
+      end
+
+      after_define do |project|
+        emma = project.emma
+        
+        namespace 'emma' do
+          # Instrumented bytecode goes in a different directory. This task creates before
running the test
+          # cases and monitors for changes in the generate bytecode.
+          instrumented = project.file(emma.instrumented_dir => file(project.compile.target))
do |task|
+            unless project.compile.sources.empty?
+              info "Instrumenting classes with emma metadata file #{emma.metadata_file}"
+              Emma.ant do |ant|
+                ant.instr :instrpath=>project.compile.target.to_s, :destdir=>task.to_s,
:metadatafile=>emma.metadata_file do
+                  ant.filter :includes=>emma.includes.join(', ') unless emma.includes.empty?
+                  ant.filter :excludes=>emma.excludes.join(', ') unless emma.excludes.empty?
+                end
+              end
+              touch task.to_s, :verbose=>false
+            end
+          end
+
+          task 'instrument' => instrumented
+          
+          [:xml, :html].each do |format|
+            task format => ['instrument', 'test'] do
+              missing_required_files = [emma.metadata_file, emma.coverage_file].reject {
|f| File.exist?(f) }
+              if missing_required_files.empty?
+                info "Creating test coverage reports in #{emma.report_dir}"
+                mkdir_p emma.report_dir, :verbose=>false
+                Emma.ant do |ant|
+                  ant.report do
+                    ant.infileset :file=>emma.metadata_file
+                    ant.infileset :file=>emma.coverage_file
+                    ant.send format, :outfile=>File.join(emma.report_to(format),"coverage.#{format}")
+                    ant.sourcepath do
+                      emma.sources.flatten.each do |src|
+                        ant.dirset(:dir=>src.to_s) if File.exist?(src.to_s)
+                      end
+                    end
+                  end
+                end
+              else
+                info "No test coverage report for #{project}. Missing: #{missing_required_files.join(',
')}"
+              end
+            end
+          end
+            
+        end
+
+        # We now have two target directories with bytecode.
+        project.test.dependencies.unshift emma.instrumented_dir
+        project.test.with Emma.requires
+        project.test.options[:properties]["emma.coverage.out.file"] = emma.coverage_file
+        
+        project.clean do
+          rm_rf [emma.report_dir, emma.coverage_file, emma.metadata_file, emma.instrumented_dir],
:verbose=>false
+        end
+        
+      end
+      
+    end
+
+    class Buildr::Project
+      include EmmaExtension
+    end
+
+    namespace "emma" do
+
+      Project.local_task('instrument') { |name| "Instrumenting #{name}" }
+      
+      [:xml, :html].each do |format|
+        desc "Run the test cases and produce code coverage reports in #{format}"
+        task format => ['instrument', 'test'] do
+          info "Creating test coverage reports in #{format}"
+          mkdir_p report_to(format), :verbose=>false
+          Emma.ant do |ant|
+            ant.merge :outfile=>data_file do
+              Buildr.projects.each do |project|
+                ant.fileset :file=>project.emma.metadata_file
+                ant.fileset :file=>project.emma.coverage_file
+              end
+            end
+            ant.report do
+              ant.infileset :file=>data_file
+              ant.send format, :outfile=>File.join(report_to(format), "coverage.#{format}")
+              ant.sourcepath do
+                Buildr.projects.map(&:emma).map(&:sources).flatten.map(&:to_s).each
do |src|
+                  ant.dirset :dir=>src if File.exist?(src)
+                end    
+              end
+            end
+          end
+        end
+      end
+      
+      task :clean do
+        rm_rf [report_to, data_file], :verbose=>false
+      end
+      end
+
+    task :clean do
+      task('emma:clean').invoke if Dir.pwd == Rake.application.original_dir
+    end
+
+  end
+end

Modified: incubator/buildr/trunk/doc/pages/more_stuff.textile
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/doc/pages/more_stuff.textile?rev=688286&r1=688285&r2=688286&view=diff
==============================================================================
--- incubator/buildr/trunk/doc/pages/more_stuff.textile (original)
+++ incubator/buildr/trunk/doc/pages/more_stuff.textile Sat Aug 23 03:03:38 2008
@@ -171,25 +171,25 @@
 Also, check out the "Buildr plugin for IDEA":http://www.digitalsanctum.com/buildr-plug-in/
(IDEA 7 and later).  Once installed, open your project with IDEA.  If IDEA finds that you
have Buildr installed and finds a buildfile in the project's directory, it will show all the
tasks available for that project.  To run a task, double-click it.  When the task completes,
IDEA will show the results in the Buildr Output window.
 
 
-h2. Cobertura, JDepend
+h2. Cobertura, Emma, JDepend
 
-You can use "Cobertura":http://cobertura.sourceforge.net/ to instrument your code, run the
tests and create a test coverage report in either HTML or XML format.
+You can use "Cobertura":http://cobertura.sourceforge.net/ or "Emma":http://emma.sourceforge.net/
to instrument your code, run the tests and create a test coverage report in either HTML or
XML format.
 
-There are two tasks, both of which generate a test coverage report in the @reports/cobertura@
directory.  For example:
+There are two tasks for each tool, both of which generate a test coverage report in the @reports/cobertura@
(respectively @reports/emma@) directory.  For example:
 
 {{{!sh
 $ buildr test cobertura:html
 }}}
 
-As you can guess, the other task is @cobertura:xml@.
+As you can guess, the other tasks are @cobertura:xml@, @emma:html@ and @emma:xml@.
 
-If you want to generate cobertura reports only for a specific project, you can do so by using
the project name as prefix to cobertura tasks. 
+If you want to generate a test coverage report only for a specific project, you can do so
by using the project name as prefix to the tasks. 
 
 {{{!sh
 $ buildr subModule:cobertura:html
 }}}
 
-Each project can specify which classes to include or exclude from cobertura instrumentation
by giving a class-name regexp to the @cobertura.include@ or  @cobertura.exclude@ methods:
+Each project can specify which classes to include or exclude from cobertura instrumentation
by giving a class-name regexp to the @cobertura.include@ or @cobertura.exclude@ methods:
 
 {{{!ruby
 define 'someModule' do 
@@ -200,6 +200,8 @@
 end
 }}}
 
+Emma has @include@ and @exclude@ methods too, but they take glob patterns instead of regexps.
+
 You can use "JDepend":http://clarkware.com/software/JDepend.html on to generate design quality
metrics.  There are three tasks this time, the eye candy one:
 
 {{{!sh
@@ -208,14 +210,15 @@
 
 The other two tasks are @jdepend:text@ and @jdepend:xml@.
 
-We want Buildr to load fast, and not everyone cares for these tasks, so we don't include
them by default.  If you want to use either one, you need to require it explicitly.  The proper
way to do it in Ruby:
+We want Buildr to load fast, and not everyone cares for these tasks, so we don't include
them by default.  If you want to use one of them, you need to require it explicitly.  The
proper way to do it in Ruby:
 
 {{{!ruby
 require 'buildr/cobertura'
+require 'buildr/emma'
 require 'buildr/jdepend'
 }}}
 
-You may want to add those to the Buildfile.  Alternatively, you can use these tasks for all
your projects without modifying the Buildfile.  One convenient method is to add these two
likes to the @buildr.rb@ file in your home directory.
+You may want to add those to the Buildfile.  Alternatively, you can use these tasks for all
your projects without modifying the Buildfile.  One convenient method is to add these lines
to the @buildr.rb@ file in your home directory.
 
 Another option is to require it from the command line (@--require@ or @-r@), for example:
 

Added: incubator/buildr/trunk/spec/emma_spec.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/emma_spec.rb?rev=688286&view=auto
==============================================================================
--- incubator/buildr/trunk/spec/emma_spec.rb (added)
+++ incubator/buildr/trunk/spec/emma_spec.rb Sat Aug 23 03:03:38 2008
@@ -0,0 +1,120 @@
+# 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/emma')
+
+Buildr::Emma::requires
+
+
+describe Buildr::Emma do
+  before do
+    # Reloading the addon because the sandbox removes all its actions
+    load File.expand_path('../addon/buildr/emma.rb')
+    @tool_module = Buildr::Emma
+  end
+
+  it_should_behave_like 'test coverage tool'
+  
+  describe 'project-specific' do
+    describe 'metadata file' do
+      it 'should have a default value' do
+        define('foo').emma.metadata_file.should point_to_path('reports/emma/coverage.em')
+      end
+      
+      it 'should be overridable' do
+        define('foo') { emma.metadata_file = path_to('target/metadata.emma') }
+        project('foo').emma.metadata_file.should point_to_path('target/metadata.emma')
+      end
+      
+      it 'should be created during instrumentation' do
+        write 'src/main/java/Foo.java', 'public class Foo {}'
+        define('foo')
+        task('foo:emma:instrument').invoke
+        file(project('foo').emma.metadata_file).should exist
+      end
+    end
+    
+    describe 'coverage file' do
+      it 'should have a default value' do
+        define('foo').emma.coverage_file.should point_to_path('reports/emma/coverage.ec')
+      end
+      
+      it 'should be overridable' do
+        define('foo') { emma.coverage_file = path_to('target/coverage.emma') }
+        project('foo').emma.coverage_file.should point_to_path('target/coverage.emma')
+      end
+      
+      it 'should be created during test' do
+        write 'src/main/java/Foo.java', 'public class Foo {}'
+        write_test :for=>'Foo', :in=>'src/test/java'
+        define('foo')
+        task('foo:test').invoke
+        file(project('foo').emma.coverage_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') { emma.include 'Foo' }
+        task("foo:emma:instrument").invoke
+        Dir.chdir('target/instrumented/classes') { Dir.glob('*').sort.should == ['Foo.class']
}
+      end
+
+      it 'should not instrument excluded classes' do
+        define('foo') { emma.exclude 'Foo' }
+        task("foo:emma: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') { emma.include('Ba*').exclude('*ar') }
+        task("foo:emma:instrument").invoke
+        Dir.chdir('target/instrumented/classes') { Dir.glob('*').sort.should == ['Baz.class']
}
+      end
+    end
+    
+    describe 'reports' do
+      before do
+        write 'src/main/java/Foo.java', 'public class Foo {}'
+        write_test :for=>'Foo', :in=>'src/test/java'
+      end
+      
+      describe 'in html' do
+        it 'should inform the user if no coverage data' do
+          rm 'src/test/java/FooTest.java'
+          define('foo')
+          lambda { task('foo:emma:html').invoke }.
+            should show_info(/No test coverage report for foo. Missing: #{project('foo').emma.coverage_file}/)
+        end
+      end
+      
+      describe 'in xml' do
+        it 'should have an xml file' do
+          define('foo')
+          task('foo:emma:xml').invoke
+          file(File.join(project('foo').emma.report_dir, 'coverage.xml')).should exist
+        end
+      end      
+    end
+  end
+end

Modified: incubator/buildr/trunk/spec/spec_helpers.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/spec_helpers.rb?rev=688286&r1=688285&r2=688286&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/spec_helpers.rb (original)
+++ incubator/buildr/trunk/spec/spec_helpers.rb Sat Aug 23 03:03:38 2008
@@ -28,13 +28,15 @@
 
     include Checks::Matchers
 
-    class ::Object #:nodoc:
-      def warn(message)
-        $warning ||= []
-        $warning << message
+    [:info, :warn, :error].each do |severity|
+      ::Object.class_eval do
+        define_method severity do |message|
+          $messages ||= {}
+          ($messages[severity] ||= []) << message
+        end
       end
     end
-
+    
     class << Buildr.application
       alias :deprecated_without_capture :deprecated
       def deprecated(message)
@@ -42,67 +44,50 @@
       end
     end
 
-    class WarningMatcher
-      def initialize(message)
+    class MessageWithSeverityMatcher
+      def initialize(severity, message)
+        @severity = severity
         @expect = message
       end
 
       def matches?(target)
-        $warning = []
+        $messages[@severity] = []
         target.call
-        return Regexp === @expect ? $warning.join('\n') =~ @expect : $warning.include?(@expect.to_s)
+        return Regexp === @expect ? $messages[@severity].join('\n') =~ @expect : $messages[@severity].include?(@expect.to_s)
       end
 
       def failure_message
-        $warning ? "Expected warning #{@expect.source}, found #{$warning}" : "Expected warning
#{@expect.source}, no warning issued"
+        $messages ? "Expected #{@severity} '#{@expect.source}', found #{$messages[@severity]}"
: \
+          "Expected #{@severity} '#{@expect.source}', no #{@severity} issued"
       end
 
       def negative_failure_message
-        "Found unexpected #{$warning}"
+        "Found unexpected '#{$messages[@severity]}'"
       end
     end
 
+    # Test if an info message was shown.  You can use a string or regular expression.
+    #
+    # For example:
+    #   lambda { info 'ze test' }.should show_info(/ze test/)
+    def show_info(message)
+      MessageWithSeverityMatcher.new :info, message
+    end
+
     # Tests if a warning was issued. You can use a string or regular expression.
     #
     # For example:
     #   lambda { warn 'ze test' }.should show_warning(/ze test/)
     def show_warning(message)
-      WarningMatcher.new message
-    end
-
-    class ::Object  #:nodoc:
-      def error(message)
-        $error ||= []
-        $error << message
-      end
+      MessageWithSeverityMatcher.new :warn, message
     end
 
-    class ErrorMessageMatcher
-      def initialize(message)
-        @expect = message
-      end
-
-      def matches?(target)
-        $error = []
-        target.call
-        return Regexp === @expect ? $error.join('\n') =~ @expect : $error.include?(@expect.to_s)
-      end
-
-      def failure_message
-        $error ? "Expected error #{@expect.source}, found #{$error}" : "Expected error #{@expect.source},
no error issued"
-      end
-
-      def negative_failure_message
-        "Found unexpected #{$error}"
-      end
-    end
-    
-    # Test if error message was shown.  You can use a string or regular expression.
+    # Test if an error message was shown.  You can use a string or regular expression.
     #
     # For example:
     #   lambda { error 'ze test' }.should show_error(/ze test/)
     def show_error(message)
-      ErrorMessageMatcher.new message
+      MessageWithSeverityMatcher.new :error, message
     end
 
 



Mime
View raw message