Class: Megatest::Runtime

Inherits:
Object
  • Object
show all
Defined in:
lib/megatest/runtime.rb

Defined Under Namespace

Classes: Expression

Constant Summary collapse

EMPTY_BACKTRACE =
[].freeze
UNSET =
BasicObject.new

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config, test_case, result) ⇒ Runtime

Returns a new instance of Runtime.



9
10
11
12
13
14
15
# File 'lib/megatest/runtime.rb', line 9

def initialize(config, test_case, result)
  @config = config
  @test_case = test_case
  @result = result
  @asserting = false
  @on_teardown = []
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



7
8
9
# File 'lib/megatest/runtime.rb', line 7

def config
  @config
end

#on_teardownObject (readonly)

Returns the value of attribute on_teardown.



7
8
9
# File 'lib/megatest/runtime.rb', line 7

def on_teardown
  @on_teardown
end

#resultObject (readonly)

Returns the value of attribute result.



7
8
9
# File 'lib/megatest/runtime.rb', line 7

def result
  @result
end

#test_caseObject (readonly)

Returns the value of attribute test_case.



7
8
9
# File 'lib/megatest/runtime.rb', line 7

def test_case
  @test_case
end

Instance Method Details

#assert(uplevel: 1) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/megatest/runtime.rb', line 27

def assert(uplevel: 1)
  if @asserting
    yield
  else
    @asserting = true
    @result.assertions_count += 1
    begin
      yield
    rescue Assertion => failure
      if failure.backtrace.empty?
        failure.set_backtrace(caller_locations(uplevel + 2))
      end
      raise
    ensure
      @asserting = false
    end
  end
end

#build_message(strings) ⇒ Object



132
133
134
135
136
137
138
139
140
# File 'lib/megatest/runtime.rb', line 132

def build_message(strings)
  return if strings.empty?

  if (strings.size + strings.sum(&:size)) < 80
    strings.join(" ")
  else
    strings.join("\n\n")
  end
end

#diff(expected, actual) ⇒ Object



240
241
242
# File 'lib/megatest/runtime.rb', line 240

def diff(expected, actual)
  @config.diff(expected, actual)
end

#expect_no_failuresObject



105
106
107
108
109
110
111
112
113
114
115
# File 'lib/megatest/runtime.rb', line 105

def expect_no_failures
  was_asserting = @asserting
  @asserting = false
  yield
rescue Assertion, *Megatest::IGNORED_ERRORS
  raise # Exceptions we shouldn't rescue
rescue Exception => unexpected_error
  raise UnexpectedError, unexpected_error, EMPTY_BACKTRACE
ensure
  @asserting = was_asserting
end

#expression(expression, block) ⇒ Object



177
178
179
180
181
182
183
# File 'lib/megatest/runtime.rb', line 177

def expression(expression, block)
  if String === expression
    Expression.new(expression, block)
  else
    expression
  end
end

#fail(user_message, *message) ⇒ Object

Raises:



119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/megatest/runtime.rb', line 119

def fail(user_message, *message)
  message = build_message(message)
  if user_message
    user_message = user_message.call if user_message.respond_to?(:call)
    user_message = String(user_message)
    if message && !user_message.end_with?("\n")
      user_message += "\n"
    end
    message = "#{user_message}#{message}"
  end
  raise(Assertion, message, EMPTY_BACKTRACE)
end

#minitest_compatibility?Boolean

Returns:

  • (Boolean)


185
186
187
# File 'lib/megatest/runtime.rb', line 185

def minitest_compatibility?
  @config.minitest_compatibility
end

#msg(positional, keyword) ⇒ Object



95
96
97
98
99
100
101
102
103
# File 'lib/megatest/runtime.rb', line 95

def msg(positional, keyword)
  if positional.nil?
    keyword
  elsif !keyword.nil?
    raise ArgumentError, "Can't pass both a positional and keyword assertion message"
  else
    positional # TODO: deprecation mechanism
  end
end

