File: shindo.rb

package info (click to toggle)
ruby-shindo 0.3.11-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 224 kB
  • sloc: ruby: 456; makefile: 14
file content (216 lines) | stat: -rw-r--r-- 6,231 bytes parent folder | download
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
require 'formatador'

module Shindo

  class Pending < StandardError; end

  unless const_defined?(:VERSION)
    VERSION = '0.3.11'
  end

  def self.tests(description = nil, tags = [], &block)
    STDOUT.sync = true
    Shindo::Tests.new(description, tags, &block)
  end

  class Tests

    def initialize(description, tags = [], &block)
      @afters     = []
      @befores    = []
      @description_stack  = []
      @tag_stack          = []
      Thread.current[:reload] = false
      Thread.current[:tags] ||= []
      Thread.current[:totals] ||= { :failed => 0, :errored => 0, :pending => 0, :skipped => 0, :succeeded => 0 }
      @if_tagged = []
      @unless_tagged = []
      for tag in Thread.current[:tags]
        case tag[0...1]
        when '+'
          @if_tagged << tag[1..-1]
        when '-'
          @unless_tagged << tag[1..-1]
        end
      end
      @pending = nil
      Formatador.display_line
      tests(description, tags, &block)
    end

    def after(&block)
      @afters.last.push(block)
    end

    def before(&block)
      @befores.last.push(block)
    end

    def pending
      raise(Shindo::Pending.new)
    end

    def tests(description, tags = [], &block)
      return self if Thread.main[:exit] || Thread.current[:reload]

      tags = [*tags].collect { |tag| tag.to_s }
      @tag_stack.push(tags)
      @befores.push([])
      @afters.push([])

      @description = nil
      @inline = false
      description ||= 'Shindo.tests'
      description = "[bold]#{description}[normal]"
      unless tags.empty?
        description << " (#{tags.join(', ')})"
      end
      @description_stack.push(description)

      # if the test includes +tags and discludes -tags, evaluate it
      if (@if_tagged.empty? || !(@if_tagged & @tag_stack.flatten).empty?) &&
          (@unless_tagged.empty? || (@unless_tagged & @tag_stack.flatten).empty?)
        if block_given?
          begin
            display_description(description)
            # HACK: increase indent
            indent = Thread.current[:formatador].instance_variable_get(:@indent)
            Thread.current[:formatador].instance_variable_set(:@indent, indent + 1)
            instance_eval(&block)
          rescue Shindo::Pending
            display_pending(description)
          rescue => error
            display_error(error)
          ensure
            # HACK: decrease indent
            indent = Thread.current[:formatador].instance_variable_get(:@indent)
            Thread.current[:formatador].instance_variable_set(:@indent, indent - 1)
          end
        else
          @inline = true
          display_description(description)
        end
      else
        display_description("[light_black]#{description}[/]")
      end

      @description_stack.pop
      @afters.pop
      @befores.pop
      @tag_stack.pop

      Thread.exit if Thread.main[:exit] || Thread.current[:reload]
      self
    end

    def raises(error, description = "raises #{error.inspect}", &block)
      assert(:raises, error, description, &block)
    end

    def returns(expectation, description = "returns #{expectation.inspect}", &block)
      assert(:returns, expectation, description, &block)
    end

    def test(description = 'returns true', &block)
      assert(:returns, true, description, &block)
    end

    private

    def assert(type, expectation, description, &block)
      return if Thread.main[:exit] || Thread.current[:reload]
      description = [@description, description].compact.join(' ')
      if block_given?
        begin
          for before in @befores.flatten.compact
            before.call
          end
          value, success = case type
          when :raises
            value = begin
              instance_eval(&block)
            rescue Shindo::Pending
              @pending = true
            rescue Exception => error
              error
            end
            [value, value.is_a?(expectation)]
          when :returns
            [value = instance_eval(&block), value == expectation]
          end
          for after in @afters.flatten.compact
            after.call
          end
        rescue Shindo::Pending
          @pending = true
        rescue => error
          success = false
          value = error
        end
        if @pending
          @pending = false
          display_pending(description)
        elsif success
          display_success(description)
        else
          display_failure(description)
          case value
          when Exception, Interrupt
            display_error(value)
          else
            @message ||= [
              "expected => #{expectation.inspect}",
              "returned => #{value.inspect}"
            ]
            Formatador.indent do
              Formatador.display_lines([*@message].map {|message| "[red]#{message}[/]"})
            end
            @message = nil
          end
          if Thread.current[:interactive] && STDOUT.tty?
            prompt(description, &block)
          end
        end
      else
        display_pending(description)
      end
      success
    end

    def prompt(description, &block)
      return if Thread.main[:exit] || Thread.current[:reload]
      Formatador.display("Action? [c,q,r,?]? ")
      choice = STDIN.gets.strip
      continue = false
      Formatador.display_line
      Formatador.indent do
        case choice
        when 'c', 'continue'
          continue = true
        when 'q', 'quit', 'exit'
          Formatador.display_line("Exiting...")
          Thread.main[:exit] = true
        when 'r', 'reload'
          Formatador.display_line("Reloading...")
          Thread.current[:reload] = true
        when '?', 'help'
          Formatador.display_lines([
            'c - ignore this error and continue',
            'q - quit Shindo',
            'r - reload and run the tests again',
            '? - display help'
          ])
        else
          Formatador.display_line("[red]#{choice} is not a valid choice, please try again.[/]")
        end
        Formatador.display_line
      end
      unless continue || Thread.main[:exit]
        Formatador.display_line("[red]- #{description}[/]")
        prompt(description, &block)
      end
    end

  end

end