Unverified Commit da5aa64f authored by Rémy Coutable's avatar Rémy Coutable
Browse files

Add new RSpecFlaky::FlakyExamplesCollection and RSpecFlaky::Config classes


Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parent 740e430e50e9
require 'json'
module RspecFlaky
class Config
def self.generate_report?
ENV['FLAKY_RSPEC_GENERATE_REPORT'] == 'true'
end
def self.suite_flaky_examples_report_path
ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'] || Rails.root.join("rspec_flaky/suite-report.json")
end
def self.flaky_examples_report_path
ENV['FLAKY_RSPEC_REPORT_PATH'] || Rails.root.join("rspec_flaky/report.json")
end
def self.new_flaky_examples_report_path
ENV['NEW_FLAKY_RSPEC_REPORT_PATH'] || Rails.root.join("rspec_flaky/new-report.json")
end
end
end
require 'json'
module RspecFlaky
class FlakyExamplesCollection < SimpleDelegator
def self.from_json(json)
new(JSON.parse(json))
end
def initialize(collection = {})
unless collection.is_a?(Hash)
raise ArgumentError, "`collection` must be a Hash, #{collection.class} given!"
end
collection_of_flaky_examples =
collection.map do |uid, example|
[
uid,
example.is_a?(RspecFlaky::FlakyExample) ? example : RspecFlaky::FlakyExample.new(example)
]
end
super(Hash[collection_of_flaky_examples])
end
def to_report
Hash[map { |uid, example| [uid, example.to_h] }].deep_symbolize_keys
end
def -(other)
unless other.respond_to?(:key)
raise ArgumentError, "`other` must respond to `#key?`, #{other.class} does not!"
end
self.class.new(reject { |uid, _| other.key?(uid) })
end
end
end
......@@ -9,7 +9,7 @@ class Listener
attr_reader :suite_flaky_examples, :flaky_examples
def initialize(suite_flaky_examples_json = nil)
@flaky_examples = {}
@flaky_examples = FlakyExamplesCollection.new
@suite_flaky_examples = init_suite_flaky_examples(suite_flaky_examples_json)
end
......@@ -25,14 +25,14 @@ def example_passed(notification)
end
def dump_summary(_)
write_report_file(flaky_examples, flaky_examples_report_path)
write_report_file(flaky_examples, RspecFlaky::Config.flaky_examples_report_path)
new_flaky_examples = _new_flaky_examples
new_flaky_examples = flaky_examples - suite_flaky_examples
if new_flaky_examples.any?
Rails.logger.warn "\nNew flaky examples detected:\n"
Rails.logger.warn JSON.pretty_generate(to_report(new_flaky_examples))
Rails.logger.warn JSON.pretty_generate(new_flaky_examples.to_report)
write_report_file(new_flaky_examples, new_flaky_examples_report_path)
write_report_file(new_flaky_examples, RspecFlaky::Config.new_flaky_examples_report_path)
end
end
......@@ -44,45 +44,21 @@ def to_report(examples)
def init_suite_flaky_examples(suite_flaky_examples_json = nil)
unless suite_flaky_examples_json
return {} unless File.exist?(suite_flaky_examples_report_path)
return {} unless File.exist?(RspecFlaky::Config.suite_flaky_examples_report_path)
suite_flaky_examples_json = File.read(suite_flaky_examples_report_path)
suite_flaky_examples_json = File.read(RspecFlaky::Config.suite_flaky_examples_report_path)
end
suite_flaky_examples = JSON.parse(suite_flaky_examples_json)
Hash[(suite_flaky_examples || {}).map { |k, ex| [k, FlakyExample.new(ex)] }].freeze
end
def _new_flaky_examples
flaky_examples.reject { |uid, _| already_flaky?(uid) }
end
def already_flaky?(example_uid)
suite_flaky_examples.key?(example_uid)
FlakyExamplesCollection.from_json(suite_flaky_examples_json)
end
def write_report_file(examples, file_path)
return unless ENV['FLAKY_RSPEC_GENERATE_REPORT'] == 'true'
def write_report_file(examples_collection, file_path)
return unless RspecFlaky::Config.generate_report?
report_path_dir = File.dirname(file_path)
FileUtils.mkdir_p(report_path_dir) unless Dir.exist?(report_path_dir)
File.write(file_path, JSON.pretty_generate(to_report(examples)))
end
def suite_flaky_examples_report_path
@suite_flaky_examples_report_path ||= ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'] ||
Rails.root.join("rspec_flaky/suite-report.json")
end
def flaky_examples_report_path
@flaky_examples_report_path ||= ENV['FLAKY_RSPEC_REPORT_PATH'] ||
Rails.root.join("rspec_flaky/report.json")
end
def new_flaky_examples_report_path
@new_flaky_examples_report_path ||= ENV['NEW_FLAKY_RSPEC_REPORT_PATH'] ||
Rails.root.join("rspec_flaky/new-report.json")
File.write(file_path, JSON.pretty_generate(examples_collection.to_report))
end
end
end
require 'spec_helper'
describe RspecFlaky::Config, :aggregate_failures do
before do
# Stub these env variables otherwise specs don't behave the same on the CI
stub_env('FLAKY_RSPEC_GENERATE_REPORT', nil)
stub_env('SUITE_FLAKY_RSPEC_REPORT_PATH', nil)
stub_env('FLAKY_RSPEC_REPORT_PATH', nil)
stub_env('NEW_FLAKY_RSPEC_REPORT_PATH', nil)
end
describe '.generate_report?' do
context "when ENV['FLAKY_RSPEC_GENERATE_REPORT'] is not set" do
it 'returns false' do
expect(described_class).not_to be_generate_report
end
end
context "when ENV['FLAKY_RSPEC_GENERATE_REPORT'] is set to 'false'" do
before do
stub_env('FLAKY_RSPEC_GENERATE_REPORT', 'false')
end
it 'returns false' do
expect(described_class).not_to be_generate_report
end
end
context "when ENV['FLAKY_RSPEC_GENERATE_REPORT'] is set to 'true'" do
before do
stub_env('FLAKY_RSPEC_GENERATE_REPORT', 'true')
end
it 'returns true' do
expect(described_class).to be_generate_report
end
end
end
describe '.suite_flaky_examples_report_path' do
context "when ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'] is not set" do
it 'returns the default path' do
expect(Rails.root).to receive(:join).with('rspec_flaky/suite-report.json')
.and_return('root/rspec_flaky/suite-report.json')
expect(described_class.suite_flaky_examples_report_path).to eq('root/rspec_flaky/suite-report.json')
end
end
context "when ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'] is set" do
before do
stub_env('SUITE_FLAKY_RSPEC_REPORT_PATH', 'foo/suite-report.json')
end
it 'returns the value of the env variable' do
expect(described_class.suite_flaky_examples_report_path).to eq('foo/suite-report.json')
end
end
end
describe '.flaky_examples_report_path' do
context "when ENV['FLAKY_RSPEC_REPORT_PATH'] is not set" do
it 'returns the default path' do
expect(Rails.root).to receive(:join).with('rspec_flaky/report.json')
.and_return('root/rspec_flaky/report.json')
expect(described_class.flaky_examples_report_path).to eq('root/rspec_flaky/report.json')
end
end
context "when ENV['FLAKY_RSPEC_REPORT_PATH'] is set" do
before do
stub_env('FLAKY_RSPEC_REPORT_PATH', 'foo/report.json')
end
it 'returns the value of the env variable' do
expect(described_class.flaky_examples_report_path).to eq('foo/report.json')
end
end
end
describe '.new_flaky_examples_report_path' do
context "when ENV['NEW_FLAKY_RSPEC_REPORT_PATH'] is not set" do
it 'returns the default path' do
expect(Rails.root).to receive(:join).with('rspec_flaky/new-report.json')
.and_return('root/rspec_flaky/new-report.json')
expect(described_class.new_flaky_examples_report_path).to eq('root/rspec_flaky/new-report.json')
end
end
context "when ENV['NEW_FLAKY_RSPEC_REPORT_PATH'] is set" do
before do
stub_env('NEW_FLAKY_RSPEC_REPORT_PATH', 'foo/new-report.json')
end
it 'returns the value of the env variable' do
expect(described_class.new_flaky_examples_report_path).to eq('foo/new-report.json')
end
end
end
end
require 'spec_helper'
describe RspecFlaky::FlakyExamplesCollection, :aggregate_failures do
let(:collection_hash) do
{
a: { example_id: 'spec/foo/bar_spec.rb:2' },
b: { example_id: 'spec/foo/baz_spec.rb:3' }
}
end
let(:collection_report) do
{
a: {
example_id: 'spec/foo/bar_spec.rb:2',
first_flaky_at: nil,
last_flaky_at: nil,
last_flaky_job: nil
},
b: {
example_id: 'spec/foo/baz_spec.rb:3',
first_flaky_at: nil,
last_flaky_at: nil,
last_flaky_job: nil
}
}
end
describe '.from_json' do
it 'accepts a JSON' do
collection = described_class.from_json(JSON.pretty_generate(collection_hash))
expect(collection.to_report).to eq(described_class.new(collection_hash).to_report)
end
end
describe '#initialize' do
it 'accepts no argument' do
expect { described_class.new }.not_to raise_error
end
it 'accepts a hash' do
expect { described_class.new(collection_hash) }.not_to raise_error
end
it 'does not accept anything else' do
expect { described_class.new([1, 2, 3]) }.to raise_error(ArgumentError, "`collection` must be a Hash, Array given!")
end
end
describe '#to_report' do
it 'calls #to_h on the values' do
collection = described_class.new(collection_hash)
expect(collection.to_report).to eq(collection_report)
end
end
describe '#-' do
it 'returns only examples that are not present in the given collection' do
collection1 = described_class.new(collection_hash)
collection2 = described_class.new(
a: { example_id: 'spec/foo/bar_spec.rb:2' },
c: { example_id: 'spec/bar/baz_spec.rb:4' })
expect((collection2 - collection1).to_report).to eq(
c: {
example_id: 'spec/bar/baz_spec.rb:4',
first_flaky_at: nil,
last_flaky_at: nil,
last_flaky_job: nil
})
end
it 'fails if the given collection does not respond to `#key?`' do
collection = described_class.new(collection_hash)
expect { collection - [1, 2, 3] }.to raise_error(ArgumentError, "`other` must respond to `#key?`, Array does not!")
end
end
end
......@@ -56,7 +56,6 @@
expect(listener.to_report(listener.suite_flaky_examples))
.to eq(expected_suite_flaky_examples)
expect(listener.__send__(:_new_flaky_examples)).to eq({})
expect(listener.flaky_examples).to eq({})
end
end
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment