From f0b71dcd307ff69977408992dfe28b82368eadbd Mon Sep 17 00:00:00 2001 From: Agis Anastasopoulos Date: Sun, 20 Sep 2020 23:50:07 +0300 Subject: [PATCH] Split spec files to examples programmatically Instead of shelling out to rspec, we now split spec files into individual examples programmatically, using the RSpec Core API. This is faster and more robust, since we avoid common shell shenanigans. Closes #6 --- lib/rspecq/worker.rb | 39 ++++++++++++++++----------------------- test/test_worker.rb | 4 ++-- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/lib/rspecq/worker.rb b/lib/rspecq/worker.rb index a999698..540e6b9 100644 --- a/lib/rspecq/worker.rb +++ b/lib/rspecq/worker.rb @@ -162,39 +162,32 @@ def try_publish_queue!(queue) puts "Published queue (size=#{queue.publish(jobs)})" end + # Split a list of spec files into their individual examples. + # # NOTE: RSpec has to load the files before we can split them as individual # examples. In case a file to be splitted fails to be loaded # (e.g. contains a syntax error), we return the files unchanged, thereby # falling back to scheduling them as whole files. Their errors will be # reported in the normal flow when they're eventually picked up by a worker. def files_to_example_ids(files) - cmd = "DISABLE_SPRING=1 bundle exec rspec --dry-run --format json #{files.join(' ')} 2>&1" - out = `#{cmd}` - cmd_result = $? - - if !cmd_result.success? - rspec_output = begin - JSON.parse(out) - rescue JSON::ParserError - out - end - - log_event( - "Failed to split slow files, falling back to regular scheduling", - "error", - rspec_output: rspec_output, - cmd_result: cmd_result.inspect, - ) + out = StringIO.new - pp rspec_output + reset_rspec_state! - return files - end - - JSON.parse(out)["examples"].map { |e| e["id"] } + RSpec.configuration.add_formatter(RSpec::Core::Formatters::JsonFormatter.new(out)) + RSpec.configuration.files_or_directories_to_run = files_or_dirs_to_run + RSpec.configuration.dry_run = true + + opts = RSpec::Core::ConfigurationOptions.new(files) + result = RSpec::Core::Runner.new(opts).run($stderr, $stdout) + return files if result != 0 + JSON.parse(out.string)["examples"].map { |e| e["id"] } + ensure + RSpec.configuration.dry_run = false + # clear formatters + reset_rspec_state! end - private def reset_rspec_state! diff --git a/test/test_worker.rb b/test/test_worker.rb index 146fc82..b9884b8 100644 --- a/test/test_worker.rb +++ b/test/test_worker.rb @@ -18,7 +18,7 @@ def test_files_to_example_ids ] ) - assert_equal expected, actual + assert_equal expected.sort, actual.sort end def test_files_to_example_ids_failure_fallback @@ -36,6 +36,6 @@ def test_files_to_example_ids_failure_fallback ] ) - assert_equal expected, actual + assert_equal expected.sort, actual.sort end end