@@ -130,25 +130,53 @@ def setup_fixtures(config = ActiveRecord::Base)
130
130
end
131
131
132
132
@fixture_cache = { }
133
- @fixture_connections = [ ]
133
+ @fixture_connections = { }
134
134
@fixture_cache_key = [ self . class . fixture_table_names . dup , self . class . fixture_paths . dup , self . class . fixture_class_names . dup ]
135
135
@@already_loaded_fixtures ||= { }
136
136
@connection_subscriber = nil
137
137
@saved_pool_configs = Hash . new { |hash , key | hash [ key ] = { } }
138
138
139
- # Load fixtures once and begin transaction.
140
139
if run_in_transaction?
140
+ # Load fixtures once and begin transaction.
141
141
@loaded_fixtures = @@already_loaded_fixtures [ @fixture_cache_key ]
142
142
unless @loaded_fixtures
143
143
@@already_loaded_fixtures . clear
144
144
@loaded_fixtures = @@already_loaded_fixtures [ @fixture_cache_key ] = load_fixtures ( config )
145
145
end
146
146
147
+ setup_transactional_fixtures
148
+ else
149
+ # Load fixtures for every test.
150
+ ActiveRecord ::FixtureSet . reset_cache
151
+ invalidate_already_loaded_fixtures
152
+ @loaded_fixtures = load_fixtures ( config )
153
+ end
154
+
155
+ # Instantiate fixtures for every test if requested.
156
+ instantiate_fixtures if use_instantiated_fixtures
157
+ end
158
+
159
+ def teardown_fixtures
160
+ # Rollback changes if a transaction is active.
161
+ if run_in_transaction?
162
+ teardown_transactional_fixtures
163
+ else
164
+ ActiveRecord ::FixtureSet . reset_cache
165
+ end
166
+
167
+ ActiveRecord ::Base . connection_handler . clear_active_connections! ( :all )
168
+ end
169
+
170
+ private
171
+ def setup_transactional_fixtures
172
+ setup_shared_connection_pool
173
+
147
174
# Begin transactions for connections already established
148
- @fixture_connections = enlist_fixture_connections
149
- @fixture_connections . each do |connection |
150
- connection . begin_transaction joinable : false , _lazy : false
151
- connection . pool . lock_thread = true if lock_threads
175
+ @fixture_connections = ActiveRecord ::Base . connection_handler . connection_pool_list ( :writing ) . to_h do |pool |
176
+ pool . lock_thread = true if lock_threads
177
+ connection = pool . connection
178
+ transaction = connection . begin_transaction ( joinable : false , _lazy : false )
179
+ [ connection , transaction ]
152
180
end
153
181
154
182
# When connections are established in the future, begin a transaction too
@@ -167,50 +195,38 @@ def setup_fixtures(config = ActiveRecord::Base)
167
195
if connection
168
196
setup_shared_connection_pool
169
197
170
- if !@fixture_connections . include? ( connection )
171
- connection . begin_transaction joinable : false , _lazy : false
198
+ if !@fixture_connections . key? ( connection )
172
199
connection . pool . lock_thread = true if lock_threads
173
- @fixture_connections << connection
200
+ @fixture_connections [ connection ] = connection . begin_transaction ( joinable : false , _lazy : false )
174
201
end
175
202
end
176
203
end
177
204
end
178
-
179
- # Load fixtures for every test.
180
- else
181
- ActiveRecord ::FixtureSet . reset_cache
182
- @@already_loaded_fixtures . clear
183
- @loaded_fixtures = load_fixtures ( config )
184
205
end
185
206
186
- # Instantiate fixtures for every test if requested.
187
- instantiate_fixtures if use_instantiated_fixtures
188
- end
189
-
190
- def teardown_fixtures
191
- # Rollback changes if a transaction is active.
192
- if run_in_transaction?
207
+ def teardown_transactional_fixtures
193
208
ActiveSupport ::Notifications . unsubscribe ( @connection_subscriber ) if @connection_subscriber
194
- @fixture_connections . each do |connection |
195
- connection . rollback_transaction if connection . transaction_open?
209
+ @fixture_connections . each do |connection , transaction |
210
+ begin
211
+ connection . rollback_transaction ( transaction )
212
+ rescue ActiveRecord ::StatementInvalid
213
+ # Something commited or rolled back the transaction.
214
+ # We can no longer trust the database state is clean.
215
+ invalidate_already_loaded_fixtures
216
+ # We also don't know for sure the connection wasn't
217
+ # mutated in dangerous ways.
218
+ connection . disconnect!
219
+ end
196
220
connection . pool . lock_thread = false
197
221
end
198
222
@fixture_connections . clear
199
223
teardown_shared_connection_pool
200
- else
201
- ActiveRecord ::FixtureSet . reset_cache
202
224
end
203
225
204
- ActiveRecord ::Base . connection_handler . clear_active_connections! ( :all )
205
- end
206
-
207
- def enlist_fixture_connections
208
- setup_shared_connection_pool
209
-
210
- ActiveRecord ::Base . connection_handler . connection_pool_list ( :writing ) . map ( &:connection )
211
- end
226
+ def invalidate_already_loaded_fixtures
227
+ @@already_loaded_fixtures . clear
228
+ end
212
229
213
- private
214
230
# Shares the writing connection pool with connections on
215
231
# other handlers.
216
232
#
0 commit comments