Bootstrap Rubocop configuration and fix offenses

This commit is contained in:
Agis Anastasopoulos 2020-10-11 13:35:36 +03:00
parent 22e3f7c267
commit 6f0077a60e
15 changed files with 121 additions and 53 deletions

49
.rubocop.yml Normal file
View File

@ -0,0 +1,49 @@
AllCops:
NewCops: enable
Metrics:
Enabled: false
Style/WordArray:
EnforcedStyle: brackets
Style/StringLiterals:
EnforcedStyle: double_quotes
Style/GlobalVars:
Exclude:
- test/sample_suites/flakey_suite/**/*.rb
- test/sample_suites/flaky_job_detection/**/*.rb
Style/SpecialGlobalVars:
Enabled: false
Style/IfUnlessModifier:
Enabled: false
Style/GuardClause:
Enabled: false
Style/TrailingCommaInArrayLiteral:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false
Style/NegatedIf:
Enabled: false
Style/NumericPredicate:
Enabled: false
Layout/FirstArrayElementIndentation:
Enabled: false
Layout/ArgumentAlignment:
Enabled: false
Gemspec/RequiredRubyVersion:
Enabled: false
Naming/MethodParameterName:
Enabled: false

View File

@ -2,7 +2,7 @@ require "rake/testtask"
Rake::TestTask.new do |t| Rake::TestTask.new do |t|
t.libs << "test" t.libs << "test"
t.test_files = FileList['test/test_*.rb'] t.test_files = FileList["test/test_*.rb"]
t.verbose = true t.verbose = true
end end

View File

@ -2,7 +2,7 @@
require "optparse" require "optparse"
require "rspecq" require "rspecq"
DEFAULT_REDIS_HOST = "127.0.0.1" DEFAULT_REDIS_HOST = "127.0.0.1".freeze
DEFAULT_REPORT_TIMEOUT = 3600 # 1 hour DEFAULT_REPORT_TIMEOUT = 3600 # 1 hour
DEFAULT_MAX_REQUEUES = 3 DEFAULT_MAX_REQUEUES = 3
DEFAULT_FAIL_FAST = 0 DEFAULT_FAIL_FAST = 0
@ -66,7 +66,7 @@ OptionParser.new do |o|
o.on("--report", "Enable reporter mode: do not pull tests off the queue; " \ o.on("--report", "Enable reporter mode: do not pull tests off the queue; " \
"instead print build progress and exit when it's " \ "instead print build progress and exit when it's " \
"finished.\n#{o.summary_indent*9} " \ "finished.\n#{o.summary_indent * 9} " \
"Exits with a non-zero status code if there were any " \ "Exits with a non-zero status code if there were any " \
"failures.") do |v| "failures.") do |v|
opts[:report] = v opts[:report] = v
@ -85,7 +85,7 @@ OptionParser.new do |o|
end end
o.on("--fail-fast N", Integer, "Abort build with a non-zero status code " \ o.on("--fail-fast N", Integer, "Abort build with a non-zero status code " \
"after N failed examples." ) do |v| "after N failed examples.") do |v|
opts[:fail_fast] = v opts[:fail_fast] = v
end end
@ -104,15 +104,17 @@ opts[:build] ||= ENV["RSPECQ_BUILD"]
opts[:worker] ||= ENV["RSPECQ_WORKER"] opts[:worker] ||= ENV["RSPECQ_WORKER"]
opts[:redis_host] ||= ENV["RSPECQ_REDIS"] || DEFAULT_REDIS_HOST opts[:redis_host] ||= ENV["RSPECQ_REDIS"] || DEFAULT_REDIS_HOST
opts[:timings] ||= env_set?("RSPECQ_UPDATE_TIMINGS") opts[:timings] ||= env_set?("RSPECQ_UPDATE_TIMINGS")
opts[:file_split_threshold] ||= Integer(ENV["RSPECQ_FILE_SPLIT_THRESHOLD"] || 9999999) opts[:file_split_threshold] ||= Integer(ENV["RSPECQ_FILE_SPLIT_THRESHOLD"] || 9_999_999)
opts[:report] ||= env_set?("RSPECQ_REPORT") opts[:report] ||= env_set?("RSPECQ_REPORT")
opts[:report_timeout] ||= Integer(ENV["RSPECQ_REPORT_TIMEOUT"] || DEFAULT_REPORT_TIMEOUT) opts[:report_timeout] ||= Integer(ENV["RSPECQ_REPORT_TIMEOUT"] || DEFAULT_REPORT_TIMEOUT)
opts[:max_requeues] ||= Integer(ENV["RSPECQ_MAX_REQUEUES"] || DEFAULT_MAX_REQUEUES) opts[:max_requeues] ||= Integer(ENV["RSPECQ_MAX_REQUEUES"] || DEFAULT_MAX_REQUEUES)
opts[:redis_url] ||= ENV["RSPECQ_REDIS_URL"] opts[:redis_url] ||= ENV["RSPECQ_REDIS_URL"]
opts[:fail_fast] ||= Integer(ENV["RSPECQ_FAIL_FAST"] || DEFAULT_FAIL_FAST) opts[:fail_fast] ||= Integer(ENV["RSPECQ_FAIL_FAST"] || DEFAULT_FAIL_FAST)
# rubocop:disable Style/RaiseArgs, Layout/EmptyLineAfterGuardClause
raise OptionParser::MissingArgument.new(:build) if opts[:build].nil? raise OptionParser::MissingArgument.new(:build) if opts[:build].nil?
raise OptionParser::MissingArgument.new(:worker) if !opts[:report] && opts[:worker].nil? raise OptionParser::MissingArgument.new(:worker) if !opts[:report] && opts[:worker].nil?
# rubocop:enable Style/RaiseArgs, Layout/EmptyLineAfterGuardClause
redis_opts = {} redis_opts = {}
@ -126,7 +128,7 @@ if opts[:report]
reporter = RSpecQ::Reporter.new( reporter = RSpecQ::Reporter.new(
build_id: opts[:build], build_id: opts[:build],
timeout: opts[:report_timeout], timeout: opts[:report_timeout],
redis_opts: redis_opts, redis_opts: redis_opts
) )
reporter.report reporter.report

