class MotionSpec::Specification

Constants

MULTIPLE_POSTPONES_ERROR_MESSAGE

Attributes

description[R]

Public Class Methods

new(context, description, block, before_filters, after_filters) click to toggle source
# File lib/motion-spec/specification.rb, line 8
def initialize(context, description, block, before_filters, after_filters)
  @context = context
  @description = description
  @block = block
  @before_filters = before_filters.dup
  @after_filters = after_filters.dup

  @postponed_blocks_count = 0
  @ran_spec_block = false
  @ran_after_filters = false
  @exception_occurred = false
  @error = ''
end

Public Instance Methods

cancel_scheduled_requests!() click to toggle source
# File lib/motion-spec/specification.rb, line 166
def cancel_scheduled_requests!
  unless Platform.android?
    NSObject.cancelPreviousPerformRequestsWithTarget(@context)
    NSObject.cancelPreviousPerformRequestsWithTarget(self)
  end
end
execute_block() { || ... } click to toggle source
# File lib/motion-spec/specification.rb, line 180
def execute_block
  yield
rescue Object => e
  @exception_occurred = true

  if e.is_a?(Exception)
    ErrorLog << "#{e.class}: #{e.message}\n"
    lines = $DEBUG ? e.backtrace : e.backtrace.find_all { |line| line !~ /bin\/macbacon|\/mac_bacon\.rb:\d+/ }
    lines.each_with_index do |line, i|
      ErrorLog << "\t#{line}#{i == 0 ? ": #{@context.name} - #{@description}" : ''}\n"
    end
    ErrorLog << "\n"
  else
    if defined?(NSException)
      # Pure NSException.
      ErrorLog << "#{e.name}: #{e.reason}\n"
    else
      # Pure Java exception.
      ErrorLog << "#{e.class.toString} : #{e.getMessage}"
    end
  end

  @error =
    if e.is_a? Error
      Counter[e.count_as] += 1
      "#{e.count_as.to_s.upcase} - #{e}"
    else
      Counter[:errors] += 1
      "ERROR: #{e.class} - #{e}"
    end
end
exit_spec() click to toggle source
# File lib/motion-spec/specification.rb, line 173
def exit_spec
  cancel_scheduled_requests!
  Counter[:depth] -= 1
  MotionSpec.handle_requirement_end(@error)
  @context.specification_did_finish(self)
end
finish_spec() click to toggle source
# File lib/motion-spec/specification.rb, line 157
def finish_spec
  if !@exception_occurred && Counter[:requirements] == @number_of_requirements_before
    # the specification did not contain any requirements, so it flunked
    execute_block { fail Error.new(:missing, "empty specification: #{@context.name} #{@description}") }
  end
  run_after_filters
  exit_spec unless postponed?
end
observeValueForKeyPath(_key_path, ofObject:object, change:_, context:__) click to toggle source

rubocop:disable Lint/UnusedMethodArgument

# File lib/motion-spec/specification.rb, line 106
def observeValueForKeyPath(_key_path, ofObject:object, change:_, context:__)
  resume
end
postpone_block(timeout = 1, &block) click to toggle source
# File lib/motion-spec/specification.rb, line 67
def postpone_block(timeout = 1, &block)
  # If an exception occurred, we definitely don't need to schedule any more blocks
  return if @exception_occurred
  fail MULTIPLE_POSTPONES_ERROR_MESSAGE if @postponed_block

  @postponed_blocks_count += 1
  @postponed_block = block

  return performSelector(
    'postponed_block_timeout_exceeded',
    withObject: nil,
    afterDelay: timeout
  ) unless Platform.android?

  sleep timeout
  postponed_block_timeout_exceeded
