pseudo_cleaner 0.0.25

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ N2YxYjQzYTAzZDJkN2M0YzQ0YjE1ODgwMjA3Y2ZlYWNiNTQzNTk4NQ==
5
+ data.tar.gz: !binary |-
6
+ ZGI3ODU3NGEyOTE5NGUxODBhNjhlMjk2MTM3ODA5OGZjZDA3NzU4ZQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MDUxNTk3ZDNiM2RlZDJmYzM5NGRlZjhhMzQ5Yjg1YWY4OTE2MmQ5ZWY1ZWVj
10
+ MTM2MGM0MzQ3MTNkYWIxNDQyYjUxZGQ1OTVkYTg5YjJmNTQ1ZTk2MmExNmNj
11
+ YzY1MWE5ZjdkMWY3YmMxNjQ1ODgxYmQxNzQ0ZGM4OWIyYzMzZmM=
12
+ data.tar.gz: !binary |-
13
+ NzBlNGYwNmU4ZTA0MDM4MzA3YTRjYjI4NzY4YTJlZTk1YjJhMTM2ZjA3YjUw
14
+ MjlmMDUyNzZlNzI2NmE4ZGUyNDQ5ODBlNjM5NDEyNGQwMzMwYzc2ODk1Zjk5
15
+ NjRhY2VhNTdlM2RmNWNmYjQ2MGZhOGI1NmIyY2EwMWVlMmMwMmM=
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ .idea/
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pseudo_cleaner.gemspec
4
+ gemspec
5
+
6
+ gem "gem-release"
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 RealNobody
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # PseudoCleaner
2
+
3
+ The [Database Cleaner gem](https://github.com/DatabaseCleaner/database_cleaner) is a wonderful tool,
4
+ and I've used it for years. I would highly recommend it. It is a well written and a well used and therefore tested
5
+ tool that you can rely on.
6
+
7
+ However, it is (quite rightly) a very conservative tool. I often run into situations where it doesn't quite fit my
8
+ needs. there are often times when I cannot use transactions (such as when I am doing Cucumber tests with Capybara to
9
+ feature test my site), but when truncating my tables just isn't reasonable or practical.
10
+
11
+ So, I came up with a compromise that works for a large number of tables and databases that I've worked with. The
12
+ thing is that this solution is not like DatabaseCleaner in that it isn't conservative,
13
+ and it doesn't guarantee much. This solution might not clean the database entirely between calls.
14
+
15
+ The thing is, the database doesn't have to be entirely clean after every call for most tests,
16
+ just clean enough is often good enough.
17
+
18
+ So, what is it that the PseudoCleaner does and why is it good enough?
19
+
20
+ The cleaner relies on the fact that most databases use 2 common defaults in most tables (well,
21
+ most tables that simple tests that rely on the workings of a cleaner anyway...) Those features are an auto-increment
22
+ `id` column, and/or a `created_at`/`updated_at` columns.
23
+
24
+ Using these, when a test starts the cleaner iterates through the tables and saves the current `MAX(id)`,
25
+ `MAX(created_at)`, and `MAX(updated_at)` values for a table. When a test ends, the cleaner iterates through the
26
+ tables again and deletes anything that is new. It will then report (optionally) on any records that have been
27
+ updated but haven't been cleaned up. In future versions, I have plans for it to also report (optionally) on any
28
+ referential integrity holes.
29
+
30
+ Because the PsuedoCleaner already uses the [SortedSeeder gem](https://github.com/RealNobody/sorted_seeder) to determine what
31
+ order to delete records in when cleaning up, when the database is truncated, it will re-seed the database using the
32
+ SortedSeeders seed_all function.
33
+
34
+ ## Installation
35
+
36
+ Add this line to your application's Gemfile in the test group:
37
+
38
+ gem 'pseudo_cleaner'
39
+
40
+ OR
41
+
42
+ gem 'pseudo_cleaner', '~> 0.0.1', :git => "git@github.com/RealNobody/pseudo_cleaner.git"
43
+
44
+ And then execute:
45
+
46
+ $ bundle
47
+
48
+ Or install it yourself as:
49
+
50
+ $ gem install pseudo_cleaner
51
+
52
+ ## Usage
53
+
54
+ There are multiple ways to use the PseudoCleaner. The main intended usages are detailed here:
55
+
56
+ ### Rspec
57
+
58
+ Rspect integration is built in to make using the PseudoCleaner simple and straight-forward.
59
+ To integrate the PseudoCleaner with Rspec, simply add the following lines to `spec_helper.rb`:
60
+
61
+ require 'pseudo_cleaner'
62
+ require 'pseudo_cleaner/rspec'
63
+
64
+ All tests will now by default use DatabaseCleaner with the `:transaction` strategy. For most tests, this will wrap
65
+ the test in a transaction, and roll back the transaction at the end of the test.
66
+
67
+ If a test is a feature test which uses Capybara using the `:js` tag, that test will be switched to not use
68
+ DatabaseCleaner. Instead, the test will use the `:pseudo_delete` strategy which as described will store the state of
69
+ the tables before the test then delete any new records at the end of the test.
70
+
71
+ If you want or need a specific strategy for a single test, you can specify the metadata tag: `:strategy` in the test
72
+ to change the behavior of the test. This tag accepts the following values:
73
+
74
+ * :none - Do not use any cleaning on this test run.
75
+ * :psedu_delete - Do not use DatabaseCleaner and clean tables individually.
76
+ * :truncation - Use the :truncation strategy with DatabaseCleaner and re-seed the database after truncation.
77
+ * :deletion - Use the :deletion strategy with DatabaseCleaner and re-seed the database after deletion.
78
+ * :transaction - Use the :transaction strategy with DatabaseCleaner.
79
+
80
+ Example:
81
+
82
+ it "is a test", strategy: :truncation do
83
+ expect(something).to work
84
+ end
85
+
86
+ ### Cucumber
87
+
88
+ Cucumber integration similar to Rspec integration is planned, but not implemented yet.
89
+
90
+ ### Manual
91
+
92
+ There are two ways to use the cleaner manually. When you use the cleaner manually, you are only using the
93
+ PseduoCleaner. You do not get DatabaseCleaner integration like you get automatically with Rspec. This will create
94
+ table cleaners and any custom defined cleaners and execute them.
95
+
96
+ NOTE: When using the tool manually, if the strategy is any strategy other than `:pseudo_delete`, the default
97
+ cleaners will not do anything. The strategy may still be useful though if you have any custom cleaners.
98
+
99
+ *PseudoCleaner::MasterCleaner.clean* This function cleans the code executed inside a block and takes two parameters.
100
+ The first parameter takes the values `:test` or `:suite`. This is used to determine if the cleaner is wrapped around
101
+ a set of tests or a single test. The default implementations provided do not distinguish between these, but custom
102
+ cleaners might. The second parameter is the strategy to use.
103
+
104
+ PseudoCleaner::MasterCleaner.clean(:test, :pseudo_delete) do
105
+ # Your code here
106
+ end
107
+
108
+ *PseudoCleaner::MasterCleaner.start_test* This takes one parameter that is the type of the cleaner this is (`:test` or
109
+ `:suite`). This creates a cleaner object that can be started and ended around the code to be cleaned. You specify
110
+ the strategy for the tests when you start the cleaner.
111
+
112
+ pseudo_cleaner = PseudoCleaner::MasterCleaner.start_test :pseudo_delete
113
+ # Your code here
114
+ pseudo_cleaner.end
115
+
116
+ ## Custom Cleaners
117
+
118
+ This system is built to clean the database after calls.
119
+
120
+ If you have additional actions which need to be done either before and/or after tests run to clean up resources, you
121
+ can create custom cleaners and place them in the `db/cleaners` folder. You can also create a custom cleaner for a
122
+ specific table by placing the cleaner in the same folder and naming it `<TableName>Cleaner`.
123
+
124
+ Cleaners must be instantiated, and will be initialized with the following prototype:
125
+ `initialize(start_method, end_method, table, options = {})`
126
+
127
+ Cleaners must also include one or more of the following funcitons:
128
+
129
+ * test_start test_strategy
130
+ * test_end test_strategy
131
+ * suite_start test_strategy
132
+ * suite_end test_strategy
133
+
134
+ A Cleaner can adjust when it is called in relation to other cleaners by overriding the instance method `<=>`
135
+
136
+ ## Contributing
137
+
138
+ 1. Fork it
139
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
140
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
141
+ 4. Push to the branch (`git push origin my-new-feature`)
142
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,12 @@
1
+ require "sorted_seeder"
2
+ require "colorize"
3
+ require "database_cleaner"
4
+ require "pseudo_cleaner/version"
5
+ require "pseudo_cleaner/configuration"
6
+ require "pseudo_cleaner/table_cleaner"
7
+ require "pseudo_cleaner/master_cleaner"
8
+ require "pseudo_cleaner/logger"
9
+
10
+ module PseudoCleaner
11
+ # Your code goes here...
12
+ end
@@ -0,0 +1,44 @@
1
+ module PseudoCleaner
2
+ class Configuration
3
+ @@current_instance = nil
4
+
5
+ # A simple configuration class for the PseudoCleaner
6
+ #
7
+ # Configurations:
8
+ # output_diagnostics - true/false
9
+ # if true, the system will use puts to output information about what it is doing...
10
+ attr_accessor :output_diagnostics
11
+ attr_accessor :clean_database_before_tests
12
+ attr_accessor :reset_auto_increment
13
+ attr_accessor :single_cleaner_set
14
+ attr_accessor :post_transaction_analysis
15
+
16
+ def self.current_instance
17
+ @@current_instance ||= PseudoCleaner::Configuration.new
18
+ end
19
+
20
+ def initialize
21
+ @output_diagnostics = false # false to keep the noise level down...
22
+ @clean_database_before_tests = false # false because I think it will annoy developers...
23
+ @reset_auto_increment = true # true because I think it should be done
24
+ @single_cleaner_set = true # true because I hope it will improve performance
25
+ @post_transaction_analysis = false # should only be set true if you are searching for a problem
26
+ end
27
+
28
+ def self.db_connection=(connection)
29
+ @db_connection = connection
30
+ end
31
+
32
+ def self.db_connection(type)
33
+ if @db_connection || type.nil?
34
+ @db_connection
35
+ else
36
+ if type == :sequel
37
+ Sequel::DATABASES[0]
38
+ else
39
+ ActiveRecord::Base
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ first_test_run = false
2
+
3
+ # turn off Cucumber's default usage of DatabaseCleaner
4
+ Cucumber::Rails::Database.autorun_database_cleaner = false
5
+
6
+ Before do |scenario|
7
+ unless first_test_run
8
+ first_test_run = true
9
+ # before tests run...
10
+ # We start suite in case a custom cleaner wants/needs to.
11
+ if PseudoCleaner::Configuration.current_instance.clean_database_before_tests
12
+ PseudoCleaner::MasterCleaner.reset_database
13
+ else
14
+ PseudoCleaner::MasterCleaner.start_suite
15
+ end
16
+
17
+ DatabaseCleaner.strategy = :transaction
18
+ end
19
+ end
20
+
21
+ Before("~@truncation", "~@deletion") do |scenario|
22
+ PseudoCleaner::MasterCleaner.start_example(scenario, :pseudo_delete)
23
+ end
24
+
25
+ Before("@truncation") do |scenario|
26
+ PseudoCleaner::MasterCleaner.start_example(scenario, :truncation)
27
+ end
28
+
29
+ Before("@deletion", "~@truncation") do |scenario|
30
+ PseudoCleaner::MasterCleaner.start_example(scenario, :deletion)
31
+ end
32
+
33
+ Before("@none") do |scenario|
34
+ PseudoCleaner::MasterCleaner.start_example(scenario, :none)
35
+ end
36
+
37
+ After do |scenario|
38
+ PseudoCleaner::MasterCleaner.end_example(scenario)
39
+ end
40
+
41
+ at_exit do
42
+ # We end suite in case a custom cleaner wants/needs to.
43
+ PseudoCleaner::MasterCleaner.end_suite
44
+ end
@@ -0,0 +1,9 @@
1
+ module PseudoCleaner
2
+ class Logger
3
+ class << self
4
+ def write(log_output)
5
+ puts(log_output)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,380 @@
1
+ require "sorted_seeder"
2
+
3
+ module PseudoCleaner
4
+ class MasterCleaner
5
+ @@suite_cleaner = nil
6
+ @@cleaner_classes = nil
7
+ @@cleaner_classes_sorted = false
8
+
9
+ CLEANING_STRATEGIES = [:transaction, :truncation, :deletion, :pseudo_delete, :none]
10
+ DB_CLEANER_CLEANING_STRATEGIES =
11
+ {
12
+ transaction: :transaction,
13
+ truncation: :truncation,
14
+ deletion: :deletion,
15
+ pseudo_delete: :transaction
16
+ }
17
+ VALID_TEST_TYPES = [:suite, :test]
18
+
19
+ VALID_START_METHODS = [:test_start, :suite_start]
20
+ VALID_END_METHODS = [:test_end, :suite_end]
21
+ VALID_TEST_METHODS = VALID_START_METHODS + VALID_END_METHODS
22
+
23
+ class << self
24
+ def start_suite
25
+ if @@suite_cleaner
26
+ @@suite_cleaner.reset_suite
27
+ end
28
+ @@suite_cleaner = PseudoCleaner::MasterCleaner.new(:suite)
29
+ @@suite_cleaner.start :pseudo_delete
30
+ @@suite_cleaner
31
+ end
32
+
33
+ def start_example(example_class, strategy)
34
+ pseudo_cleaner_data = {}
35
+ pseudo_cleaner_data[:test_strategy] = strategy
36
+
37
+ unless strategy == :none
38
+ raise "invalid strategy" unless PseudoCleaner::MasterCleaner::DB_CLEANER_CLEANING_STRATEGIES.has_key? strategy
39
+
40
+ DatabaseCleaner.strategy = PseudoCleaner::MasterCleaner::DB_CLEANER_CLEANING_STRATEGIES[strategy]
41
+ unless [:pseudo_delete].include? strategy
42
+ if Object.const_defined?("ActiveRecord", false) && ActiveRecord.const_defined?("Base", false)
43
+ DatabaseCleaner[:active_record, connection: PseudoCleaner::Configuration.db_connection(:active_record)].
44
+ start
45
+ elsif Object.const_defined?("Sequel", false) && Sequel.const_defined?("Model", false)
46
+ DatabaseCleaner[:sequel, connection: PseudoCleaner::Configuration.db_connection(:sequel)].start
47
+ end
48
+ # DatabaseCleaner.start
49
+ end
50
+
51
+ pseudo_cleaner_data[:pseudo_state] = PseudoCleaner::MasterCleaner.start_test strategy
52
+ end
53
+
54
+ example_class.instance_variable_set(:@pseudo_cleaner_data, pseudo_cleaner_data)
55
+ end
56
+
57
+ def end_example(example_class)
58
+ pseudo_cleaner_data = example_class.instance_variable_get(:@pseudo_cleaner_data)
59
+
60
+ unless pseudo_cleaner_data[:test_strategy] == :none
61
+ unless [:pseudo_delete].include? pseudo_cleaner_data[:test_strategy]
62
+ if Object.const_defined?("ActiveRecord", false) && ActiveRecord.const_defined?("Base", false)
63
+ DatabaseCleaner[:active_record, connection: PseudoCleaner::Configuration.db_connection(:active_record)].
64
+ clean
65
+ elsif Object.const_defined?("Sequel", false) && Sequel.const_defined?("Model", false)
66
+ DatabaseCleaner[:sequel, connection: PseudoCleaner::Configuration.db_connection(:sequel)].clean
67
+ end
68
+ # DatabaseCleaner.clean
69
+ end
70
+
71
+ case pseudo_cleaner_data[:test_strategy]
72
+ when :deletion, :truncation
73
+ PseudoCleaner::MasterCleaner.database_reset
74
+ end
75
+
76
+ pseudo_cleaner_data[:pseudo_state].end test_type: :test, test_strategy: pseudo_cleaner_data[:test_strategy]
77
+ end
78
+ end
79
+
80
+ def end_suite
81
+ @@suite_cleaner.end test_strategy: :pseudo_delete if @@suite_cleaner
82
+ end
83
+
84
+ def start_test test_strategy
85
+ raise "Invalid test_strategy \"#{test_strategy}\"" unless CLEANING_STRATEGIES.include? test_strategy
86
+
87
+ cleaner = if PseudoCleaner::Configuration.current_instance.single_cleaner_set
88
+ @@suite_cleaner
89
+ else
90
+ PseudoCleaner::MasterCleaner.new(:test)
91
+ end
92
+
93
+ cleaner.start test_strategy, test_type: :test, test_strategy: test_strategy
94
+
95
+ cleaner
96
+ end
97
+
98
+ def clean(test_type, test_strategy, &block)
99
+ raise "Invalid test_type \"#{test_type}\"" unless [:suite, :test].include? test_type
100
+ raise "Invalid test_strategy \"#{test_strategy}\"" unless CLEANING_STRATEGIES.include? test_strategy
101
+
102
+ master_cleaner = PseudoCleaner::MasterCleaner.send "start_#{test_type}", test_strategy
103
+
104
+ body_error = nil
105
+ begin
106
+ block.yield master_cleaner
107
+ rescue => error
108
+ body_error = error
109
+ end
110
+
111
+ master_cleaner.end test_type: test_type, test_strategy: test_strategy
112
+
113
+ raise body_error if body_error
114
+ end
115
+
116
+ def reset_database
117
+ DatabaseCleaner.clean_with(:truncation)
118
+
119
+ PseudoCleaner::MasterCleaner.database_reset
120
+ end
121
+
122
+ def database_reset
123
+ PseudoCleaner::MasterCleaner.seed_data
124
+ PseudoCleaner::MasterCleaner.start_suite
125
+ end
126
+
127
+ def seed_data
128
+ PseudoCleaner::Logger.write("Re-seeding database".red.on_light_white)
129
+ SortedSeeder::Seeder.seed_all(PseudoCleaner::Configuration.db_connection(nil))
130
+ end
131
+
132
+ def process_exception(error)
133
+ PseudoCleaner::Logger.write(" An exception has occurred:".red.on_light_white)
134
+ PseudoCleaner::Logger.write("")
135
+ PseudoCleaner::Logger.write(error.to_s)
136
+ PseudoCleaner::Logger.write(error.backtrace.join("\n")) if error.backtrace
137
+ end
138
+
139
+ def cleaner_class(table_name)
140
+ seed_class_name = "#{table_name.to_s.classify}Cleaner"
141
+ seed_class_base_name = seed_class_name.demodulize
142
+ base_module = seed_class_name.split("::")[0..-2].join("::")
143
+ base_module_classes = [Object]
144
+
145
+ unless base_module.blank?
146
+ base_module_classes = base_module_classes.unshift base_module.constantize
147
+ end
148
+
149
+ return_class = nil
150
+ 2.times do
151
+ base_module_classes.each do |base_class|
152
+ if (base_class.const_defined?(seed_class_base_name, false))
153
+ if base_class == Object
154
+ return_class = seed_class_base_name.constantize
155
+ else
156
+ return_class = "#{base_class.name}::#{seed_class_base_name}".constantize
157
+ end
158
+
159
+ break
160
+ end
161
+ end
162
+
163
+ break if return_class
164
+
165
+ seeder_file = "db/cleaners/"
166
+ seeder_file += base_module.split("::").map { |module_name| module_name.underscore }.join("/")
167
+ seeder_file += "/" unless seeder_file[-1] == "/"
168
+ seeder_file += seed_class_base_name.underscore
169
+ seeder_file += ".rb"
170
+ seeder_file = File.join(Rails.root, seeder_file)
171
+
172
+ break unless File.exists?(seeder_file)
173
+
174
+ require seeder_file
175
+ end
176
+
177
+ # unless return_class &&
178
+ # VALID_TEST_METHODS.any? { |test_method| return_class.instance_methods.include?(test_method.to_sym) }
179
+ # return_class = table
180
+ # end
181
+
182
+ unless return_class &&
183
+ VALID_TEST_METHODS.any? { |test_method| return_class.instance_methods.include?(test_method.to_sym) }
184
+ return_class = PseudoCleaner::TableCleaner
185
+ end
186
+
187
+ return_class
188
+ end
189
+
190
+ def cleaner_classes
191
+ unless @@cleaner_classes
192
+ @@cleaner_classes = []
193
+
194
+ PseudoCleaner::MasterCleaner.create_table_cleaners
195
+ PseudoCleaner::MasterCleaner.create_custom_cleaners
196
+ end
197
+
198
+ @@cleaner_classes
199
+ end
200
+
201
+ def create_table_cleaners(options = {})
202
+ SortedSeeder::Seeder.create_order(PseudoCleaner::Configuration.db_connection(nil)).each do |table|
203
+ cleaner_class = PseudoCleaner::MasterCleaner.cleaner_class(table.name)
204
+ if cleaner_class
205
+ PseudoCleaner::MasterCleaner.cleaner_classes << [table, nil, cleaner_class]
206
+ end
207
+ end
208
+ if SortedSeeder::Seeder.respond_to?(:unclassed_tables)
209
+ SortedSeeder::Seeder.unclassed_tables(PseudoCleaner::Configuration.db_connection(nil)).each do |table_name|
210
+ cleaner_class = PseudoCleaner::MasterCleaner.cleaner_class(table_name)
211
+ if cleaner_class
212
+ PseudoCleaner::MasterCleaner.cleaner_classes << [nil, table_name, cleaner_class]
213
+ end
214
+ end
215
+ end
216
+ end
217
+
218
+ def create_custom_cleaners(options = {})
219
+ if Object.const_defined?("Rails", false)
220
+ cleaner_root = Rails.root.join("db/cleaners/").to_s
221
+ cleaner_files = Dir[Rails.root.join("db/cleaners/**/*.rb")]
222
+
223
+ cleaner_files.each do |cleaner_file|
224
+ class_name = File.basename(cleaner_file, ".rb").classify
225
+
226
+ check_class, full_module_name = find_file_class(cleaner_file, cleaner_root)
227
+ unless check_class && check_class.const_defined?(class_name, false)
228
+ require cleaner_file
229
+ check_class, full_module_name = find_file_class(cleaner_file, cleaner_root)
230
+ end
231
+
232
+ if check_class
233
+ full_module_name << class_name
234
+ if check_class.const_defined?(class_name, false)
235
+ check_class = full_module_name.join("::").constantize
236
+ else
237
+ check_class = nil
238
+ end
239
+ end
240
+
241
+ if check_class &&
242
+ PseudoCleaner::MasterCleaner::VALID_TEST_METHODS.
243
+ any? { |test_method| check_class.instance_methods.include?(test_method) }
244
+ unless PseudoCleaner::MasterCleaner.cleaner_classes.any? { |cleaner| check_class == cleaner[2] }
245
+ PseudoCleaner::MasterCleaner.cleaner_classes << [nil, nil, check_class]
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ def find_file_class(seeder_file, seeder_root)
253
+ check_class = Object
254
+ full_module_name = []
255
+
256
+ File.dirname(seeder_file.to_s[seeder_root.length..-1]).split("/").map do |module_element|
257
+ if (module_element != ".")
258
+ full_module_name << module_element.classify
259
+ if check_class.const_defined?(full_module_name[-1], false)
260
+ check_class = full_module_name.join("::").constantize
261
+ else
262
+ check_class = nil
263
+ break
264
+ end
265
+ end
266
+ end
267
+
268
+ return check_class, full_module_name
269
+ end
270
+
271
+ def review_rows(&block)
272
+ @@suite_cleaner.review_rows &block
273
+ end
274
+ end
275
+
276
+ def initialize(test_type)
277
+ raise "Invalid test type. Must be one of: #{VALID_TEST_TYPES}" unless VALID_TEST_TYPES.include?(test_type)
278
+
279
+ @test_type = test_type
280
+ end
281
+
282
+ def start(test_strategy, options = {})
283
+ test_type = options[:test_type] || @test_type
284
+
285
+ unless @cleaners
286
+ @cleaners = []
287
+ @test_strategy = test_strategy
288
+
289
+ start_method = "#{test_type}_start".to_sym
290
+ end_method = "#{test_type}_end".to_sym
291
+
292
+ PseudoCleaner::MasterCleaner.cleaner_classes.each do |clean_class|
293
+ table = clean_class[0]
294
+ table ||= clean_class[1]
295
+
296
+ begin
297
+ @cleaners << clean_class[2].new(start_method, end_method, table, options)
298
+ rescue Exception => error
299
+ puts error.to_s
300
+ raise error
301
+ end
302
+ end
303
+
304
+ unless @@cleaner_classes_sorted
305
+ seed_sorts = @cleaners.map { |cleaner| SortedSeeder::Seeder::SeederSorter.new(cleaner) }
306
+ seed_sorts.sort!
307
+
308
+ @cleaners = seed_sorts.map(&:seed_base_object)
309
+
310
+ sorted_classes = []
311
+ @cleaners.each do |cleaner|
312
+ cleaner_class = PseudoCleaner::MasterCleaner.cleaner_classes.detect do |unsorted_cleaner|
313
+ if cleaner.class == unsorted_cleaner[2]
314
+ if unsorted_cleaner[2] == PseudoCleaner::TableCleaner
315
+ cleaner.table == unsorted_cleaner[0] || cleaner.table == unsorted_cleaner[1]
316
+ else
317
+ true
318
+ end
319
+ end
320
+ end
321
+
322
+ sorted_classes << cleaner_class
323
+ end
324
+
325
+ @@cleaner_classes = sorted_classes
326
+ @@cleaner_classes_sorted = true
327
+ end
328
+ end
329
+
330
+ start_all_cleaners options
331
+ end
332
+
333
+ def end(options = {})
334
+ test_type = options[:test_type] || @test_type
335
+ if PseudoCleaner::Configuration.current_instance.output_diagnostics ||
336
+ PseudoCleaner::Configuration.current_instance.post_transaction_analysis
337
+ PseudoCleaner::Logger.write("Cleaning #{test_type}")
338
+ end
339
+ end_all_cleaners options
340
+ end
341
+
342
+ def start_all_cleaners(options)
343
+ test_type = options[:test_type] || @test_type
344
+ test_strategy = options[:test_strategy] || @test_strategy
345
+ run_all_cleaners("#{test_type}_start".to_sym, @cleaners, test_strategy)
346
+ end
347
+
348
+ def end_all_cleaners(options)
349
+ test_type = options[:test_type] || @test_type
350
+ test_strategy = options[:test_strategy] || @test_strategy
351
+ run_all_cleaners("#{test_type}_end".to_sym, @cleaners.reverse, test_strategy)
352
+ end
353
+
354
+ def reset_suite
355
+ run_all_cleaners(:reset_suite, @cleaners.reverse)
356
+ end
357
+
358
+ def run_all_cleaners(cleaner_function, cleaners, *args, &block)
359
+ last_error = nil
360
+
361
+ cleaners.each do |cleaner|
362
+ begin
363
+ if cleaner.respond_to?(cleaner_function)
364
+ cleaner.send(cleaner_function, *args, &block)
365
+ end
366
+ rescue => error
367
+ PseudoCleaner::MasterCleaner.process_exception(last_error) if last_error
368
+
369
+ last_error = error
370
+ end
371
+ end
372
+
373
+ raise last_error if last_error
374
+ end
375
+
376
+ def review_rows(&block)
377
+ run_all_cleaners(:review_rows, @cleaners, &block)
378
+ end
379
+ end
380
+ end