class CCK::MessagesComparator

def compare(detected, expected)

def compare(detected, expected)
  detected_by_type = messages_by_type(detected)
  expected_by_type = messages_by_type(expected)
  detected_by_type.each_key do |type|
    compare_list(detected_by_type[type], expected_by_type[type])
  rescue StandardError => e
    @all_errors << "Error while comparing #{type}: #{e.message}"
  end
end

def compare_list(detected, expected)

def compare_list(detected, expected)
  detected.each_with_index do |message, index|
    compare_message(message, expected[index])
  end
end

def compare_message(detected, expected)

def compare_message(detected, expected)
  return if not_message?(detected)
  return if ignorable?(detected)
  return if incomparable?(detected)
  @all_errors << @validator.compare(detected, expected)
  @compared << detected.class.name
  compare_sub_messages(detected, expected)
end

def compare_sub_messages(detected, expected)

def compare_sub_messages(detected, expected)
  return unless expected.respond_to? :to_h
  expected.to_h.each_key do |key|
    value = expected.send(key)
    if value.is_a?(Array)
      compare_list(detected.send(key), value)
    else
      compare_message(detected.send(key), value)
    end
  end
end

def errors

def errors
  @all_errors.flatten
end

def ignorable?(detected)

These messages we need to ignore because they are too large or they feature timestamps which always vary
def ignorable?(detected)
  too_large_message?(detected) || time_message?(detected)
end

def incomparable?(detected)

These messages we need to ignore because they are often not of identical shape/value
def incomparable?(detected)
  detected.is_a?(Cucumber::Messages::Ci) || detected.is_a?(Cucumber::Messages::Git)
end

def initialize(validator, detected, expected)

def initialize(validator, detected, expected)
  @all_errors = []
  @compared = []
  @validator = validator
  compare(detected, expected)
end

def messages_by_type(messages)

def messages_by_type(messages)
  by_type = Hash.new { |h, k| h[k] = [] }
  messages.each do |msg|
    by_type[message_type(msg)] << remove_envelope(msg)
  end
  by_type
end

def not_message?(detected)

def not_message?(detected)
  !detected.is_a?(Cucumber::Messages::Message)
end

def remove_envelope(message)

def remove_envelope(message)
  message.send(message_type(message))
end

def time_message?(detected)

def time_message?(detected)
  detected.is_a?(Cucumber::Messages::Timestamp) || detected.is_a?(Cucumber::Messages::Duration)
end

def too_large_message?(detected)

def too_large_message?(detected)
  detected.is_a?(Cucumber::Messages::GherkinDocument) || detected.is_a?(Cucumber::Messages::Pickle)
end