diff options
author | Rémy Coutable <remy@rymai.me> | 2017-10-05 15:59:58 +0200 |
---|---|---|
committer | Rémy Coutable <remy@rymai.me> | 2017-10-05 19:07:38 +0200 |
commit | 54247c5419e1b06a89bf872dc635a0d0db9871c2 (patch) | |
tree | 2726f8af8ffaba7536b7836cd52eb0363eaefde3 | |
parent | f286d0978483c912e66257fec299a117b81025c3 (diff) | |
download | gitlab-ce-rc/fix-flaky_example.tar.gz |
Add new RSpecFlaky::FlakyExamplesCollection and RSpecFlaky::Config classesrc/fix-flaky_example
Signed-off-by: Rémy Coutable <remy@rymai.me>
-rw-r--r-- | lib/rspec_flaky/config.rb | 21 | ||||
-rw-r--r-- | lib/rspec_flaky/flaky_examples_collection.rb | 38 | ||||
-rw-r--r-- | lib/rspec_flaky/listener.rb | 46 | ||||
-rw-r--r-- | spec/lib/rspec_flaky/config_spec.rb | 102 | ||||
-rw-r--r-- | spec/lib/rspec_flaky/flaky_examples_collection_spec.rb | 79 | ||||
-rw-r--r-- | spec/lib/rspec_flaky/listener_spec.rb | 1 |
6 files changed, 251 insertions, 36 deletions
diff --git a/lib/rspec_flaky/config.rb b/lib/rspec_flaky/config.rb new file mode 100644 index 00000000000..a17ae55910e --- /dev/null +++ b/lib/rspec_flaky/config.rb @@ -0,0 +1,21 @@ +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 diff --git a/lib/rspec_flaky/flaky_examples_collection.rb b/lib/rspec_flaky/flaky_examples_collection.rb new file mode 100644 index 00000000000..6391fa7b488 --- /dev/null +++ b/lib/rspec_flaky/flaky_examples_collection.rb @@ -0,0 +1,38 @@ +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 diff --git a/lib/rspec_flaky/listener.rb b/lib/rspec_flaky/listener.rb index 4752ebe6410..4a5bfec9967 100644 --- a/lib/rspec_flaky/listener.rb +++ b/lib/rspec_flaky/listener.rb @@ -9,7 +9,7 @@ module RspecFlaky 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 @@ module RspecFlaky 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 @@ module RspecFlaky 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 diff --git a/spec/lib/rspec_flaky/config_spec.rb b/spec/lib/rspec_flaky/config_spec.rb new file mode 100644 index 00000000000..83556787e85 --- /dev/null +++ b/spec/lib/rspec_flaky/config_spec.rb @@ -0,0 +1,102 @@ +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 diff --git a/spec/lib/rspec_flaky/flaky_examples_collection_spec.rb b/spec/lib/rspec_flaky/flaky_examples_collection_spec.rb new file mode 100644 index 00000000000..06a8ba0d02e --- /dev/null +++ b/spec/lib/rspec_flaky/flaky_examples_collection_spec.rb @@ -0,0 +1,79 @@ +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 diff --git a/spec/lib/rspec_flaky/listener_spec.rb b/spec/lib/rspec_flaky/listener_spec.rb index 5d04d43c66f..7590ea9576d 100644 --- a/spec/lib/rspec_flaky/listener_spec.rb +++ b/spec/lib/rspec_flaky/listener_spec.rb @@ -56,7 +56,6 @@ describe RspecFlaky::Listener, :aggregate_failures do 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 |