Last active
September 21, 2024 10:49
-
-
Save costa/f9ae7184ca254b25f98872ae7a170358 to your computer and use it in GitHub Desktop.
Git-based "report-skip-retry" RSpec helper for detailing code version releases and more efficient testing -- just `require` it in your spec_helper.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# NOTE As seen at https://gist.github.com/costa/f9ae7184ca254b25f98872ae7a170358 | |
# | |
# Keeps spec (https://RSpec.info) test results (as YAML) in Git commit messages | |
# | |
# NOTE Makes sense for heavy or lengthy (e.g. manual) tests, both for | |
# - documenting the results in the repo | |
# - splitting testing sessions | |
require 'yaml' | |
require 'git' | |
module GitHelper | |
class << self | |
def record!(example, result, time:) | |
if dirty? | |
puts "\nNot actually git-recording (repo dirty)\n#{example.full_description}: #{result} (#{time}s)" | |
else | |
record.track! example, result, time | |
record.save! | |
end | |
end | |
def passed?(example) | |
!dirty? && passed_include?(example) | |
end | |
def dirty! | |
@dirty = true | |
end | |
private | |
def dirty? | |
@dirty || !record | |
end | |
def record | |
return @record if defined? @record | |
@record = Record.open(File.expand_path '.') | |
end | |
def passed_include?(example) | |
!!record.passed[example.id] | |
end | |
end | |
def run(example_group_instance, reporter) | |
start_t = Time.now | |
if GitHelper.passed?(self) | |
puts %[\n"#{full_description}"\n -- successfully passed and git-recorded already] | |
true | |
else | |
super(example_group_instance, reporter).tap do |result| | |
GitHelper.record! self, result, time: (Time.now - start_t) | |
end | |
end | |
rescue Exception | |
GitHelper.record! self, $!, time: (Time.now - start_t) | |
raise | |
end | |
class Record | |
class << self | |
def open(work_dir) | |
g = Git.open(work_dir) | |
return nil unless g.diff.size == 0 | |
new git: g | |
end | |
end | |
attr_reader :failed, :passed | |
def initialize(git:) | |
@g = git | |
@spec_commit = last_commit! | |
data = | |
if @g.config('user.email') == @spec_commit.author.email | |
load | |
else | |
@spec_commit = nil | |
end | |
if data.respond_to?(:keys) && ([:failed, :passed] - data.keys).empty? | |
puts "git-record: will try and fix spec run results in YAML ending of commit message" | |
data | |
else | |
if data | |
puts "git-record WARN: commit message has incompatible YAML ending, will overwrite" | |
else | |
puts "git-record: will try and add spec run results as YAML ending of commit message" | |
end | |
{failed: {}, passed: {}} | |
end.tap do |data| | |
@failed = data[:failed] | |
@passed = data[:passed] | |
end | |
end | |
def track!(example, result, time) | |
if !result || result.is_a?(Exception) | |
[@failed, @passed] | |
else | |
[@passed, @failed] | |
end.tap do |to_insert, to_delete| | |
to_insert[example.id] = [example.full_description, time] | |
to_delete.delete example.id | |
end | |
end | |
def save!(retries: 3) | |
data = {total: @failed.size + @passed.size, failed: @failed, passed: @passed} | |
@g.commit "#{@message}\n\n#{data.to_yaml}", allow_empty: true, amend: !!@spec_commit | |
@spec_commit = last_commit! | |
rescue Git::FailedError | |
raise unless | |
(retries -= 1) >= 0 | |
sleep 3 | |
retry | |
end | |
private | |
def last_commit! | |
@g.object('HEAD') | |
end | |
def load | |
@message = @spec_commit.message | |
yaml_start = @message.lines.index{|l| l.strip == '---'} | |
if yaml_start | |
begin | |
YAML.load @message.lines[yaml_start..-1].join | |
rescue # TODO be more specific here | |
end.tap do | |
@message = @message.lines[0..(yaml_start - 1)].join.strip | |
end | |
end | |
end | |
end | |
end | |
# TODO couldn't get it working with an `around` hook (playing nicely with other hooks), had to resort to monkey patching | |
class RSpec::Core::Example | |
prepend GitHelper | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment