mutator_rails 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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