View File

@ -1,5 +1,10 @@
module RSpecQ module RSpecQ
module Formatters module Formatters
# Persists failed examples information (i.e. message and backtrace), so
# that they can be reported to the end user by the Reporter.
#
# Also persists non-example error information (e.g. a syntax error that
# in a spec file).
class FailureRecorder class FailureRecorder
def initialize(queue, job, max_requeues) def initialize(queue, job, max_requeues)
@queue = queue @queue = queue
@ -33,16 +38,19 @@ module RSpecQ
end end
presenter = RSpec::Core::Formatters::ExceptionPresenter.new( presenter = RSpec::Core::Formatters::ExceptionPresenter.new(
example.exception, example) example.exception, example
)
msg = presenter.fully_formatted(nil, @colorizer) msg = presenter.fully_formatted(nil, @colorizer)
msg << "\n" msg << "\n"
msg << @colorizer.wrap( msg << @colorizer.wrap(
"bin/rspec #{example.location_rerun_argument}", "bin/rspec #{example.location_rerun_argument}",
RSpec.configuration.failure_color) RSpec.configuration.failure_color
)
msg << @colorizer.wrap( msg << @colorizer.wrap(
" # #{example.full_description}", RSpec.configuration.detail_color) " # #{example.full_description}", RSpec.configuration.detail_color
)
@queue.record_example_failure(notification.example.id, msg) @queue.record_example_failure(notification.example.id, msg)
end end

View File

@ -1,5 +1,8 @@
module RSpecQ module RSpecQ
module Formatters module Formatters
# Persists each job's timing (in seconds). Those timings are used when
# determining the ordering in which jobs are scheduled (slower jobs will
# be enqueued first).
class JobTimingRecorder class JobTimingRecorder
def initialize(queue, job) def initialize(queue, job)
@queue = queue @queue = queue

View File

@ -14,4 +14,3 @@ module RSpecQ
end end
end end
end end

View File

@ -79,7 +79,7 @@ module RSpecQ
# NOTE: jobs will be processed from head to tail (lpop) # NOTE: jobs will be processed from head to tail (lpop)
def publish(jobs, fail_fast = 0) def publish(jobs, fail_fast = 0)
@redis.multi do @redis.multi do
@redis.hset(key_queue_config, 'fail_fast', fail_fast) @redis.hset(key_queue_config, "fail_fast", fail_fast)
@redis.rpush(key_queue_unprocessed, jobs) @redis.rpush(key_queue_unprocessed, jobs)
@redis.set(key_queue_status, STATUS_READY) @redis.set(key_queue_status, STATUS_READY)
end.first end.first
@ -131,7 +131,7 @@ module RSpecQ
@redis.eval( @redis.eval(
REQUEUE_JOB, REQUEUE_JOB,
keys: [key_queue_unprocessed, key_requeues], keys: [key_queue_unprocessed, key_requeues],
argv: [job, max_requeues], argv: [job, max_requeues]
) )
end end
@ -210,9 +210,10 @@ module RSpecQ
@redis.get(key_queue_status) == STATUS_READY @redis.get(key_queue_status) == STATUS_READY
end end
def wait_until_published(timeout=30) def wait_until_published(timeout = 30)
(timeout * 10).times do (timeout * 10).times do
return if published? return if published?
sleep 0.1 sleep 0.1
end end
@ -250,7 +251,7 @@ module RSpecQ
def fail_fast def fail_fast
return nil unless published? return nil unless published?
@fail_fast ||= Integer(@redis.hget(key_queue_config, 'fail_fast')) @fail_fast ||= Integer(@redis.hget(key_queue_config, "fail_fast"))
end end
# Returns true if the number of failed tests, has surpassed the threshold # Returns true if the number of failed tests, has surpassed the threshold

