module RBS::UnitTest::TypeAssertions

Public Class Methods

included(base) click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 57
def self.included(base)
  base.extend ClassMethods
end

Public Instance Methods

allow_non_simple_method_type() { || ... } click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 314
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) { || ... } click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 246
def allows_error(*errors)
  yield
rescue *errors => exn
  notify "Error allowed: #{exn.inspect}"
end
assert_const_type(type, constant_name) click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 252
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(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_send_type(method_type, receiver, method, *args, &block) click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 171
               def assert_send_type(method_type, receiver, method, *args, &block)
  send_setup(method_type, receiver, method, args, block) do |method_type, trace, result, exception|
    typecheck = RBS::Test::TypeCheck.new(
      self_class: receiver.class,
      builder: builder,
      sample_size: 100,
      unchecked_classes: [],
      instance_class: instance_class,
      class_class: class_class
    )
    errors = typecheck.method_call(method, method_type, trace, errors: [])

    assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }, "Call trace does not match with given method type: #{trace.inspect}"

    method_types = method_types(method)
    all_errors = method_types.map {|t| typecheck.method_call(method, t, trace, errors: []) }
    assert all_errors.any? {|es| es.empty? }, "Call trace does not match one of method definitions:\n  #{trace.inspect}\n  #{method_types.join(" | ")}"

    raise exception if exception

    result
  end
end
assert_type(type, value) click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 293
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) click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 331
def break_from_block(value = nil)
  raise "Cannot break without `@break_tag`" unless @break_tag
  throw @break_tag, value
end
builder() click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 65
def builder
  (_ = self.class).builder
end
class_class() click to toggle source
# File rbs-3.4.0/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
env() click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 61
def env
  (_ = self.class).env
end
instance_class() click to toggle source
# File rbs-3.4.0/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_types(method) click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 230
def method_types(method)
  type, definition = target

  case type
  when Types::ClassInstance
    subst = RBS::Substitution.build(definition.type_params, type.args)
    definition.methods[method].method_types.map do |method_type|
      method_type.sub(subst)
    end
  when Types::ClassSingleton
    definition.methods[method].method_types
  else
    raise
  end
end
pass(message = nil) click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 336
def pass(message = nil)
  assert true, message
end
refute_send_type(method_type, receiver, method, *args, &block) click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 195
               def refute_send_type(method_type, receiver, method, *args, &block)
  send_setup(method_type, receiver, method, args, block) do |method_type, trace, result, exception|
    method_type = method_type.update(
      block:
        if method_type.block
          RBS::Types::Block.new(
            type: method_type.block.type.with_return_type(RBS::Types::Bases::Any.new(location: nil)),
            required: method_type.block.required,
            self_type: nil
          )
        end,
      type: method_type.type.with_return_type(RBS::Types::Bases::Any.new(location: nil))
    )

    typecheck = RBS::Test::TypeCheck.new(
      self_class: receiver.class,
      instance_class: instance_class,
      class_class: class_class,
      builder: builder,
      sample_size: 100,
      unchecked_classes: []
    )
    errors = typecheck.method_call(method, method_type, trace, errors: [])

    assert_operator exception, :is_a?, ::Exception
    assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }

    method_types = method_types(method)
    all_errors = method_types.map {|t| typecheck.method_call(method, t, trace, errors: []) }
    assert all_errors.all? {|es| es.size > 0 }, "Call trace unexpectedly matches one of method definitions:\n  #{trace.inspect}\n  #{method_types.join(" | ")}"

    result
  end
end
send_setup(method_type, receiver, method, args, proc) { |mt, last_trace, result, exception| ... } click to toggle source
# File rbs-3.4.0/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

  yield(mt, last_trace, result, exception)
end
target() click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 73
def target
  targets.last || (_ = self.class).target
end
targets() click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 69
def targets
  @targets ||= []
end
testing(type_or_string) { || ... } click to toggle source
# File rbs-3.4.0/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) click to toggle source
# File rbs-3.4.0/lib/rbs/unit_test/type_assertions.rb, line 323
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