#pp(object) ⇒ Object



189
190
191
# File 'lib/megatest/runtime.rb', line 189

def pp(object)
  @config.render_object(object)
end

#pp_expression(callable) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/megatest/runtime.rb', line 193

def pp_expression(callable)
  case callable
  when Expression
    callable.string
  when Proc
    # Logic borrowed from Active Support.
    if defined?(RubyVM::InstructionSequence)
      iseq = RubyVM::InstructionSequence.of(callable)
      return pp(callable) unless iseq

      source = if RubyVM::InstructionSequence.method_defined?(:script_lines) && iseq.script_lines
        iseq.script_lines.join("\n")
      elsif File.readable?(iseq.absolute_path)
        File.read(iseq.absolute_path)
      end
      return pp(callable) unless source

      location = iseq.to_a[4][:code_location]
      return pp(callable) unless location

      lines = source.lines[(location[0] - 1)..(location[2] - 1)]
      lines[-1] = lines[-1].byteslice(0...location[3])
      lines[0] = lines[0].byteslice(location[1]...-1)
      source = lines.join.strip

      # Ruby 4.1.0dev includes the `->`
      source.delete_prefix!("->")
      source.strip!

      # We ignore procs defined with do/end as they are likely multi-line anyway.
      if source.start_with?("{")
        source.delete_suffix!("}")
        source.delete_prefix!("{")
        source.strip!
        # It won't read nice if the callable contains multiple
        # lines, and it should be a rare occurrence anyway.
        # Same if it takes arguments.
        if !source.include?("\n") && !source.start_with?("|")
          return source
        end
      end
    end
  end

  pp(callable)
end

#record_failures(downlevel: 1, &block) ⇒ Object



252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/megatest/runtime.rb', line 252

def record_failures(downlevel: 1, &block)
  expect_no_failures(&block)
rescue Assertion => assertion
  error = assertion
  while error
    error = strip_backtrace(error, __FILE__, __LINE__ - 4, downlevel + 2)
    error = error.cause
  end

  @result.failures << Failure.new(assertion)
  true
else
  false
end

#safe_yieldObject



142
143
144
145
146
147
148
# File 'lib/megatest/runtime.rb', line 142

def safe_yield
  yield
rescue Assertion, *IGNORED_ERRORS
  raise
rescue ::Exception => unexepected_exception
  raise UnexpectedError, unexepected_exception
end

#set?(arg) ⇒ Boolean

Returns:

  • (Boolean)


160
161
162
# File 'lib/megatest/runtime.rb', line 160

def set?(arg)
  !UNSET.equal?(arg)
end

#strip_backtrace(error, yield_file, yield_line, downlevel) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/megatest/runtime.rb', line 46

def strip_backtrace(error, yield_file, yield_line, downlevel)
  if backtrace = error.backtrace_locations
    rindex = backtrace.rindex { |l| l.lineno == yield_line && l.path == yield_file }
    backtrace = backtrace.slice(0..rindex)
    backtrace.pop(downlevel) unless downlevel.zero?
    error.set_backtrace(backtrace)
  elsif backtrace = error.backtrace
    yield_point = "#{yield_file}:#{yield_line}:"
    rindex = backtrace.rindex { |l| l.start_with?(yield_point) }
    backtrace = backtrace.slice(0..rindex)
    backtrace.pop(downlevel) unless downlevel.zero?
    error.set_backtrace(backtrace)
  end

  error
end

#teardownObject



244
245
246
247
248
249
250
# File 'lib/megatest/runtime.rb', line 244

def teardown
  until @on_teardown.empty?
    record_failures do
      @on_teardown.pop.call
    end
  end
end

#unsetObject



152
153
154
# File 'lib/megatest/runtime.rb', line 152

def unset
  UNSET
end

#unset?(arg) ⇒ Boolean

Returns:

  • (Boolean)


156
157
158
# File 'lib/megatest/runtime.rb', line 156

def unset?(arg)
  UNSET.equal?(arg)
end