View File

@ -16,7 +16,7 @@ module RSpecQ
# We want feedback to be immediattely printed to CI users, so # We want feedback to be immediattely printed to CI users, so
# we disable buffering. # we disable buffering.
STDOUT.sync = true $stdout.sync = true
end end
def report def report
@ -28,7 +28,7 @@ module RSpecQ
failure_heading_printed = false failure_heading_printed = false
tests_duration = measure_duration do tests_duration = measure_duration do
@timeout.times do |i| @timeout.times do
@queue.example_failures.each do |job, rspec_output| @queue.example_failures.each do |job, rspec_output|
next if reported_failures[job] next if reported_failures[job]
@ -124,13 +124,13 @@ module RSpecQ
return if jobs.empty? return if jobs.empty?
jobs.each do |job| jobs.each do |job|
filename = job.sub(/\[.+\]/, '') filename = job.sub(/\[.+\]/, "")
extra = { extra = {
build: @build_id, build: @build_id,
build_timeout: @timeout, build_timeout: @timeout,
queue: @queue.inspect, queue: @queue.inspect,
object: self.inspect, object: inspect,
pid: Process.pid, pid: Process.pid,
job_path: job, job_path: job,
build_duration: build_duration build_duration: build_duration
@ -143,7 +143,7 @@ module RSpecQ
Raven.capture_message( Raven.capture_message(
"Flaky test in #{filename}", "Flaky test in #{filename}",
level: 'warning', level: "warning",
extra: extra, extra: extra,
tags: tags tags: tags
) )

View File

@ -55,7 +55,7 @@ module RSpecQ
@fail_fast = 0 @fail_fast = 0
@files_or_dirs_to_run = "spec" @files_or_dirs_to_run = "spec"
@populate_timings = false @populate_timings = false
@file_split_threshold = 999999 @file_split_threshold = 999_999
@heartbeat_updated_at = nil @heartbeat_updated_at = nil
@max_requeues = 3 @max_requeues = 3
@ -98,7 +98,7 @@ module RSpecQ
# reconfigure rspec # reconfigure rspec
RSpec.configuration.detail_color = :magenta RSpec.configuration.detail_color = :magenta
RSpec.configuration.seed = srand && srand % 0xFFFF RSpec.configuration.seed = srand && srand % 0xFFFF
RSpec.configuration.backtrace_formatter.filter_gem('rspecq') RSpec.configuration.backtrace_formatter.filter_gem("rspecq")
RSpec.configuration.add_formatter(Formatters::FailureRecorder.new(queue, job, max_requeues)) RSpec.configuration.add_formatter(Formatters::FailureRecorder.new(queue, job, max_requeues))
RSpec.configuration.add_formatter(Formatters::ExampleCountRecorder.new(queue)) RSpec.configuration.add_formatter(Formatters::ExampleCountRecorder.new(queue))
RSpec.configuration.add_formatter(Formatters::WorkerHeartbeatRecorder.new(self)) RSpec.configuration.add_formatter(Formatters::WorkerHeartbeatRecorder.new(self))
@ -155,7 +155,7 @@ module RSpecQ
jobs.concat(files_to_run) jobs.concat(files_to_run)
end end
default_timing = timings.values[timings.values.size/2] default_timing = timings.values[timings.values.size / 2]
# assign timings (based on previous runs) to all jobs # assign timings (based on previous runs) to all jobs
jobs = jobs.each_with_object({}) do |j, h| jobs = jobs.each_with_object({}) do |j, h|
@ -180,7 +180,8 @@ module RSpecQ
# see https://github.com/rspec/rspec-core/pull/2723 # see https://github.com/rspec/rspec-core/pull/2723
if Gem::Version.new(RSpec::Core::Version::STRING) <= Gem::Version.new("3.9.1") if Gem::Version.new(RSpec::Core::Version::STRING) <= Gem::Version.new("3.9.1")
RSpec.world.instance_variable_set( RSpec.world.instance_variable_set(
:@example_group_counts_by_spec_file, Hash.new(0)) :@example_group_counts_by_spec_file, Hash.new(0)
)
end end
# RSpec.clear_examples does not reset those, which causes issues when # RSpec.clear_examples does not reset those, which causes issues when
@ -204,17 +205,17 @@ module RSpecQ
if !cmd_result.success? if !cmd_result.success?
rspec_output = begin rspec_output = begin
JSON.parse(out) JSON.parse(out)
rescue JSON::ParserError rescue JSON::ParserError
out out
end end
log_event( log_event(
"Failed to split slow files, falling back to regular scheduling.\n #{err}", "Failed to split slow files, falling back to regular scheduling.\n #{err}",
"error", "error",
rspec_stdout: rspec_output, rspec_stdout: rspec_output,
rspec_stderr: err, rspec_stderr: err,
cmd_result: cmd_result.inspect, cmd_result: cmd_result.inspect
) )
pp rspec_output pp rspec_output
@ -236,7 +237,7 @@ module RSpecQ
# Prints msg to standard output and emits an event to Sentry, if the # Prints msg to standard output and emits an event to Sentry, if the
# SENTRY_DSN environment variable is set. # SENTRY_DSN environment variable is set.
def log_event(msg, level, additional={}) def log_event(msg, level, additional = {})
puts msg puts msg
Raven.capture_message(msg, level: level, extra: { Raven.capture_message(msg, level: level, extra: {
@ -247,8 +248,8 @@ module RSpecQ
populate_timings: populate_timings, populate_timings: populate_timings,
file_split_threshold: file_split_threshold, file_split_threshold: file_split_threshold,
heartbeat_updated_at: @heartbeat_updated_at, heartbeat_updated_at: @heartbeat_updated_at,
object: self.inspect, object: inspect,
pid: Process.pid, pid: Process.pid
}.merge(additional)) }.merge(additional))
end end
end end

View File

@ -13,7 +13,7 @@ Gem::Specification.new do |s|
s.license = "MIT" s.license = "MIT"
if ENV["CI"] && ENV["RSPEC_CORE"] if ENV["CI"] && ENV["RSPEC_CORE"]
s.add_dependency "rspec-core", "#{ENV['RSPEC_CORE']}" s.add_dependency "rspec-core", ENV["RSPEC_CORE"]
else else
s.add_dependency "rspec-core" s.add_dependency "rspec-core"
end end
@ -21,9 +21,9 @@ Gem::Specification.new do |s|
s.add_dependency "redis" s.add_dependency "redis"
s.add_dependency "sentry-raven" s.add_dependency "sentry-raven"
s.add_development_dependency "rake"
s.add_development_dependency "pry-byebug"
s.add_development_dependency "minitest" s.add_development_dependency "minitest"
s.add_development_dependency "pry-byebug"
s.add_development_dependency "rake"
s.add_development_dependency "rspec" s.add_development_dependency "rspec"
s.add_development_dependency "rubocop", "~> 0.93.0" s.add_development_dependency "rubocop", "~> 0.93.0"
end end

View File

@ -1,4 +1,6 @@
# rubocop:disable Style/StderrPuts
$stderr.puts "I'm a warning!" $stderr.puts "I'm a warning!"
# rubocop:enable Style/StderrPuts
describe "A slow spec file to be splitted" do describe "A slow spec file to be splitted" do
it do it do

View File

@ -17,12 +17,13 @@ class TestEndToEnd < RSpecQTest
"./spec/success_spec.rb", "./spec/success_spec.rb",
], queue ], queue
assert_equal 3 + 3 + 5, queue.example_count assert_equal 3 + 3 + 5, queue.example_count
assert_equal({ assert_equal(
"./spec/fail_1_spec.rb[1:2]" => "3", { "./spec/fail_1_spec.rb[1:2]" => "3",
"./spec/fail_2_spec.rb[1:2]" => "3", "./spec/fail_2_spec.rb[1:2]" => "3" },
}, queue.requeued_jobs) queue.requeued_jobs
)
end end
def test_passing_suite def test_passing_suite
@ -87,7 +88,7 @@ class TestEndToEnd < RSpecQTest
"./spec/medium_spec.rb", "./spec/medium_spec.rb",
"./spec/slow_spec.rb", "./spec/slow_spec.rb",
"./spec/very_slow_spec.rb", "./spec/very_slow_spec.rb",
], queue.timings.sort_by { |k,v| v }.map(&:first) ], queue.timings.sort_by { |_, v| v }.map(&:first)
end end
def test_timings_no_update def test_timings_no_update
@ -98,11 +99,11 @@ class TestEndToEnd < RSpecQTest
end end
def test_spec_file_splitting def test_spec_file_splitting
queue = exec_build( "spec_file_splitting", "--update-timings") queue = exec_build("spec_file_splitting", "--update-timings")
assert queue.build_successful? assert queue.build_successful?
refute_empty queue.timings refute_empty queue.timings
queue = exec_build( "spec_file_splitting", "--file-split-threshold 1") queue = exec_build("spec_file_splitting", "--file-split-threshold 1")
assert queue.build_successful? assert queue.build_successful?
refute_empty queue.timings refute_empty queue.timings

