require 'cucumber/platform' module Cucumber module Rake # Defines a Rake task for running features. # # The simplest use of it goes something like: # # Cucumber::Rake::Task.new # # This will create a task named 'features' described as 'Run Features with # Cucumber'. It will use steps from 'features/**/*.rb' and features in 'features/**/*.feature'. # # To further configure the task, you can pass a block: # # Cucumber::Rake::Task.new do |t| # t.cucumber_opts = %w{--format progress} # end # # This task can also be configured to be run with RCov: # # Cucumber::Rake::Task.new do |t| # t.rcov = true # end # # See the attributes for additional configuration possibilities. class Task class InProcessCucumberRunner #:nodoc: attr_reader :args def initialize(libs, cucumber_opts, feature_files) raise "libs must be an Array when running in-process" unless Array === libs libs.reverse.each{|lib| $LOAD_PATH.unshift(lib)} @args = ( cucumber_opts + feature_files ).flatten.compact end def run require 'cucumber/cli/main' Cucumber::Cli::Main.execute(args) end end class ForkedCucumberRunner #:nodoc: attr_reader :args def initialize(libs, cucumber_bin, cucumber_opts, feature_files) @args = ( ['-I'] + load_path(libs) + quoted_binary(cucumber_bin) + cucumber_opts + feature_files ).flatten end def load_path(libs) ['"%s"' % libs.join(File::PATH_SEPARATOR)] end def quoted_binary(cucumber_bin) ['"%s"' % cucumber_bin] end def run ruby(args.join(" ")) # ruby(*args) is broken on Windows end end class RCovCucumberRunner < ForkedCucumberRunner #:nodoc: def initialize(libs, cucumber_bin, cucumber_opts, feature_files, rcov_opts) @args = ( ['-I'] + load_path(libs) + ['-S', 'rcov'] + rcov_opts + quoted_binary(cucumber_bin) + ['--'] + cucumber_opts + feature_files ).flatten end end LIB = File.expand_path(File.dirname(__FILE__) + '/../..') # :nodoc: # TODO: remove depreated accessors for 0.4.0 def self.deprecate_accessor(attribute) # :nodoc: attr_reader attribute class_eval <<-EOF, __FILE__, __LINE__ + 1 def #{attribute}=(value) @#{attribute} = value warn("Cucumber::Rake::Task##{attribute} is deprecated and will be removed in 0.4.0. Please use profiles for complex settings: http://wiki.github.com/aslakhellesoy/cucumber/using-rake#profiles") end EOF end # Directories to add to the Ruby $LOAD_PATH attr_accessor :libs # Name of the cucumber binary to use for running features. Defaults to Cucumber::BINARY attr_accessor :binary # Array of paths to specific step definition files to use deprecate_accessor :step_list # File pattern for finding step definitions. Defaults to # 'features/**/*.rb'. deprecate_accessor :step_pattern # Array of paths to specific features to run. deprecate_accessor :feature_list # File pattern for finding features to run. Defaults to # 'features/**/*.feature'. Can be overridden by the FEATURE environment variable. deprecate_accessor :feature_pattern # Extra options to pass to the cucumber binary. Can be overridden by the CUCUMBER_OPTS environment variable. # It's recommended to pass an Array, but if it's a String it will be #split by ' '. attr_accessor :cucumber_opts def cucumber_opts=(opts) #:nodoc: @cucumber_opts = String === opts ? opts.split(' ') : opts end # Run cucumber with RCov? Defaults to false. If you set this to # true, +fork+ is implicit. attr_accessor :rcov # Extra options to pass to rcov. # It's recommended to pass an Array, but if it's a String it will be #split by ' '. attr_accessor :rcov_opts def rcov_opts=(opts) #:nodoc: @rcov_opts = String === opts ? opts.split(' ') : opts end # Whether or not to fork a new ruby interpreter. Defaults to true. attr_accessor :fork # Define what profile to be used. When used with cucumber_opts it is simply appended to it. Will be ignored when CUCUMBER_OPTS is used. attr_accessor :profile def profile=(profile) #:nodoc: @profile = profile unless feature_list # TODO: remove once we completely remove these from the rake task. @step_list = [] @feature_list = [] # Don't use accessor to avoid deprecation warning. end end # Define Cucumber Rake task def initialize(task_name = "features", desc = "Run Features with Cucumber") @task_name, @desc = task_name, desc @fork = true @libs = ['lib'] @rcov_opts = %w{--rails --exclude osx\/objc,gems\/} yield self if block_given? @feature_pattern = "features/**/*.feature" if feature_pattern.nil? && feature_list.nil? @step_pattern = "features/**/*.rb" if step_pattern.nil? && step_list.nil? @binary = binary.nil? ? Cucumber::BINARY : File.expand_path(binary) @libs.insert(0, LIB) if binary == Cucumber::BINARY define_task end def define_task # :nodoc: desc @desc task @task_name do runner.run end end def runner(task_args = nil) # :nodoc: cucumber_opts = [(ENV['CUCUMBER_OPTS'] ? ENV['CUCUMBER_OPTS'].split(/\s+/) : nil) || cucumber_opts_with_profile] if(@rcov) RCovCucumberRunner.new(libs, binary, cucumber_opts, feature_files(task_args), rcov_opts) elsif(@fork) ForkedCucumberRunner.new(libs, binary, cucumber_opts, feature_files(task_args)) else InProcessCucumberRunner.new(libs, cucumber_opts, feature_files(task_args)) end end def cucumber_opts_with_profile # :nodoc: @profile ? [cucumber_opts, '--profile', @profile] : cucumber_opts end def feature_files(task_args = nil) # :nodoc: if ENV['FEATURE'] FileList[ ENV['FEATURE'] ] else result = [] result += feature_list.to_a if feature_list result += FileList[feature_pattern].to_a if feature_pattern FileList[result] end end def step_files(task_args = nil) # :nodoc: if ENV['STEPS'] FileList[ ENV['STEPS'] ] else result = [] result += Array(step_list) if step_list result += Array(FileList[step_pattern]) if step_pattern FileList[result] end end end class FeatureTask < Task def initialize(task_name = "feature", desc = "Run a specified feature with Cucumber. #{task_name}[feature_name]") super(task_name, desc) end def define_task # :nodoc: desc @desc task @task_name, :feature_name do |t, args| runner(args).run end end def feature_files(task_arguments) # :nodoc: FileList[File.join("features", "**", "#{task_arguments[:feature_name]}.feature")] end end end end