Module: RBS::UnitTest::TypeAssertions

Defined in:
lib/rbs/unit_test/type_assertions.rb

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



57
58
59
# File 'lib/rbs/unit_test/type_assertions.rb', line 57

def self.included(base)
  base.extend ClassMethods
end

Instance Method Details

#allow_non_simple_method_typeObject



347
348
349
350
351
352
353
354
# File 'lib/rbs/unit_test/type_assertions.rb', line 347

def allow_non_simple_method_type()
  begin
    @allows_non_simple_method_type = true
    yield
  rescue
    @allows_non_simple_method_type = false
  end
end

#allows_error(*errors) ⇒ Object



279
280
281
282
283
# File 'lib/rbs/unit_test/type_assertions.rb', line 279

def allows_error(*errors)
  yield
rescue *errors => exn
  notify "Error allowed: #{exn.inspect}"
end

#assert_const_type(type, constant_name) ⇒ Object



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/rbs/unit_test/type_assertions.rb', line 285

def assert_const_type(type, constant_name)
  constant = Object.const_get(constant_name)

  typecheck = RBS::Test::TypeCheck.new(
    self_class: constant.class,
    instance_class: instance_class,
    class_class: class_class,
    builder: builder,
    sample_size: 100,
    unchecked_classes: []
  )

  value_type =
    case type
    when String
      RBS::Parser.parse_type(type, variables: []) || raise
    else
      type
    end

  assert typecheck.value(constant, value_type), "`#{constant_name}` (#{constant.inspect}) must be compatible with given type `#{value_type}`"

  type_name = TypeName.parse(constant_name).absolute!
  definition = env.constant_entry(type_name)
  assert definition, "Cannot find RBS type definition of `#{constant_name}`"

  case definition
  when RBS::Environment::ClassEntry, RBS::Environment::ModuleEntry
    definition_type = RBS::Types::ClassSingleton.new(name: type_name, location: nil)
  when RBS::Environment::ClassAliasEntry, RBS::Environment::ModuleAliasEntry
    type_name = env.normalize_type_name!(type_name)
    definition_type = RBS::Types::ClassSingleton.new(name: type_name, location: nil)
  when RBS::Environment::ConstantEntry
    definition_type = definition.decl.type
  end

  assert definition_type, "Cannot find RBS entry for `#{constant_name}`"
  definition_type or raise
  assert typecheck.value(constant, definition_type), "`#{constant_name}` (#{constant.inspect}) must be compatible with RBS type definition `#{definition_type}`"
end

#assert_type(type, value) ⇒ Object



326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/rbs/unit_test/type_assertions.rb', line 326

def assert_type(type, value)
  typecheck = RBS::Test::TypeCheck.new(
    self_class: value.class,
    instance_class: _ = "No `instance` class allowed",
    class_class: _ = "No `class` class allowed",
    builder: builder,
    sample_size: 100,
    unchecked_classes: []
  )

  type =
    case type
    when String
      RBS::Parser.parse_type(type, variables: []) or raise
    else
      type
    end

  assert typecheck.value(value, type), "`#{value.inspect}` must be compatible with given type `#{type}`"
end

#break_from_block(value = nil) ⇒ Object



364
365
366
367
# File 'lib/rbs/unit_test/type_assertions.rb', line 364

def break_from_block(value = nil)
  raise "Cannot break without `@break_tag`" unless @break_tag
  throw @break_tag, value
end

#builderObject



65
66
67
# File 'lib/rbs/unit_test/type_assertions.rb', line 65

def builder
  (_ = self.class).builder
end

#class_classObject



121
122
123
124
125
126
127
128
# File 'lib/rbs/unit_test/type_assertions.rb', line 121

def class_class
  type, _ = target

  case type
  when RBS::Types::ClassSingleton, RBS::Types::ClassInstance
    Object.const_get(type.name.to_s).singleton_class
  end
end

#envObject



61
62
63
# File 'lib/rbs/unit_test/type_assertions.rb', line 61

def env
  (_ = self.class).env
end

#instance_classObject



