summaryrefslogtreecommitdiffstats
path: root/test/lib/spec/runner/specification.rb
blob: de8d750fd0b0e6e0f9fe10988bcfd53d83043ac3 (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
module Spec
  module Runner
    class Specification

      class << self
        attr_accessor :current, :generated_description
        protected :current=

        callback_events :before_setup, :after_teardown
      end

      attr_reader :spec_block
      callback_events :before_setup, :after_teardown

      def initialize(name, opts={}, &spec_block)
        @from = caller(0)[3]
        @description = name
        @options = opts
        @spec_block = spec_block
        @description_generated_callback = lambda { |desc| @generated_description = desc }
      end

      def run(reporter, setup_block, teardown_block, dry_run, execution_context)
        reporter.spec_started(name) if reporter
        return reporter.spec_finished(name) if dry_run

        errors = []
        begin
          set_current
          setup_ok = setup_spec(execution_context, errors, &setup_block)
          spec_ok = execute_spec(execution_context, errors) if setup_ok
          teardown_ok = teardown_spec(execution_context, errors, &teardown_block)
        ensure
          clear_current
        end

        SpecShouldRaiseHandler.new(@from, @options).handle(errors)
        reporter.spec_finished(name, errors.first, failure_location(setup_ok, spec_ok, teardown_ok)) if reporter
      end
      
      def matches?(matcher, description)
        matcher.spec_desc = name
        matcher.matches?(description)
      end
      
      private
      def name
        @description == :__generate_description ? generated_description : @description
      end
      
      def generated_description
        @generated_description || "NAME NOT GENERATED"
      end
      
      def setup_spec(execution_context, errors, &setup_block)
        notify_before_setup(errors)
        execution_context.instance_eval(&setup_block) if setup_block
        return errors.empty?
      rescue => e
        errors << e
        return false
      end

      def execute_spec(execution_context, errors)
        begin
          execution_context.instance_eval(&spec_block)
          return true
        rescue Exception => e
          errors << e
          return false
        end
      end

      def teardown_spec(execution_context, errors, &teardown_block)
        execution_context.instance_eval(&teardown_block) if teardown_block
        notify_after_teardown(errors)
        return errors.empty?
      rescue => e
        errors << e
        return false
      end

      def notify_before_setup(errors)
        notify_class_callbacks(:before_setup, self, &append_errors(errors))
        notify_callbacks(:before_setup, self, &append_errors(errors))
      end
      
      def notify_after_teardown(errors)
        notify_callbacks(:after_teardown, self, &append_errors(errors))
        notify_class_callbacks(:after_teardown, self, &append_errors(errors))
      end
      
      def append_errors(errors)
        proc {|error| errors << error}
      end
      
      def set_current
        Spec::Matchers.description_generated(&@description_generated_callback)
        self.class.send(:current=, self)
      end

      def clear_current
        Spec::Matchers.unregister_callback(:description_generated, @description_generated_callback)
        self.class.send(:current=, nil)
      end

      def failure_location(setup_ok, spec_ok, teardown_ok)
        return 'setup' unless setup_ok
        return name unless spec_ok
        return 'teardown' unless teardown_ok
      end
    end
  end
end