end
postpone_block_until_change(object_to_observe, key_path, timeout = 1, &block) click to toggle source
# File lib/motion-spec/specification.rb, line 85
def postpone_block_until_change(object_to_observe, key_path, timeout = 1, &block)
  # If an exception occurred, we definitely don't need to schedule any more blocks
  return if @exception_occurred
  fail MULTIPLE_POSTPONES_ERROR_MESSAGE if @postponed_block

  @postponed_blocks_count += 1
  @postponed_block = block
  @observed_object_and_key_path = [object_to_observe, key_path]
  object_to_observe.addObserver(self, forKeyPath: key_path, options: 0, context: nil)

  return performSelector(
    'postponed_change_block_timeout_exceeded',
    withObject: nil,
    afterDelay: timeout
  ) unless Platform.android?

  sleep timeout
  postponed_change_block_timeout_exceeded
end
postponed?() click to toggle source
# File lib/motion-spec/specification.rb, line 22
def postponed?
  @postponed_blocks_count != 0
end
postponed_block_timeout_exceeded() click to toggle source
# File lib/motion-spec/specification.rb, line 124
def postponed_block_timeout_exceeded
  cancel_scheduled_requests!
  execute_block { fail Error.new(:failed, "timeout exceeded: #{@context.name} - #{@description}") }
  @postponed_blocks_count = 0
  finish_spec
end
postponed_change_block_timeout_exceeded() click to toggle source

rubocop:enable Lint/UnusedMethodArgument

# File lib/motion-spec/specification.rb, line 111
def postponed_change_block_timeout_exceeded
  remove_observer!
  postponed_block_timeout_exceeded
end
remove_observer!() click to toggle source
# File lib/motion-spec/specification.rb, line 116
def remove_observer!
  if @observed_object_and_key_path
    object, key_path = @observed_object_and_key_path
    object.removeObserver(self, forKeyPath: key_path)
    @observed_object_and_key_path = nil
  end
end
resume() click to toggle source
# File lib/motion-spec/specification.rb, line 131
def resume
  unless Platform.android?
    NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: 'postponed_block_timeout_exceeded', object: nil)
    NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: 'postponed_change_block_timeout_exceeded', object: nil)
  end
  remove_observer!
  block = @postponed_block
  @postponed_block = nil
  run_postponed_block(block)
end
run() click to toggle source
# File lib/motion-spec/specification.rb, line 46
def run
  MotionSpec.handle_requirement_begin(@description)
  Counter[:depth] += 1
  run_before_filters
  @number_of_requirements_before = Counter[:requirements]
  run_spec_block unless postponed?
end
run_after_filters() click to toggle source
# File lib/motion-spec/specification.rb, line 39
def run_after_filters
  @ran_after_filters = true
  execute_block { @after_filters.each { |f| @context.instance_eval(&f) } }
  Mocks.clear!
  Stubs.clear!
end
run_before_filters() click to toggle source
# File lib/motion-spec/specification.rb, line 26
def run_before_filters
  execute_block { @before_filters.each { |f| @context.instance_eval(&f) } }
end
run_postponed_block(block) click to toggle source
# File lib/motion-spec/specification.rb, line 142
def run_postponed_block(block)
  # If an exception occurred, we definitely don't need execute any more blocks
  execute_block(&block) unless @exception_occurred
  @postponed_blocks_count -= 1
  unless postponed?
    if @ran_after_filters
      exit_spec
    elsif @ran_spec_block
      finish_spec
    else
      run_spec_block
    end
  end
end
run_spec_block() click to toggle source
# File lib/motion-spec/specification.rb, line 30
def run_spec_block
  @ran_spec_block = true
  # If an exception occurred, we definitely don't need to perform the actual spec anymore
  unless @exception_occurred
    execute_block { @context.instance_eval(&@block) }
  end
  finish_spec unless postponed?
end
schedule_block(seconds, &block) click to toggle source
# File lib/motion-spec/specification.rb, line 54
def schedule_block(seconds, &block)
  # If an exception occurred, we definitely don't need to schedule any more blocks
  return if @exception_occurred

  @postponed_blocks_count += 1
  if Platform.android?
    sleep seconds
    run_postponed_block(block)
  else
    performSelector('run_postponed_block:', withObject: block, afterDelay: seconds)
  end
end