112
113
114
115
116
117
118
119
# File 'lib/rbs/unit_test/type_assertions.rb', line 112

def instance_class
  type, _ = target

  case type
  when RBS::Types::ClassSingleton, RBS::Types::ClassInstance
    Object.const_get(type.name.to_s)
  end
end

#method_defs(method) ⇒ Object



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/rbs/unit_test/type_assertions.rb', line 255

def method_defs(method)
  type, definition = target

  case type
  when Types::ClassInstance, Types::ClassSingleton
    if type.is_a?(Types::ClassSingleton) && type.args.empty?
      definition.methods[method].defs
    else
      subst = RBS::Substitution.build(definition.type_params, type.args)
      definition.methods[method].defs.map do |type_def|
        type_def.update(
          type: type_def.type.sub(subst)
        )
      end
    end
  else
    raise
  end
end

#method_types(method) ⇒ Object



275
276
277
# File 'lib/rbs/unit_test/type_assertions.rb', line 275

def method_types(method)
  method_defs(method).map(&:type)
end

#pass(message = nil) ⇒ Object



369
370
371
# File 'lib/rbs/unit_test/type_assertions.rb', line 369

def pass(message = nil)
  assert true, message
end

#send_setup(method_type, receiver, method, args, proc) {|mt, last_trace, result, exception| ... } ⇒ Object

Yields:

  • (mt, last_trace, result, exception)


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
# File 'lib/rbs/unit_test/type_assertions.rb', line 130

def send_setup(method_type, receiver, method, args, proc)
  mt =
    case method_type
    when String
      RBS::Parser.parse_method_type(method_type, variables: []) || raise
    when RBS::MethodType
      method_type
    end

  validate_simple_method_type(mt)

  trace = [] #: Array[Test::CallTrace]
  spy = Spy.wrap(receiver, method)
  spy.callback = -> (result) { trace << result }

  result = nil #: untyped
  exception = nil #: Exception?
  non_jump_exit = true

  begin
    result = catch do |tag|
      @break_tag = tag
      spy.wrapped_object.__send__(method, *args, &proc)
    ensure
      @break_tag = nil
    end

    non_jump_exit = false
  rescue Exception => exn
    exception = exn
  ensure
    if non_jump_exit && !exception
      raise "`break` nor `return` from blocks given to `assert_send_type` are prohibited. Use `#break_from_block` instead."
    end
  end

  last_trace = trace.last or raise "empty trace"

  yield(mt, last_trace, result, exception)
end

#targetObject



73
74
75
# File 'lib/rbs/unit_test/type_assertions.rb', line 73

def target
  targets.last || (_ = self.class).target
end

#targetsObject



69
70
71
# File 'lib/rbs/unit_test/type_assertions.rb', line 69

def targets
  @targets ||= []
end

#testing(type_or_string) ⇒ Object



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
# File 'lib/rbs/unit_test/type_assertions.rb', line 77

def testing(type_or_string)
  type = case type_or_string
         when String
           RBS::Parser.parse_type(type_or_string, variables: [])
         else
           type_or_string
         end

  definition = case type
               when RBS::Types::ClassInstance
                 builder.build_instance(type.name)
               when RBS::Types::ClassSingleton
                 builder.build_singleton(type.name)
               else
                 raise "Test target should be class instance or class singleton: #{type}"
               end

  targets.push(
    [
      type,  #: target_type
      definition
    ]
  )

  if block_given?
    begin
      yield
    ensure
      targets.pop
    end
  else
    [type, definition]
  end
end

#validate_simple_method_type(type) ⇒ Object



356
357
358
359
360
361
362
# File 'lib/rbs/unit_test/type_assertions.rb', line 356

def validate_simple_method_type(type)
  return if @allows_non_simple_method_type

  refute_predicate type, :has_self_type?, "`self` types is prohibited in method type: `#{type}`"
  refute_predicate type, :has_classish_type?, "`instance` and `class` types is prohibited in method type: `#{type}`"
  refute_predicate type, :with_nonreturn_void?, "`void` is only allowed at return type or generics parameters: `#{type}`"
end