mutator_rails 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +37 -0
- data/.codeclimate.yml +20 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +232 -0
- data/LICENSE.md +21 -0
- data/README.md +31 -0
- data/Rakefile +8 -0
- data/app/models/test.rb +7 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/config/mutator_rails.yml +8 -0
- data/defaults.reek +131 -0
- data/lib/mutator_rails.rb +33 -0
- data/lib/mutator_rails/analyze.rb +27 -0
- data/lib/mutator_rails/cleanup.rb +60 -0
- data/lib/mutator_rails/config.rb +34 -0
- data/lib/mutator_rails/full_mutate.rb +39 -0
- data/lib/mutator_rails/guide.rb +69 -0
- data/lib/mutator_rails/list_maker.rb +25 -0
- data/lib/mutator_rails/mutation_log.rb +112 -0
- data/lib/mutator_rails/railtie.rb +18 -0
- data/lib/mutator_rails/single_mutate.rb +134 -0
- data/lib/mutator_rails/statistics.rb +192 -0
- data/lib/mutator_rails/version.rb +5 -0
- data/lib/tasks/mutator/analyze.rake +13 -0
- data/lib/tasks/mutator/cleanup.rake +13 -0
- data/lib/tasks/mutator/mutate_files.rake +12 -0
- data/lib/tasks/mutator/mutator.rake +17 -0
- data/lib/tasks/mutator/statistics.rake +13 -0
- data/log/mutant/analysis.tsv +3 -0
- data/log/mutant/guide.txt +1 -0
- data/log/mutant/models/test.log +92 -0
- data/log/mutant/models/test2.log +92 -0
- data/log/mutant/statistics.txt +19 -0
- data/mutator_rails.gemspec +37 -0
- data/spec/models/test_spec.rb +15 -0
- data/spec/mutator_rails/analyze_spec.rb +26 -0
- data/spec/mutator_rails/cleanup_spec.rb +13 -0
- data/spec/mutator_rails/full_mutate_spec.rb +13 -0
- data/spec/mutator_rails/guide_spec.rb +33 -0
- data/spec/mutator_rails/list_maker_spec.rb +17 -0
- data/spec/mutator_rails/mutation_log_spec.rb +36 -0
- data/spec/mutator_rails/single_mutate_spec.rb +111 -0
- data/spec/mutator_rails/statistics_spec.rb +48 -0
- data/spec/mutator_rails_spec.rb +9 -0
- data/spec/spec_helper.rb +49 -0
- metadata +306 -0
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'mutator_rails'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/defaults.reek
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
---
|
2
|
+
Attribute:
|
3
|
+
enabled: true
|
4
|
+
exclude: []
|
5
|
+
BooleanParameter:
|
6
|
+
enabled: true
|
7
|
+
exclude: []
|
8
|
+
ClassVariable:
|
9
|
+
enabled: true
|
10
|
+
exclude: []
|
11
|
+
ControlParameter:
|
12
|
+
enabled: true
|
13
|
+
exclude: []
|
14
|
+
DataClump:
|
15
|
+
enabled: true
|
16
|
+
exclude: []
|
17
|
+
max_copies: 2
|
18
|
+
min_clump_size: 2
|
19
|
+
DuplicateMethodCall:
|
20
|
+
enabled: true
|
21
|
+
exclude: []
|
22
|
+
max_calls: 1
|
23
|
+
allow_calls: []
|
24
|
+
FeatureEnvy:
|
25
|
+
enabled: true
|
26
|
+
exclude: []
|
27
|
+
InstanceVariableAssumption:
|
28
|
+
enabled: true
|
29
|
+
exclude: []
|
30
|
+
IrresponsibleModule:
|
31
|
+
enabled: true
|
32
|
+
exclude: []
|
33
|
+
LongParameterList:
|
34
|
+
enabled: true
|
35
|
+
exclude: []
|
36
|
+
max_params: 3
|
37
|
+
overrides:
|
38
|
+
initialize:
|
39
|
+
max_params: 5
|
40
|
+
LongYieldList:
|
41
|
+
enabled: true
|
42
|
+
exclude: []
|
43
|
+
max_params: 3
|
44
|
+
ManualDispatch:
|
45
|
+
enabled: true
|
46
|
+
exclude: []
|
47
|
+
ModuleInitialize:
|
48
|
+
enabled: true
|
49
|
+
exclude: []
|
50
|
+
NestedIterators:
|
51
|
+
enabled: true
|
52
|
+
exclude: []
|
53
|
+
max_allowed_nesting: 1
|
54
|
+
ignore_iterators:
|
55
|
+
- tap
|
56
|
+
NilCheck:
|
57
|
+
enabled: true
|
58
|
+
exclude: []
|
59
|
+
PrimaDonnaMethod:
|
60
|
+
enabled: true
|
61
|
+
exclude: []
|
62
|
+
RepeatedConditional:
|
63
|
+
enabled: true
|
64
|
+
exclude: []
|
65
|
+
max_ifs: 2
|
66
|
+
SubclassedFromCoreClass:
|
67
|
+
enabled: true
|
68
|
+
exclude: []
|
69
|
+
Syntax:
|
70
|
+
enabled: true
|
71
|
+
exclude: []
|
72
|
+
TooManyConstants:
|
73
|
+
enabled: true
|
74
|
+
exclude: []
|
75
|
+
max_constants: 5
|
76
|
+
TooManyInstanceVariables:
|
77
|
+
enabled: true
|
78
|
+
exclude: []
|
79
|
+
max_instance_variables: 4
|
80
|
+
TooManyMethods:
|
81
|
+
enabled: true
|
82
|
+
exclude: []
|
83
|
+
max_methods: 15
|
84
|
+
TooManyStatements:
|
85
|
+
enabled: true
|
86
|
+
exclude:
|
87
|
+
- initialize
|
88
|
+
max_statements: 5
|
89
|
+
UncommunicativeMethodName:
|
90
|
+
enabled: true
|
91
|
+
exclude: []
|
92
|
+
reject:
|
93
|
+
- !ruby/regexp /^[a-z]$/
|
94
|
+
- !ruby/regexp /[0-9]$/
|
95
|
+
- !ruby/regexp /[A-Z]/
|
96
|
+
accept: []
|
97
|
+
UncommunicativeModuleName:
|
98
|
+
enabled: true
|
99
|
+
exclude: []
|
100
|
+
reject:
|
101
|
+
- !ruby/regexp /^.$/
|
102
|
+
- !ruby/regexp /[0-9]$/
|
103
|
+
accept: []
|
104
|
+
UncommunicativeParameterName:
|
105
|
+
enabled: true
|
106
|
+
exclude: []
|
107
|
+
reject:
|
108
|
+
- !ruby/regexp /^.$/
|
109
|
+
- !ruby/regexp /[0-9]$/
|
110
|
+
- !ruby/regexp /[A-Z]/
|
111
|
+
- !ruby/regexp /^_/
|
112
|
+
accept: []
|
113
|
+
UncommunicativeVariableName:
|
114
|
+
enabled: true
|
115
|
+
exclude: []
|
116
|
+
reject:
|
117
|
+
- !ruby/regexp /^.$/
|
118
|
+
- !ruby/regexp /[0-9]$/
|
119
|
+
- !ruby/regexp /[A-Z]/
|
120
|
+
accept:
|
121
|
+
- !ruby/regexp /^_$/
|
122
|
+
UnusedParameters:
|
123
|
+
enabled: true
|
124
|
+
exclude: []
|
125
|
+
UnusedPrivateMethod:
|
126
|
+
enabled: false
|
127
|
+
exclude: []
|
128
|
+
UtilityFunction:
|
129
|
+
enabled: true
|
130
|
+
exclude: []
|
131
|
+
public_methods_only: false
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'etc'
|
4
|
+
require 'yaml'
|
5
|
+
require 'json'
|
6
|
+
require 'ostruct'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'rake/file_list'
|
9
|
+
require 'procto'
|
10
|
+
require 'adamantium'
|
11
|
+
require 'concord'
|
12
|
+
require 'rails'
|
13
|
+
|
14
|
+
require 'mutator_rails/guide'
|
15
|
+
require 'mutator_rails/railtie'
|
16
|
+
require 'mutator_rails/config'
|
17
|
+
require 'mutator_rails/version'
|
18
|
+
require 'mutator_rails/list_maker'
|
19
|
+
|
20
|
+
require 'mutator_rails/analyze'
|
21
|
+
require 'mutator_rails/cleanup'
|
22
|
+
require 'mutator_rails/statistics'
|
23
|
+
require 'mutator_rails/full_mutate'
|
24
|
+
|
25
|
+
# Mutator Rails' core functionality
|
26
|
+
module MutatorRails
|
27
|
+
MUTANT_VERSION = `bundle exec mutant --version`.strip.split('-').last.strip
|
28
|
+
|
29
|
+
COMMAND = 'RAILS_ENV=test bundle exec mutant '
|
30
|
+
BASIC_PARMS = ['-r./config/environment.rb', '--use rspec'].freeze
|
31
|
+
|
32
|
+
APP_BASE = 'app/'
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mutator_rails/mutation_log'
|
4
|
+
|
5
|
+
module MutatorRails
|
6
|
+
class Analyze
|
7
|
+
include Procto.call
|
8
|
+
include Adamantium::Flat
|
9
|
+
|
10
|
+
def call
|
11
|
+
list = ListMaker.new.make_list
|
12
|
+
return if list.blank?
|
13
|
+
|
14
|
+
content = list.map(&:to_s).join("\n")
|
15
|
+
|
16
|
+
puts " ... storing #{csv}"
|
17
|
+
File.write(csv,
|
18
|
+
MutationLog::HEADER + content)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def csv
|
24
|
+
MutatorRails::Config.configuration.analysis_csv
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mutator_rails/single_mutate'
|
4
|
+
|
5
|
+
module MutatorRails
|
6
|
+
class Cleanup
|
7
|
+
include Procto.call
|
8
|
+
include Adamantium::Flat
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
excluded_files
|
12
|
+
@guide = Guide.new
|
13
|
+
@guide_keys = guide.guides.keys
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
Dir
|
18
|
+
.glob(APP_BASE + '**/*.rb')
|
19
|
+
.each do |file|
|
20
|
+
next if exclude?(file)
|
21
|
+
|
22
|
+
check(file)
|
23
|
+
end
|
24
|
+
|
25
|
+
guide_keys.each do |file|
|
26
|
+
File.delete(file) if File.exist?(file)
|
27
|
+
puts "removing #{file}"
|
28
|
+
guide.remove(file)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def check(file)
|
35
|
+
log = file.sub(APP_BASE, Config.configuration.logroot).sub('.rb','.log')
|
36
|
+
|
37
|
+
if guide_keys.include?(log)
|
38
|
+
guide_keys.delete(log)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def excluded_files
|
43
|
+
@exclusions ||= load_exclusions
|
44
|
+
end
|
45
|
+
|
46
|
+
def load_exclusions
|
47
|
+
MutatorRails::Config
|
48
|
+
.configuration
|
49
|
+
.exclusions
|
50
|
+
.compact
|
51
|
+
.flat_map { |exclusion| Dir.glob(exclusion) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def exclude?(file)
|
55
|
+
excluded_files.include?(file)
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_reader :guide_keys, :guide
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mutator_rails/mutation_log'
|
4
|
+
|
5
|
+
module MutatorRails
|
6
|
+
class Config
|
7
|
+
|
8
|
+
PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
|
9
|
+
CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'mutator_rails.yml').freeze
|
10
|
+
USER_CONFIG = Pathname(Dir.pwd).join('mutator_rails.yml').freeze
|
11
|
+
|
12
|
+
private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def configuration
|
16
|
+
@configuration ||= load_configuration
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_configuration
|
20
|
+
default_config = YAML.load_file(CONFIG_DEFAULT)
|
21
|
+
user_config = if USER_CONFIG && File.exist?(USER_CONFIG.to_s)
|
22
|
+
YAML.load_file(USER_CONFIG)
|
23
|
+
else
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
27
|
+
consolidated = default_config.merge(user_config)
|
28
|
+
|
29
|
+
JSON.parse(consolidated.to_json, object_class: OpenStruct).freeze
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mutator_rails/single_mutate'
|
4
|
+
|
5
|
+
module MutatorRails
|
6
|
+
class FullMutate
|
7
|
+
include Procto.call
|
8
|
+
include Adamantium::Flat
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
excluded_files
|
12
|
+
@guide = Guide.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
Dir.glob(APP_BASE + '**/*.rb').sort_by { |x| File.size(x) }.each do |file|
|
17
|
+
next if exclude?(file)
|
18
|
+
|
19
|
+
SingleMutate.new(guide, file).call
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def excluded_files
|
26
|
+
@exclusions ||= load_exclusions
|
27
|
+
end
|
28
|
+
|
29
|
+
def load_exclusions
|
30
|
+
MutatorRails::Config.configuration.exclusions.compact.flat_map { |exclusion| Dir.glob(exclusion) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def exclude?(file)
|
34
|
+
excluded_files.include?(file)
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :guide
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MutatorRails
|
4
|
+
class Guide
|
5
|
+
|
6
|
+
SEP = ' | '.freeze
|
7
|
+
|
8
|
+
attr_reader :guides
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
preset_file
|
12
|
+
end
|
13
|
+
|
14
|
+
def current?(log, code_md5, spec_md5)
|
15
|
+
# p guides[log]
|
16
|
+
File.exist?(log) &&
|
17
|
+
File.size(log).positive? &&
|
18
|
+
guides[log].present? &&
|
19
|
+
guides[log].eql?([code_md5, spec_md5, MUTANT_VERSION])
|
20
|
+
end
|
21
|
+
|
22
|
+
def update(log, code_md5, spec_md5)
|
23
|
+
guides[log] = [code_md5, spec_md5, MUTANT_VERSION]
|
24
|
+
recreate
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove(log)
|
28
|
+
guides.delete(log)
|
29
|
+
recreate
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def preset_file
|
35
|
+
create_guide_file unless File.exist?(guide_file_name)
|
36
|
+
|
37
|
+
load
|
38
|
+
end
|
39
|
+
|
40
|
+
def load
|
41
|
+
@guides = {}
|
42
|
+
File.readlines(guide_file_name).each do |line|
|
43
|
+
log, code_md5, spec_md5, mutant_version = line.strip.split(SEP)
|
44
|
+
guides[log] = [code_md5, spec_md5, mutant_version]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def recreate
|
49
|
+
f = File.open(guide_file_name, 'w')
|
50
|
+
guides.sort.each do |k, v|
|
51
|
+
f.puts ("#{k}#{SEP}#{v.join(SEP)}") if k.present?
|
52
|
+
end
|
53
|
+
f.close
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_guide_file
|
57
|
+
puts ".. creating guide file #{guide_file_name}"
|
58
|
+
File.open(guide_file_name, "w+")
|
59
|
+
end
|
60
|
+
|
61
|
+
def guide_file_name
|
62
|
+
Pathname(Dir.pwd).join(MutatorRails::Config.configuration.guide_file).tap do |p|
|
63
|
+
FileUtils.mkdir_p(p.dirname)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mutator_rails/single_mutate'
|
4
|
+
|
5
|
+
module MutatorRails
|
6
|
+
class ListMaker
|
7
|
+
include Adamantium::Flat
|
8
|
+
|
9
|
+
def make_list
|
10
|
+
list = []
|
11
|
+
|
12
|
+
Dir.glob(Config.configuration.logroot + '**/*.log').each do |target_log|
|
13
|
+
next unless File.exist?(target_log)
|
14
|
+
|
15
|
+
begin
|
16
|
+
list << MutationLog.new(target_log)
|
17
|
+
rescue Exception => se
|
18
|
+
# skip it
|
19
|
+
puts "Error: #{se}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
list.sort
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|