View File

@ -3,7 +3,7 @@ require "securerandom"
require "rspecq" require "rspecq"
module TestHelpers module TestHelpers
REDIS_OPTS = {host: "127.0.0.1"}.freeze REDIS_OPTS = { host: "127.0.0.1" }.freeze
EXEC_CMD = "bundle exec rspecq".freeze EXEC_CMD = "bundle exec rspecq".freeze
def rand_id def rand_id
@ -20,7 +20,7 @@ module TestHelpers
w w
end end
def exec_build(path, args="") def exec_build(path, args = "")
worker_id = rand_id worker_id = rand_id
build_id = rand_id build_id = rand_id
@ -34,7 +34,7 @@ module TestHelpers
queue = RSpecQ::Queue.new(build_id, worker_id, REDIS_OPTS) queue = RSpecQ::Queue.new(build_id, worker_id, REDIS_OPTS)
assert_queue_well_formed(queue) assert_queue_well_formed(queue)
return queue queue
end end
def suite_path(path) def suite_path(path)
@ -42,11 +42,11 @@ module TestHelpers
end end
# Returns the worker pid # Returns the worker pid
def start_worker(build_id:, worker_id: rand_id, suite:) def start_worker(build_id:, suite:, worker_id: rand_id)
Process.spawn( Process.spawn(
"#{EXEC_CMD} -w #{worker_id} -b #{build_id}", "#{EXEC_CMD} -w #{worker_id} -b #{build_id}",
chdir: suite_path(suite), chdir: suite_path(suite),
out: (ENV["RSPECQ_DEBUG"] ? :out : File::NULL), out: (ENV["RSPECQ_DEBUG"] ? :out : File::NULL)
) )
end end
@ -59,7 +59,7 @@ module TestHelpers
begin begin
orig = $stdout.clone orig = $stdout.clone
$stdout.reopen(File::NULL, 'w') $stdout.reopen(File::NULL, "w")
yield yield
ensure ensure
$stdout.reopen(orig) $stdout.reopen(orig)

