class DeprecationTracker

A shitlist for deprecation warnings during test runs. It has two modes: “save” and “compare”

DEPRECATION_TRACKER=save Record deprecation warnings, grouped by spec file. After the test run, save to a file.

DEPRECATION_TRACKER=compare Tracks deprecation warnings, grouped by spec file. After the test run, compare against shitlist of expected deprecation warnings. If anything is added or removed, raise an error with a diff of the changes.

Constants

UnexpectedDeprecations

Attributes

bucket[R]
deprecation_messages[R]
shitlist_path[R]
transform_message[R]

Public Class Methods

new(shitlist_path, transform_message = nil) click to toggle source
# File lib/deprecation_tracker.rb, line 74
def initialize(shitlist_path, transform_message = nil)
  @shitlist_path = shitlist_path
  @transform_message = transform_message || -> (message) { message }
  @deprecation_messages = {}
end
track_rspec(rspec_config, opts = {}) click to toggle source
# File lib/deprecation_tracker.rb, line 42
def self.track_rspec(rspec_config, opts = {})
  shitlist_path = opts[:shitlist_path]
  mode = opts[:mode]
  transform_message = opts[:transform_message]
  deprecation_tracker = DeprecationTracker.new(shitlist_path, transform_message)
  if defined?(ActiveSupport)
    ActiveSupport::Deprecation.behavior << -> (message, _callstack, _deprecation_horizon, _gem_name) { deprecation_tracker.add(message) }
  end
  KernelWarnTracker.callbacks << -> (message) { deprecation_tracker.add(message) }

  rspec_config.around do |example|
    deprecation_tracker.bucket = example.metadata.fetch(:rerun_file_path)

    begin
      example.run
    ensure
      deprecation_tracker.bucket = nil
    end
  end

  rspec_config.after(:suite) do
    if mode == "save"
      deprecation_tracker.save
    elsif mode == "compare"
      deprecation_tracker.compare
    end
  end
end

Public Instance Methods

add(message) click to toggle source
# File lib/deprecation_tracker.rb, line 80
def add(message)
  return if bucket.nil?

  @deprecation_messages[bucket] << transform_message.(message)
end
bucket=(value) click to toggle source
# File lib/deprecation_tracker.rb, line 86
def bucket=(value)
  @bucket = value
  @deprecation_messages[value] ||= [] unless value.nil?
end
compare() click to toggle source
# File lib/deprecation_tracker.rb, line 91
  def compare
    shitlist = read_shitlist

    changed_buckets = []
    normalized_deprecation_messages.each do |bucket, messages|
      if shitlist[bucket] != messages
        changed_buckets << bucket
      end
    end

    if changed_buckets.length > 0
      message = Rainbow(<<-MESSAGE).red
        ⚠️  Deprecation warnings have changed!

        Code called by the following spec files is now generating different deprecation warnings:

        #{changed_buckets.join("\n")}

        To check your failures locally, you can run:

        DEPRECATION_TRACKER=compare bundle exec rspec #{changed_buckets.join(" ")}

        Here is a diff between what is expected and what was generated by this process:

        #{diff}

        See \e[4;37mdev-docs/testing/deprecation_tracker.md\e[0;31m for more information.
      MESSAGE

      raise UnexpectedDeprecations, message
    end
  end
create_temp_shitlist() click to toggle source
# File lib/deprecation_tracker.rb, line 138
def create_temp_shitlist
  temp_file = Tempfile.new("temp-deprecation-tracker-shitlist")
  temp_file.write(JSON.pretty_generate(normalized_deprecation_messages))
  temp_file.flush

  temp_file
end
diff() click to toggle source
# File lib/deprecation_tracker.rb, line 124
def diff
  new_shitlist = create_temp_shitlist
  `git diff --no-index #{shitlist_path} #{new_shitlist.path}`
ensure
  new_shitlist.delete
end
normalized_deprecation_messages() click to toggle source

Normalize deprecation messages to reduce noise from file output and test files to be tracked with separate test runs

# File lib/deprecation_tracker.rb, line 147
def normalized_deprecation_messages
  normalized = read_shitlist.merge(deprecation_messages).each_with_object({}) do |(bucket, messages), hash|
    hash[bucket] = messages.sort
  end

  normalized.reject {|_key, value| value.empty? }.sort_by {|key, _value| key }.to_h
end
read_shitlist() click to toggle source
# File lib/deprecation_tracker.rb, line 155
def read_shitlist
  return {} unless File.exist?(shitlist_path)
  JSON.parse(File.read(shitlist_path))
rescue JSON::ParserError => e
  raise "#{shitlist_path} is not valid JSON: #{e.message}"
end
save() click to toggle source
# File lib/deprecation_tracker.rb, line 131
def save
  new_shitlist = create_temp_shitlist
  FileUtils.cp(new_shitlist.path, shitlist_path)
ensure
  new_shitlist.delete if new_shitlist
end