diff options
Diffstat (limited to 'spec/mspec/lib/mspec/guards')
-rw-r--r-- | spec/mspec/lib/mspec/guards/block_device.rb | 18 | ||||
-rw-r--r-- | spec/mspec/lib/mspec/guards/bug.rb | 30 | ||||
-rw-r--r-- | spec/mspec/lib/mspec/guards/conflict.rb | 19 | ||||
-rw-r--r-- | spec/mspec/lib/mspec/guards/endian.rb | 27 | ||||
-rw-r--r-- | spec/mspec/lib/mspec/guards/feature.rb | 43 | ||||
-rw-r--r-- | spec/mspec/lib/mspec/guards/guard.rb | 118 | ||||
-rw-r--r-- | spec/mspec/lib/mspec/guards/platform.rb | 78 | ||||
-rw-r--r-- | spec/mspec/lib/mspec/guards/quarantine.rb | 13 | ||||
-rw-r--r-- | spec/mspec/lib/mspec/guards/superuser.rb | 17 | ||||
-rw-r--r-- | spec/mspec/lib/mspec/guards/support.rb | 16 | ||||
-rw-r--r-- | spec/mspec/lib/mspec/guards/version.rb | 39 |
11 files changed, 418 insertions, 0 deletions
diff --git a/spec/mspec/lib/mspec/guards/block_device.rb b/spec/mspec/lib/mspec/guards/block_device.rb new file mode 100644 index 0000000000..327f6e564e --- /dev/null +++ b/spec/mspec/lib/mspec/guards/block_device.rb @@ -0,0 +1,18 @@ +require 'mspec/guards/guard' + +class BlockDeviceGuard < SpecGuard + def match? + platform_is_not :freebsd, :windows, :opal do + block = `find /dev /devices -type b 2> /dev/null` + return !(block.nil? || block.empty?) + end + + false + end +end + +class Object + def with_block_device(&block) + BlockDeviceGuard.new.run_if(:with_block_device, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/bug.rb b/spec/mspec/lib/mspec/guards/bug.rb new file mode 100644 index 0000000000..31de6e080d --- /dev/null +++ b/spec/mspec/lib/mspec/guards/bug.rb @@ -0,0 +1,30 @@ +require 'mspec/guards/version' + +class BugGuard < VersionGuard + def initialize(bug, version) + @bug = bug + if String === version + MSpec.deprecate "ruby_bug with a single version", 'an exclusive range ("2.1"..."2.3")' + @version = SpecVersion.new version, true + else + super(version) + end + @parameters = [@bug, @version] + end + + def match? + return false if MSpec.mode? :no_ruby_bug + return false unless PlatformGuard.standard? + if Range === @version + super + else + FULL_RUBY_VERSION <= @version + end + end +end + +class Object + def ruby_bug(bug, version, &block) + BugGuard.new(bug, version).run_unless(:ruby_bug, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/conflict.rb b/spec/mspec/lib/mspec/guards/conflict.rb new file mode 100644 index 0000000000..c1d33e3512 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/conflict.rb @@ -0,0 +1,19 @@ +require 'mspec/guards/guard' + +class ConflictsGuard < SpecGuard + def match? + # Always convert constants to symbols regardless of version. + constants = Object.constants.map { |x| x.to_sym } + @parameters.any? { |mod| constants.include? mod } + end +end + +class Object + # In some cases, libraries will modify another Ruby method's + # behavior. The specs for the method's behavior will then fail + # if that library is loaded. This guard will not run if any of + # the specified constants exist in Object.constants. + def conflicts_with(*modules, &block) + ConflictsGuard.new(*modules).run_unless(:conflicts_with, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/endian.rb b/spec/mspec/lib/mspec/guards/endian.rb new file mode 100644 index 0000000000..6bb01263c7 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/endian.rb @@ -0,0 +1,27 @@ +require 'mspec/guards/guard' + +# Despite that these are inverses, the two classes are +# used to simplify MSpec guard reporting modes + +class EndianGuard < SpecGuard + def pattern + @pattern ||= [1].pack('L') + end + private :pattern +end + +class BigEndianGuard < EndianGuard + def match? + pattern[-1] == ?\001 + end +end + +class Object + def big_endian(&block) + BigEndianGuard.new.run_if(:big_endian, &block) + end + + def little_endian(&block) + BigEndianGuard.new.run_unless(:little_endian, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/feature.rb b/spec/mspec/lib/mspec/guards/feature.rb new file mode 100644 index 0000000000..346212bda0 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/feature.rb @@ -0,0 +1,43 @@ +require 'mspec/guards/guard' + +class FeatureGuard < SpecGuard + def self.enabled?(*features) + new(*features).match? + end + + def match? + @parameters.all? { |f| MSpec.feature_enabled? f } + end +end + +class Object + # Provides better documentation in the specs by + # naming sets of features that work together as + # a whole. Examples include :encoding, :fiber, + # :continuation, :fork. + # + # Usage example: + # + # with_feature :encoding do + # # specs for a method that provides aspects + # # of the encoding feature + # end + # + # Multiple features must all be enabled for the + # guard to run: + # + # with_feature :one, :two do + # # these specs will run if features :one AND + # # :two are enabled. + # end + # + # The implementation must explicitly enable a feature + # by adding code like the following to the .mspec + # configuration file: + # + # MSpec.enable_feature :encoding + # + def with_feature(*features, &block) + FeatureGuard.new(*features).run_if(:with_feature, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/guard.rb b/spec/mspec/lib/mspec/guards/guard.rb new file mode 100644 index 0000000000..c95d8f7923 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/guard.rb @@ -0,0 +1,118 @@ +require 'mspec/runner/mspec' +require 'mspec/runner/actions/tally' +require 'mspec/utils/ruby_name' + +class SpecGuard + def self.report + @report ||= Hash.new { |h,k| h[k] = [] } + end + + def self.clear + @report = nil + end + + def self.finish + report.keys.sort.each do |key| + desc = report[key] + size = desc.size + spec = size == 1 ? "spec" : "specs" + print "\n\n#{size} #{spec} omitted by guard: #{key}:\n" + desc.each { |description| print "\n", description; } + end + + print "\n\n" + end + + def self.guards + @guards ||= [] + end + + def self.clear_guards + @guards = [] + end + + # Returns a partial Ruby version string based on +which+. + # For example, if RUBY_VERSION = 8.2.3: + # + # :major => "8" + # :minor => "8.2" + # :tiny => "8.2.3" + # :teeny => "8.2.3" + # :full => "8.2.3" + def self.ruby_version(which = :minor) + case which + when :major + n = 1 + when :minor + n = 2 + when :tiny, :teeny, :full + n = 3 + end + + RUBY_VERSION.split('.')[0,n].join('.') + end + + attr_accessor :name + + def initialize(*args) + @parameters = args + end + + def yield?(invert = false) + return true if MSpec.mode? :unguarded + + allow = match? ^ invert + + if !allow and reporting? + MSpec.guard + MSpec.register :finish, SpecGuard + MSpec.register :add, self + return true + elsif MSpec.mode? :verify + return true + end + + allow + end + + def run_if(name, &block) + @name = name + yield if yield?(false) + ensure + unregister + end + + def run_unless(name, &block) + @name = name + yield if yield?(true) + ensure + unregister + end + + def reporting? + MSpec.mode?(:report) or + (MSpec.mode?(:report_on) and SpecGuard.guards.include?(name)) + end + + def report_key + "#{name} #{@parameters.join(", ")}" + end + + def record(description) + SpecGuard.report[report_key] << description + end + + def add(example) + record example.description + MSpec.retrieve(:formatter).tally.counter.guards! + end + + def unregister + MSpec.unguard + MSpec.unregister :add, self + end + + def match? + raise "must be implemented by the subclass" + end +end diff --git a/spec/mspec/lib/mspec/guards/platform.rb b/spec/mspec/lib/mspec/guards/platform.rb new file mode 100644 index 0000000000..875aef6c9c --- /dev/null +++ b/spec/mspec/lib/mspec/guards/platform.rb @@ -0,0 +1,78 @@ +require 'mspec/guards/guard' + +class PlatformGuard < SpecGuard + def self.implementation?(*args) + args.any? do |name| + case name + when :rubinius + RUBY_NAME.start_with?('rbx') + when :ruby, :jruby, :truffleruby, :ironruby, :macruby, :maglev, :topaz, :opal + RUBY_NAME.start_with?(name.to_s) + else + raise "unknown implementation #{name}" + end + end + end + + def self.standard? + implementation? :ruby + end + + HOST_OS = begin + require 'rbconfig' + RbConfig::CONFIG['host_os'] || RUBY_PLATFORM + rescue LoadError + RUBY_PLATFORM + end.downcase + + def self.os?(*oses) + oses.any? do |os| + raise ":java is not a valid OS" if os == :java + if os == :windows + HOST_OS =~ /(mswin|mingw)/ + else + HOST_OS.include?(os.to_s) + end + end + end + + def self.windows? + os?(:windows) + end + + def self.wordsize?(size) + size == 8 * 1.size + end + + def initialize(*args) + if args.last.is_a?(Hash) + @options, @platforms = args.last, args[0..-2] + else + @options, @platforms = {}, args + end + @parameters = args + end + + def match? + match = @platforms.empty? ? true : PlatformGuard.os?(*@platforms) + @options.each do |key, value| + case key + when :os + match &&= PlatformGuard.os?(*value) + when :wordsize + match &&= PlatformGuard.wordsize? value + end + end + match + end +end + +class Object + def platform_is(*args, &block) + PlatformGuard.new(*args).run_if(:platform_is, &block) + end + + def platform_is_not(*args, &block) + PlatformGuard.new(*args).run_unless(:platform_is_not, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/quarantine.rb b/spec/mspec/lib/mspec/guards/quarantine.rb new file mode 100644 index 0000000000..4724613a0f --- /dev/null +++ b/spec/mspec/lib/mspec/guards/quarantine.rb @@ -0,0 +1,13 @@ +require 'mspec/guards/guard' + +class QuarantineGuard < SpecGuard + def match? + true + end +end + +class Object + def quarantine!(&block) + QuarantineGuard.new.run_unless(:quarantine!, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/superuser.rb b/spec/mspec/lib/mspec/guards/superuser.rb new file mode 100644 index 0000000000..6e447198a7 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/superuser.rb @@ -0,0 +1,17 @@ +require 'mspec/guards/guard' + +class SuperUserGuard < SpecGuard + def match? + Process.euid == 0 + end +end + +class Object + def as_superuser(&block) + SuperUserGuard.new.run_if(:as_superuser, &block) + end + + def as_user(&block) + SuperUserGuard.new.run_unless(:as_user, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/support.rb b/spec/mspec/lib/mspec/guards/support.rb new file mode 100644 index 0000000000..f1760ece2e --- /dev/null +++ b/spec/mspec/lib/mspec/guards/support.rb @@ -0,0 +1,16 @@ +require 'mspec/guards/platform' + +class SupportedGuard < SpecGuard + def match? + if @parameters.include? :ruby + raise Exception, "improper use of not_supported_on guard" + end + !PlatformGuard.standard? and PlatformGuard.implementation?(*@parameters) + end +end + +class Object + def not_supported_on(*args, &block) + SupportedGuard.new(*args).run_unless(:not_supported_on, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/version.rb b/spec/mspec/lib/mspec/guards/version.rb new file mode 100644 index 0000000000..110853e082 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/version.rb @@ -0,0 +1,39 @@ +require 'mspec/utils/deprecate' +require 'mspec/utils/version' +require 'mspec/guards/guard' + +class VersionGuard < SpecGuard + FULL_RUBY_VERSION = SpecVersion.new SpecGuard.ruby_version(:full) + + def initialize(version) + case version + when String + @version = SpecVersion.new version + when Range + MSpec.deprecate "an empty version range end", 'a specific version' if version.end.empty? + a = SpecVersion.new version.begin + b = SpecVersion.new version.end + unless version.exclude_end? + MSpec.deprecate "ruby_version_is with an inclusive range", 'an exclusive range ("2.1"..."2.3")' + end + @version = version.exclude_end? ? a...b : a..b + else + raise "version must be a String or Range but was a #{version.class}" + end + @parameters = [version] + end + + def match? + if Range === @version + @version.include? FULL_RUBY_VERSION + else + FULL_RUBY_VERSION >= @version + end + end +end + +class Object + def ruby_version_is(*args, &block) + VersionGuard.new(*args).run_if(:ruby_version_is, &block) + end +end |