diff options
Diffstat (limited to 'ci')
-rwxr-xr-x | ci/custom_cops/bin/test | 5 | ||||
-rw-r--r-- | ci/custom_cops/lib/custom_cops.rb | 4 | ||||
-rw-r--r-- | ci/custom_cops/lib/custom_cops/assert_not.rb | 40 | ||||
-rw-r--r-- | ci/custom_cops/lib/custom_cops/refute_not.rb | 71 | ||||
-rw-r--r-- | ci/custom_cops/test/custom_cops/assert_not_test.rb | 42 | ||||
-rw-r--r-- | ci/custom_cops/test/custom_cops/refute_not_test.rb | 66 | ||||
-rw-r--r-- | ci/custom_cops/test/support/cop_helper.rb | 47 | ||||
-rw-r--r-- | ci/qunit-selenium-runner.rb | 16 | ||||
-rwxr-xr-x | ci/travis.rb | 25 |
9 files changed, 311 insertions, 5 deletions
diff --git a/ci/custom_cops/bin/test b/ci/custom_cops/bin/test new file mode 100755 index 0000000000..495ffec83a --- /dev/null +++ b/ci/custom_cops/bin/test @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +COMPONENT_ROOT = File.expand_path("..", __dir__) +require_relative "../../../tools/test" diff --git a/ci/custom_cops/lib/custom_cops.rb b/ci/custom_cops/lib/custom_cops.rb new file mode 100644 index 0000000000..157b8247e4 --- /dev/null +++ b/ci/custom_cops/lib/custom_cops.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require_relative "custom_cops/refute_not" +require_relative "custom_cops/assert_not" diff --git a/ci/custom_cops/lib/custom_cops/assert_not.rb b/ci/custom_cops/lib/custom_cops/assert_not.rb new file mode 100644 index 0000000000..8b49d3eac2 --- /dev/null +++ b/ci/custom_cops/lib/custom_cops/assert_not.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module CustomCops + # Enforces the use of `assert_not` over `assert !`. + # + # @example + # # bad + # assert !x + # assert ! x + # + # # good + # assert_not x + # + class AssertNot < RuboCop::Cop::Cop + MSG = "Prefer `assert_not` over `assert !`" + + def_node_matcher :offensive?, "(send nil? :assert (send ... :!) ...)" + + def on_send(node) + add_offense(node) if offensive?(node) + end + + def autocorrect(node) + expression = node.loc.expression + + ->(corrector) do + corrector.replace( + expression, + corrected_source(expression.source) + ) + end + end + + private + + def corrected_source(source) + source.gsub(/^assert(\(| ) *! */, "assert_not\\1") + end + end +end diff --git a/ci/custom_cops/lib/custom_cops/refute_not.rb b/ci/custom_cops/lib/custom_cops/refute_not.rb new file mode 100644 index 0000000000..3e89e0fd32 --- /dev/null +++ b/ci/custom_cops/lib/custom_cops/refute_not.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module CustomCops + # Enforces the use of `#assert_not` methods over `#refute` methods. + # + # @example + # # bad + # refute false + # refute_empty [1, 2, 3] + # refute_equal true, false + # + # # good + # assert_not false + # assert_not_empty [1, 2, 3] + # assert_not_equal true, false + # + class RefuteNot < RuboCop::Cop::Cop + MSG = "Prefer `%<assert_method>s` over `%<refute_method>s`" + + CORRECTIONS = { + refute: "assert_not", + refute_empty: "assert_not_empty", + refute_equal: "assert_not_equal", + refute_in_delta: "assert_not_in_delta", + refute_in_epsilon: "assert_not_in_epsilon", + refute_includes: "assert_not_includes", + refute_instance_of: "assert_not_instance_of", + refute_kind_of: "assert_not_kind_of", + refute_nil: "assert_not_nil", + refute_operator: "assert_not_operator", + refute_predicate: "assert_not_predicate", + refute_respond_to: "assert_not_respond_to", + refute_same: "assert_not_same", + refute_match: "assert_no_match" + }.freeze + + OFFENSIVE_METHODS = CORRECTIONS.keys.freeze + + def_node_matcher :offensive?, "(send nil? #offensive_method? ...)" + + def on_send(node) + return unless offensive?(node) + + message = offense_message(node.method_name) + add_offense(node, location: :selector, message: message) + end + + def autocorrect(node) + ->(corrector) do + corrector.replace( + node.loc.selector, + CORRECTIONS[node.method_name] + ) + end + end + + private + + def offensive_method?(method_name) + OFFENSIVE_METHODS.include?(method_name) + end + + def offense_message(method_name) + format( + MSG, + refute_method: method_name, + assert_method: CORRECTIONS[method_name] + ) + end + end +end diff --git a/ci/custom_cops/test/custom_cops/assert_not_test.rb b/ci/custom_cops/test/custom_cops/assert_not_test.rb new file mode 100644 index 0000000000..abb151aeb4 --- /dev/null +++ b/ci/custom_cops/test/custom_cops/assert_not_test.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "support/cop_helper" +require_relative "../../lib/custom_cops/assert_not" + +class AssertNotTest < ActiveSupport::TestCase + include CopHelper + + setup do + @cop = CustomCops::AssertNot.new + end + + test "rejects 'assert !'" do + inspect_source @cop, "assert !x" + assert_offense @cop, "^^^^^^^^^ Prefer `assert_not` over `assert !`" + end + + test "rejects 'assert !' with a complex value" do + inspect_source @cop, "assert !a.b(c)" + assert_offense @cop, "^^^^^^^^^^^^^^ Prefer `assert_not` over `assert !`" + end + + test "autocorrects `assert !`" do + corrected = autocorrect_source(@cop, "assert !false") + assert_equal "assert_not false", corrected + end + + test "autocorrects `assert !` with extra spaces" do + corrected = autocorrect_source(@cop, "assert ! false") + assert_equal "assert_not false", corrected + end + + test "autocorrects `assert !` with parentheses" do + corrected = autocorrect_source(@cop, "assert(!false)") + assert_equal "assert_not(false)", corrected + end + + test "accepts `assert_not`" do + inspect_source @cop, "assert_not x" + assert_empty @cop.offenses + end +end diff --git a/ci/custom_cops/test/custom_cops/refute_not_test.rb b/ci/custom_cops/test/custom_cops/refute_not_test.rb new file mode 100644 index 0000000000..f0f6eaeda0 --- /dev/null +++ b/ci/custom_cops/test/custom_cops/refute_not_test.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require "support/cop_helper" +require_relative "../../lib/custom_cops/refute_not" + +class RefuteNotTest < ActiveSupport::TestCase + include CopHelper + + setup do + @cop = CustomCops::RefuteNot.new + end + + { + refute: :assert_not, + refute_empty: :assert_not_empty, + refute_equal: :assert_not_equal, + refute_in_delta: :assert_not_in_delta, + refute_in_epsilon: :assert_not_in_epsilon, + refute_includes: :assert_not_includes, + refute_instance_of: :assert_not_instance_of, + refute_kind_of: :assert_not_kind_of, + refute_nil: :assert_not_nil, + refute_operator: :assert_not_operator, + refute_predicate: :assert_not_predicate, + refute_respond_to: :assert_not_respond_to, + refute_same: :assert_not_same, + refute_match: :assert_no_match + }.each do |refute_method, assert_method| + test "rejects `#{refute_method}` with a single argument" do + inspect_source(@cop, "#{refute_method} a") + assert_offense @cop, offense_message(refute_method, assert_method) + end + + test "rejects `#{refute_method}` with multiple arguments" do + inspect_source(@cop, "#{refute_method} a, b, c") + assert_offense @cop, offense_message(refute_method, assert_method) + end + + test "autocorrects `#{refute_method}` with a single argument" do + corrected = autocorrect_source(@cop, "#{refute_method} a") + assert_equal "#{assert_method} a", corrected + end + + test "autocorrects `#{refute_method}` with multiple arguments" do + corrected = autocorrect_source(@cop, "#{refute_method} a, b, c") + assert_equal "#{assert_method} a, b, c", corrected + end + + test "accepts `#{assert_method}` with a single argument" do + inspect_source(@cop, "#{assert_method} a") + assert_empty @cop.offenses + end + + test "accepts `#{assert_method}` with multiple arguments" do + inspect_source(@cop, "#{assert_method} a, b, c") + assert_empty @cop.offenses + end + end + + private + + def offense_message(refute_method, assert_method) + carets = "^" * refute_method.to_s.length + "#{carets} Prefer `#{assert_method}` over `#{refute_method}`" + end +end diff --git a/ci/custom_cops/test/support/cop_helper.rb b/ci/custom_cops/test/support/cop_helper.rb new file mode 100644 index 0000000000..c2c6b969dd --- /dev/null +++ b/ci/custom_cops/test/support/cop_helper.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "rubocop" + +module CopHelper + def inspect_source(cop, source) + processed_source = parse_source(source) + raise "Error parsing example code" unless processed_source.valid_syntax? + investigate(cop, processed_source) + processed_source + end + + def autocorrect_source(cop, source) + cop.instance_variable_get(:@options)[:auto_correct] = true + processed_source = inspect_source(cop, source) + rewrite(cop, processed_source) + end + + def assert_offense(cop, expected_message) + assert_not_empty( + cop.offenses, + "Expected offense with message \"#{expected_message}\", but got no offense" + ) + + offense = cop.offenses.first + carets = "^" * offense.column_length + + assert_equal expected_message, "#{carets} #{offense.message}" + end + + private + TARGET_RUBY_VERSION = 2.4 + + def parse_source(source) + RuboCop::ProcessedSource.new(source, TARGET_RUBY_VERSION) + end + + def rewrite(cop, processed_source) + RuboCop::Cop::Corrector.new(processed_source.buffer, cop.corrections) + .rewrite + end + + def investigate(cop, processed_source) + RuboCop::Cop::Commissioner.new([cop], [], raise_error: true) + .investigate(processed_source) + end +end diff --git a/ci/qunit-selenium-runner.rb b/ci/qunit-selenium-runner.rb new file mode 100644 index 0000000000..05bcab8cdb --- /dev/null +++ b/ci/qunit-selenium-runner.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "qunit/selenium/test_runner" +require "chromedriver/helper" + +driver_options = Selenium::WebDriver::Chrome::Options.new +driver_options.add_argument("--headless") +driver_options.add_argument("--disable-gpu") +driver_options.add_argument("--no-sandbox") + +driver = ::Selenium::WebDriver.for(:chrome, options: driver_options) +result = QUnit::Selenium::TestRunner.new(driver).open(ARGV[0], timeout: 60) +driver.quit + +puts "Time: #{result.duration} seconds, Total: #{result.assertions[:total]}, Passed: #{result.assertions[:passed]}, Failed: #{result.assertions[:failed]}" +exit(result.tests[:failed] > 0 ? 1 : 0) diff --git a/ci/travis.rb b/ci/travis.rb index 1d658bae47..861063afa5 100755 --- a/ci/travis.rb +++ b/ci/travis.rb @@ -1,8 +1,14 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + require "fileutils" include FileUtils commands = [ + 'mysql -e "create user rails@localhost;"', + 'mysql -e "grant all privileges on activerecord_unittest.* to rails@localhost;"', + 'mysql -e "grant all privileges on activerecord_unittest2.* to rails@localhost;"', + 'mysql -e "grant all privileges on inexistent_activerecord_unittest.* to rails@localhost;"', 'mysql -e "create database activerecord_unittest;"', 'mysql -e "create database activerecord_unittest2;"', 'psql -c "create database activerecord_unittest;" -U postgres', @@ -10,7 +16,7 @@ commands = [ ] commands.each do |command| - system("#{command} > /dev/null 2>&1") + system(command, [1, 2] => File::NULL) end class Build @@ -24,6 +30,7 @@ class Build "av" => "actionview", "aj" => "activejob", "ac" => "actioncable", + "ast" => "activestorage", "guides" => "guides" } @@ -36,8 +43,10 @@ class Build def run!(options = {}) self.options.update(options) + Dir.chdir(dir) do announce(heading) + if guides? run_bug_report_templates else @@ -69,7 +78,7 @@ class Build end tasks else - ["test", ("isolated" if isolated?), ("integration" if integration?)].compact.join(":") + ["test", ("isolated" if isolated?), ("integration" if integration?), ("ujs" if ujs?)].compact.join(":") end end @@ -92,6 +101,10 @@ class Build gem == "guides" end + def ujs? + component.split(":").last == "ujs" + end + def isolated? options[:isolated] end @@ -122,7 +135,7 @@ class Build if activesupport? && !isolated? # There is a known issue with the listen tests that causes files to be # incorrectly GC'ed even when they are still in-use. The current solution - # is to only run them in isolation to avoid randomly failing our test suite. + # is to only run them in isolation to avoid random failures of our test suite. { "LISTEN" => "0" } else {} @@ -136,7 +149,7 @@ class Build end end -if ENV["GEM"]=="aj:integration" +if ENV["GEM"] == "aj:integration" ENV["QC_DATABASE_URL"] = "postgres://postgres@localhost/active_jobs_qc_int_test" ENV["QUE_DATABASE_URL"] = "postgres://postgres@localhost/active_jobs_que_int_test" end @@ -146,15 +159,17 @@ results = {} ENV["GEM"].split(",").each do |gem| [false, true].each do |isolated| next if ENV["TRAVIS_PULL_REQUEST"] && ENV["TRAVIS_PULL_REQUEST"] != "false" && isolated + next if RUBY_VERSION < "2.5" && isolated next if gem == "railties" && isolated next if gem == "ac" && isolated next if gem == "ac:integration" && isolated next if gem == "aj:integration" && isolated next if gem == "guides" && isolated + next if gem == "av:ujs" && isolated + next if gem == "ast" && isolated build = Build.new(gem, isolated: isolated) results[build.key] = build.run! - end end |