mutator_rails 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +37 -0
  3. data/.codeclimate.yml +20 -0
  4. data/.gitignore +12 -0
  5. data/.rspec +2 -0
  6. data/.ruby-version +1 -0
  7. data/CODE_OF_CONDUCT.md +46 -0
  8. data/Gemfile +7 -0
  9. data/Gemfile.lock +232 -0
  10. data/LICENSE.md +21 -0
  11. data/README.md +31 -0
  12. data/Rakefile +8 -0
  13. data/app/models/test.rb +7 -0
  14. data/bin/console +15 -0
  15. data/bin/setup +8 -0
  16. data/config/mutator_rails.yml +8 -0
  17. data/defaults.reek +131 -0
  18. data/lib/mutator_rails.rb +33 -0
  19. data/lib/mutator_rails/analyze.rb +27 -0
  20. data/lib/mutator_rails/cleanup.rb +60 -0
  21. data/lib/mutator_rails/config.rb +34 -0
  22. data/lib/mutator_rails/full_mutate.rb +39 -0
  23. data/lib/mutator_rails/guide.rb +69 -0
  24. data/lib/mutator_rails/list_maker.rb +25 -0
  25. data/lib/mutator_rails/mutation_log.rb +112 -0
  26. data/lib/mutator_rails/railtie.rb +18 -0
  27. data/lib/mutator_rails/single_mutate.rb +134 -0
  28. data/lib/mutator_rails/statistics.rb +192 -0
  29. data/lib/mutator_rails/version.rb +5 -0
  30. data/lib/tasks/mutator/analyze.rake +13 -0
  31. data/lib/tasks/mutator/cleanup.rake +13 -0
  32. data/lib/tasks/mutator/mutate_files.rake +12 -0
  33. data/lib/tasks/mutator/mutator.rake +17 -0
  34. data/lib/tasks/mutator/statistics.rake +13 -0
  35. data/log/mutant/analysis.tsv +3 -0
  36. data/log/mutant/guide.txt +1 -0
  37. data/log/mutant/models/test.log +92 -0
  38. data/log/mutant/models/test2.log +92 -0
  39. data/log/mutant/statistics.txt +19 -0
  40. data/mutator_rails.gemspec +37 -0
  41. data/spec/models/test_spec.rb +15 -0
  42. data/spec/mutator_rails/analyze_spec.rb +26 -0
  43. data/spec/mutator_rails/cleanup_spec.rb +13 -0
  44. data/spec/mutator_rails/full_mutate_spec.rb +13 -0
  45. data/spec/mutator_rails/guide_spec.rb +33 -0
  46. data/spec/mutator_rails/list_maker_spec.rb +17 -0
  47. data/spec/mutator_rails/mutation_log_spec.rb +36 -0
  48. data/spec/mutator_rails/single_mutate_spec.rb +111 -0
  49. data/spec/mutator_rails/statistics_spec.rb +48 -0
  50. data/spec/mutator_rails_spec.rb +9 -0
  51. data/spec/spec_helper.rb +49 -0
  52. 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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,8 @@
1
+ ---
2
+ logroot: 'log/mutant/'
3
+ guide_file: 'log/mutant/guide.txt'
4
+ analysis_csv: 'log/mutant/analysis.tsv'
5
+ statistics: 'log/mutant/statistics.txt'
6
+
7
+ exclusions:
8
+ -
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