summaryrefslogtreecommitdiff
path: root/spec/mspec/lib/mspec/guards
diff options
context:
space:
mode:
Diffstat (limited to 'spec/mspec/lib/mspec/guards')
-rw-r--r--spec/mspec/lib/mspec/guards/block_device.rb18
-rw-r--r--spec/mspec/lib/mspec/guards/bug.rb30
-rw-r--r--spec/mspec/lib/mspec/guards/conflict.rb19
-rw-r--r--spec/mspec/lib/mspec/guards/endian.rb27
-rw-r--r--spec/mspec/lib/mspec/guards/feature.rb43
-rw-r--r--spec/mspec/lib/mspec/guards/guard.rb118
-rw-r--r--spec/mspec/lib/mspec/guards/platform.rb78
-rw-r--r--spec/mspec/lib/mspec/guards/quarantine.rb13
-rw-r--r--spec/mspec/lib/mspec/guards/superuser.rb17
-rw-r--r--spec/mspec/lib/mspec/guards/support.rb16
-rw-r--r--spec/mspec/lib/mspec/guards/version.rb39
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