summaryrefslogtreecommitdiff
path: root/spec/mspec/lib/mspec/guards/guard.rb
blob: 322a08145d39c95724c26f69735364433110f030 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
require 'mspec/runner/mspec'
require 'mspec/runner/actions/tally'

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
    if block
      yield if yield?(false)
    else
      yield?(false)
    end
  ensure
    unregister
  end

  def run_unless(name, &block)
    @name = name
    if block
      yield if yield?(true)
    else
      yield?(true)
    end
  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

# Combined guards

def guard(condition, &block)
  raise "condition must be a Proc" unless condition.is_a?(Proc)
  raise LocalJumpError, "no block given" unless block
  return yield if MSpec.mode? :unguarded or MSpec.mode? :verify or MSpec.mode? :report
  yield if condition.call
end

def guard_not(condition, &block)
  raise "condition must be a Proc" unless condition.is_a?(Proc)
  raise LocalJumpError, "no block given" unless block
  return yield if MSpec.mode? :unguarded or MSpec.mode? :verify or MSpec.mode? :report
  yield unless condition.call
end