View File

@ -1,14 +1,15 @@
module TestHelpers module TestHelpers
module Assertions module Assertions
def assert_queue_well_formed(queue, msg=nil) def assert_queue_well_formed(queue, _msg = nil)
redis = queue.redis redis = queue.redis
heartbeats = redis.zrange( heartbeats = redis.zrange(
queue.send(:key_worker_heartbeats), 0, -1, withscores: true) queue.send(:key_worker_heartbeats), 0, -1, withscores: true
)
assert queue.published? assert queue.published?
assert (queue.build_failed_fast? || queue.exhausted?) assert(queue.build_failed_fast? || queue.exhausted?)
assert_operator heartbeats.size, :>=, 0 assert_operator heartbeats.size, :>=, 0
assert heartbeats.all? { |hb| Time.at(hb.last) <= Time.now } assert(heartbeats.all? { |hb| Time.at(hb.last) <= Time.now })
end end
def assert_build_not_flakey(queue) def assert_build_not_flakey(queue)

View File

@ -20,7 +20,8 @@ class TestQueue < RSpecQTest
"./spec/flaky_spec.rb[1:1]", "./spec/flaky_spec.rb[1:1]",
"./spec/flaky_spec.rb[1:3]", "./spec/flaky_spec.rb[1:3]",
"./spec/legit_failure_spec.rb", "./spec/legit_failure_spec.rb",
"./spec/legit_failure_spec.rb[1:3]"], queue) "./spec/legit_failure_spec.rb[1:3]"], queue
)
assert_failures(["./spec/legit_failure_spec.rb[1:3]"], queue) assert_failures(["./spec/legit_failure_spec.rb[1:3]"], queue)
end end