summaryrefslogtreecommitdiff
path: root/lib/minitest/spec.rb
blob: e730e205e86809b5df4495beeb6e61922b9f1c49 (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
############################################################
# This file is imported from a different project.
# DO NOT make modifications in this repo.
# File a patch instead and assign it to Ryan Davis
############################################################

#!/usr/bin/ruby -w

require 'minitest/unit'

class Module
  def infect_an_assertion meth, new_name, dont_flip = false
    # warn "%-22p -> %p %p" % [meth, new_name, dont_flip]
    self.class_eval <<-EOM
      def #{new_name} *args, &block
        return MiniTest::Spec.current.#{meth}(*args, &self) if
          Proc === self
        return MiniTest::Spec.current.#{meth}(args.first, self) if
          args.size == 1 unless #{!!dont_flip}
        return MiniTest::Spec.current.#{meth}(self, *args)
      end
    EOM
  end

  def infect_with_assertions(pos_prefix, neg_prefix,
                             skip_re,
                             dont_flip_re = /\c0/,
                             map = {})
    MiniTest::Assertions.public_instance_methods(false).each do |meth|
      meth = meth.to_s

      new_name = case meth
                 when /^assert/ then
                   meth.sub(/^assert/, pos_prefix.to_s)
                 when /^refute/ then
                   meth.sub(/^refute/, neg_prefix.to_s)
                 end
      next unless new_name
      next if new_name =~ skip_re

      regexp, replacement = map.find { |re, _| new_name =~ re }
      new_name.sub! regexp, replacement if replacement

      infect_an_assertion meth, new_name, new_name =~ dont_flip_re
    end
  end
end

Object.infect_with_assertions(:must, :wont,
                              /^(must|wont)$|wont_(throw)|
                                 must_(block|not?_|nothing|raise$)/x,
                              /(must|wont)_(include|respond_to)/,
                              /(must_throw)s/                 => '\1',
                              /(?!not)_same/                  => '_be_same_as',
                              /_in_/                          => '_be_within_',
                              /_operator/                     => '_be',
                              /_includes/                     => '_include',
                              /(must|wont)_(.*_of|nil|empty)/ => '\1_be_\2',
                              /must_raises/                   => 'must_raise')

class Object
  alias :must_be_close_to :must_be_within_delta
  alias :wont_be_close_to :wont_be_within_delta
end

module Kernel
  def describe desc, &block
    stack = MiniTest::Spec.describe_stack
    name  = desc.to_s.split(/\W+/).map { |s| s.capitalize }.join + "Spec"
    prev  = stack.last
    name  = "#{prev == MiniTest::Spec ? nil : prev}::#{name}"
    cls   = Object.class_eval "class #{name} < #{prev}; end; #{name}"

    cls.nuke_test_methods!

    stack.push cls
    cls.class_eval(&block)
    stack.pop
  end
  private :describe
end

class MiniTest::Spec < MiniTest::Unit::TestCase
  @@describe_stack = [MiniTest::Spec]
  def self.describe_stack
    @@describe_stack
  end

  def self.current
    @@current_spec
  end

  def initialize name
    super
    @@current_spec = self
  end

  def self.nuke_test_methods!
    self.public_instance_methods.grep(/^test_/).each do |name|
      self.send :undef_method, name
    end
  end

  def self.define_inheritable_method name, &block
    super_method = self.superclass.instance_method name

    define_method name do
      super_method.bind(self).call if super_method # regular super() warns
      instance_eval(&block)
    end
  end

  def self.before(type = :each, &block)
    if type == :all
      warn "change before :all to before :each"
      type = :each
    end
    raise "unsupported before type: #{type}" unless type == :each
    define_inheritable_method :setup, &block
  end

  def self.after(type = :each, &block)
    if type == :all # REFACTOR
      warn "change before :all to before :each"
      type = :each
    end
    raise "unsupported after type: #{type}" unless type == :each
    define_inheritable_method :teardown, &block
  end

  def self.it desc, &block
    define_method "test_#{desc.gsub(/\W+/, '_').downcase}", &block
  end
end