mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
82e973dfd5
This pull request addresses these 17 failures when tested with Oracle enhanced adapter. All of these failures are due to the incorrect number of queries. Here is the first one. ```ruby $ ARCONN=oracle bin/test test/cases/nested_attributes_test.rb:1086 Using oracle Run options: --seed 27985 F Finished in 0.874514s, 1.1435 runs/s, 1.1435 assertions/s. 1) Failure: TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations#test_circular_references_do_not_perform_unnecessary_queries [/home/yahonda/git/rails/activerecord/test/cases/nested_attributes_test.rb:1086]: 6 instead of 3 queries were executed. Queries: select us.sequence_name from all_sequences us where us.sequence_owner = :owner and us.sequence_name = :sequence_name INSERT INTO "SHIPS" ("NAME", "ID") VALUES (:a1, :a2) select us.sequence_name from all_sequences us where us.sequence_owner = :owner and us.sequence_name = :sequence_name INSERT INTO "SHIP_PARTS" ("NAME", "SHIP_ID", "UPDATED_AT", "ID") VALUES (:a1, :a2, :a3, :a4) select us.sequence_name from all_sequences us where us.sequence_owner = :owner and us.sequence_name = :sequence_name INSERT INTO "TREASURES" ("LOOTER_ID", "LOOTER_TYPE", "SHIP_ID", "ID") VALUES (:a1, :a2, :a3, :a4). Expected: 3 Actual: 6 1 runs, 1 assertions, 1 failures, 0 errors, 0 skips $ ``` Since https://github.com/rsim/oracle-enhanced/pull/1490 Oracle enhanced adapter drops its own schema caching called OracleEnhancedAdapter.cache_columns` to use Rails scehema cache generated by `db:schema:cache:dump`. By this change some extra sql statements executed at ActiveRecord unit test, which can be fixed by adding the sql statement to `oracle_ignored`. * All 17 failures fixed by this pull request: ```ruby ARCONN=oracle bin/test test/cases/nested_attributes_test.rb:1086 ARCONN=oracle bin/test test/cases/locking_test.rb:308 ARCONN=oracle bin/test test/cases/locking_test.rb:365 ARCONN=oracle bin/test test/cases/dirty_test.rb:351 ARCONN=oracle bin/test test/cases/dirty_test.rb:334 ARCONN=oracle bin/test test/cases/autosave_association_test.rb:192 ARCONN=oracle bin/test test/cases/associations/has_many_associations_test.rb:950 ARCONN=oracle bin/test test/cases/associations/has_many_associations_test.rb:1059 ARCONN=oracle bin/test test/cases/autosave_association_test.rb:627 ARCONN=oracle bin/test test/cases/autosave_association_test.rb:607 ARCONN=oracle bin/test test/cases/autosave_association_test.rb:617 ARCONN=oracle bin/test test/cases/autosave_association_test.rb:641 ARCONN=oracle bin/test test/cases/associations/has_many_through_associations_test.rb:546 ARCONN=oracle bin/test test/cases/associations/has_many_through_associations_test.rb:297 ARCONN=oracle bin/test test/cases/associations/has_many_through_associations_test.rb:586 ARCONN=oracle bin/test test/cases/associations/has_many_through_associations_test.rb:172 ARCONN=oracle bin/test test/cases/associations/has_many_through_associations_test.rb:269 ```
138 lines
4.8 KiB
Ruby
138 lines
4.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "active_support/test_case"
|
|
require "active_support/testing/autorun"
|
|
require "active_support/testing/method_call_assertions"
|
|
require "active_support/testing/stream"
|
|
require "active_record/fixtures"
|
|
|
|
require "cases/validations_repair_helper"
|
|
|
|
module ActiveRecord
|
|
# = Active Record Test Case
|
|
#
|
|
# Defines some test assertions to test against SQL queries.
|
|
class TestCase < ActiveSupport::TestCase #:nodoc:
|
|
include ActiveSupport::Testing::MethodCallAssertions
|
|
include ActiveSupport::Testing::Stream
|
|
include ActiveRecord::TestFixtures
|
|
include ActiveRecord::ValidationsRepairHelper
|
|
|
|
self.fixture_path = FIXTURES_ROOT
|
|
self.use_instantiated_fixtures = false
|
|
self.use_transactional_tests = true
|
|
|
|
def create_fixtures(*fixture_set_names, &block)
|
|
ActiveRecord::FixtureSet.create_fixtures(ActiveRecord::TestCase.fixture_path, fixture_set_names, fixture_class_names, &block)
|
|
end
|
|
|
|
def teardown
|
|
SQLCounter.clear_log
|
|
end
|
|
|
|
def capture_sql
|
|
SQLCounter.clear_log
|
|
yield
|
|
SQLCounter.log_all.dup
|
|
end
|
|
|
|
def assert_sql(*patterns_to_match)
|
|
capture_sql { yield }
|
|
ensure
|
|
failed_patterns = []
|
|
patterns_to_match.each do |pattern|
|
|
failed_patterns << pattern unless SQLCounter.log_all.any? { |sql| pattern === sql }
|
|
end
|
|
assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}"
|
|
end
|
|
|
|
def assert_queries(num = 1, options = {})
|
|
ignore_none = options.fetch(:ignore_none) { num == :any }
|
|
SQLCounter.clear_log
|
|
x = yield
|
|
the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log
|
|
if num == :any
|
|
assert_operator the_log.size, :>=, 1, "1 or more queries expected, but none were executed."
|
|
else
|
|
mesg = "#{the_log.size} instead of #{num} queries were executed.#{the_log.size == 0 ? '' : "\nQueries:\n#{the_log.join("\n")}"}"
|
|
assert_equal num, the_log.size, mesg
|
|
end
|
|
x
|
|
end
|
|
|
|
def assert_no_queries(options = {}, &block)
|
|
options.reverse_merge! ignore_none: true
|
|
assert_queries(0, options, &block)
|
|
end
|
|
|
|
def assert_column(model, column_name, msg = nil)
|
|
assert has_column?(model, column_name), msg
|
|
end
|
|
|
|
def assert_no_column(model, column_name, msg = nil)
|
|
assert_not has_column?(model, column_name), msg
|
|
end
|
|
|
|
def has_column?(model, column_name)
|
|
model.reset_column_information
|
|
model.column_names.include?(column_name.to_s)
|
|
end
|
|
end
|
|
|
|
class PostgreSQLTestCase < TestCase
|
|
def self.run(*args)
|
|
super if current_adapter?(:PostgreSQLAdapter)
|
|
end
|
|
end
|
|
|
|
class Mysql2TestCase < TestCase
|
|
def self.run(*args)
|
|
super if current_adapter?(:Mysql2Adapter)
|
|
end
|
|
end
|
|
|
|
class SQLite3TestCase < TestCase
|
|
def self.run(*args)
|
|
super if current_adapter?(:SQLite3Adapter)
|
|
end
|
|
end
|
|
|
|
class SQLCounter
|
|
class << self
|
|
attr_accessor :ignored_sql, :log, :log_all
|
|
def clear_log; self.log = []; self.log_all = []; end
|
|
end
|
|
|
|
clear_log
|
|
|
|
self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
|
|
|
|
# FIXME: this needs to be refactored so specific database can add their own
|
|
# ignored SQL, or better yet, use a different notification for the queries
|
|
# instead examining the SQL content.
|
|
oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im, /^\s*select .* from all_sequences/im]
|
|
mysql_ignored = [/^SHOW FULL TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /, /^\s*SELECT (?:column_name|table_name)\b.*\bFROM information_schema\.(?:key_column_usage|tables)\b/im]
|
|
postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
|
|
sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im]
|
|
|
|
[oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
|
|
ignored_sql.concat db_ignored_sql
|
|
end
|
|
|
|
attr_reader :ignore
|
|
|
|
def initialize(ignore = Regexp.union(self.class.ignored_sql))
|
|
@ignore = ignore
|
|
end
|
|
|
|
def call(name, start, finish, message_id, values)
|
|
return if values[:cached]
|
|
|
|
sql = values[:sql]
|
|
self.class.log_all << sql
|
|
self.class.log << sql unless ignore.match?(sql)
|
|
end
|
|
end
|
|
|
|
ActiveSupport::Notifications.subscribe("sql.active_record", SQLCounter.new)
|
|
end
|