pseudo_cleaner 0.0.25
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +18 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +142 -0
- data/Rakefile +1 -0
- data/lib/pseudo_cleaner.rb +12 -0
- data/lib/pseudo_cleaner/configuration.rb +44 -0
- data/lib/pseudo_cleaner/cucumber.rb +44 -0
- data/lib/pseudo_cleaner/logger.rb +9 -0
- data/lib/pseudo_cleaner/master_cleaner.rb +380 -0
- data/lib/pseudo_cleaner/rspec.rb +45 -0
- data/lib/pseudo_cleaner/spinach.rb +38 -0
- data/lib/pseudo_cleaner/table_cleaner.rb +557 -0
- data/lib/pseudo_cleaner/version.rb +3 -0
- data/pseudo_cleaner.gemspec +26 -0
- metadata +116 -0
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
data/Gemfile
ADDED
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,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
|