devtools 0.1.0 → 0.1.5

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/Gemfile +0 -2
  4. data/README.md +3 -3
  5. data/circle.yml +1 -1
  6. data/config/devtools.yml +2 -0
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/mutant.yml +2 -0
  10. data/config/reek.yml +7 -12
  11. data/config/rubocop.yml +8 -0
  12. data/devtools.gemspec +21 -19
  13. data/lib/devtools/config.rb +76 -55
  14. data/lib/devtools/flay.rb +94 -0
  15. data/lib/devtools/project/initializer/rake.rb +9 -7
  16. data/lib/devtools/project/initializer/rspec.rb +28 -55
  17. data/lib/devtools/project/initializer.rb +2 -12
  18. data/lib/devtools/project.rb +28 -105
  19. data/lib/devtools/rake/flay.rb +126 -0
  20. data/lib/devtools/spec_helper.rb +1 -3
  21. data/lib/devtools.rb +34 -83
  22. data/shared/spec/shared/abstract_type_behavior.rb +0 -2
  23. data/shared/spec/shared/command_method_behavior.rb +0 -2
  24. data/shared/spec/shared/each_method_behaviour.rb +0 -2
  25. data/shared/spec/shared/hash_method_behavior.rb +0 -2
  26. data/shared/spec/shared/idempotent_method_behavior.rb +0 -2
  27. data/shared/spec/support/ice_nine_config.rb +0 -2
  28. data/spec/integration/devtools/rake/flay/verify_spec.rb +164 -0
  29. data/spec/spec_helper.rb +2 -8
  30. data/spec/unit/devtools/config/yardstick_spec.rb +17 -0
  31. data/spec/unit/devtools/config_spec.rb +78 -0
  32. data/spec/unit/devtools/flay/file_list/call_spec.rb +19 -0
  33. data/spec/unit/devtools/flay/scale/flay_report_spec.rb +17 -0
  34. data/spec/unit/devtools/flay/scale/measure_spec.rb +43 -0
  35. data/spec/unit/devtools/project/initializer/rake_spec.rb +21 -0
  36. data/spec/unit/devtools/project/initializer/rspec_spec.rb +52 -0
  37. data/spec/unit/devtools/project_spec.rb +34 -0
  38. data/spec/unit/devtools_spec.rb +14 -0
  39. data/tasks/metrics/ci.rake +2 -3
  40. data/tasks/metrics/coverage.rake +0 -2
  41. data/tasks/metrics/flay.rake +6 -33
  42. data/tasks/metrics/flog.rake +0 -2
  43. data/tasks/metrics/mutant.rake +31 -39
  44. data/tasks/metrics/reek.rake +5 -4
  45. data/tasks/metrics/rubocop.rake +0 -2
  46. data/tasks/metrics/yardstick.rake +0 -14
  47. data/tasks/spec.rake +0 -2
  48. data/tasks/yard.rake +0 -2
  49. metadata +99 -35
  50. data/.travis.yml +0 -15
  51. data/TODO +0 -0
  52. data/bin/devtools +0 -18
  53. data/default/config/devtools.yml +0 -2
  54. data/default/config/flay.yml +0 -3
  55. data/default/config/flog.yml +0 -2
  56. data/default/config/mutant.yml +0 -3
  57. data/default/config/reek.yml +0 -103
  58. data/default/config/rubocop.yml +0 -91
  59. data/default/config/yardstick.yml +0 -2
  60. data/lib/devtools/platform.rb +0 -118
  61. data/lib/devtools/site/initializer.rb +0 -57
  62. data/lib/devtools/site.rb +0 -41
@@ -0,0 +1,17 @@
1
+ RSpec.describe Devtools::Config::Yardstick do
2
+ let(:object) { described_class.new(Devtools.root.join('config')) }
3
+
4
+ describe '#options' do
5
+ subject { object.options }
6
+
7
+ specify do
8
+ should eql(
9
+ 'threshold' => 100,
10
+ 'rules' => nil,
11
+ 'verbose' => nil,
12
+ 'path' => nil,
13
+ 'require_exact_threshold' => nil
14
+ )
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,78 @@
1
+ RSpec.describe Devtools::Config do
2
+
3
+ describe '.attribute' do
4
+ let(:raw) do
5
+ {
6
+ 'a' => 'bar',
7
+ 'c' => []
8
+ }
9
+
10
+ end
11
+
12
+ let(:config_path) { instance_double(Pathname) }
13
+
14
+ let(:class_under_test) do
15
+ expect(config_path).to receive(:file?)
16
+ .and_return(file?)
17
+ expect(config_path).to receive(:frozen?)
18
+ .and_return(true)
19
+ expect(config_path).to receive(:join)
20
+ .with('bar.yml')
21
+ .and_return(config_path)
22
+
23
+ Class.new(described_class) do
24
+ attribute :a, [String]
25
+ attribute :b, [Array], default: []
26
+ attribute :c, [TrueClass, FalseClass]
27
+
28
+ const_set(:FILE, 'bar.yml')
29
+ end
30
+ end
31
+
32
+ subject do
33
+ class_under_test.new(config_path)
34
+ end
35
+
36
+ context 'on present config' do
37
+ let(:class_under_test) do
38
+ # Setup message expectation in a lasy way, not in a before
39
+ # block to make sure the around hook setting timeouts from the
40
+ # code under test is not affected.
41
+ expect(YAML).to receive(:load_file)
42
+ .with(config_path)
43
+ .and_return(raw)
44
+
45
+ expect(IceNine).to receive(:deep_freeze)
46
+ .with(raw)
47
+ .and_return(raw)
48
+
49
+ super()
50
+ end
51
+
52
+ let(:file?) { true }
53
+
54
+ it 'allows to receive existing keys' do
55
+ expect(subject.a).to eql('bar')
56
+ end
57
+
58
+ it 'allows to receive absent keys with defaults' do
59
+ expect(subject.b).to eql([])
60
+ end
61
+
62
+ it 'executes checks when configured' do
63
+ expect { subject.c }.to raise_error(
64
+ Devtools::Config::TypeError,
65
+ 'c: Got instance of Array expected TrueClass,FalseClass'
66
+ )
67
+ end
68
+ end
69
+
70
+ context 'on absent config' do
71
+ let(:file?) { false }
72
+
73
+ it 'defaults to absent keys' do
74
+ expect(subject.b).to eql([])
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,19 @@
1
+ describe Devtools::Flay::FileList, '.call' do
2
+ subject(:output) { described_class.call([tmpdir.to_s], [exclude]) }
3
+
4
+ let(:tmpdir) { Dir.mktmpdir }
5
+ let(:one) { Pathname(tmpdir).join('1.rb') }
6
+ let(:two) { Pathname(tmpdir).join('2.erb') }
7
+ let(:three) { Pathname(tmpdir).join('3.rb') }
8
+ let(:exclude) { Pathname(tmpdir).join('3*').to_s }
9
+
10
+ around(:each) do |example|
11
+ [one, two, three].map(&FileUtils.method(:touch))
12
+
13
+ example.run
14
+
15
+ FileUtils.rm_rf(tmpdir)
16
+ end
17
+
18
+ it { should eql(Set.new([one, two])) }
19
+ end
@@ -0,0 +1,17 @@
1
+ describe Devtools::Flay::Scale, '#flay_report' do
2
+ subject(:instance) { described_class.new(minimum_mass: 0, files: []) }
3
+
4
+ let(:flay) do
5
+ instance_double(::Flay, process: nil, analyze: nil, masses: {})
6
+ end
7
+
8
+ before do
9
+ allow(::Flay).to receive(:new).with(mass: 0).and_return(flay)
10
+ end
11
+
12
+ specify do
13
+ allow(flay).to receive(:report)
14
+ instance.flay_report
15
+ expect(flay).to have_received(:report)
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ describe Devtools::Flay::Scale, '#measure' do
2
+ subject(:measure) { instance.measure }
3
+
4
+ let(:minimum_mass) { 0 }
5
+ let(:files) { [instance_double(File)] }
6
+ let(:flay_masses) { { 0 => 5, 1 => 10 } }
7
+
8
+ let(:instance) do
9
+ described_class.new(minimum_mass: minimum_mass, files: files)
10
+ end
11
+
12
+ let(:flay_hashes) do
13
+ {
14
+ 0 => instance_double(Array, size: 3),
15
+ 1 => instance_double(Array, size: 11)
16
+ }
17
+ end
18
+
19
+ let(:flay) do
20
+ instance_double(
21
+ ::Flay,
22
+ analyze: nil,
23
+ masses: flay_masses,
24
+ hashes: flay_hashes
25
+ )
26
+ end
27
+
28
+ before do
29
+ allow(::Flay).to receive(:new).with(mass: minimum_mass).and_return(flay)
30
+ allow(flay).to receive(:process).with(*files)
31
+ end
32
+
33
+ it { should eql([Rational(5, 3), Rational(10, 11)]) }
34
+
35
+ context 'when minimum mass is not 0' do
36
+ let(:minimum_mass) { 1 }
37
+
38
+ specify do
39
+ measure
40
+ expect(::Flay).to have_received(:new).with(mass: 1)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ describe Devtools::Project::Initializer::Rake do
2
+ describe '.call' do
3
+ subject do
4
+ described_class.call
5
+ end
6
+
7
+ it 'performs expected rake initialization' do
8
+ path_a = instance_double(Pathname)
9
+ path_b = instance_double(Pathname)
10
+
11
+ expect(FileList).to receive(:glob)
12
+ .with(Devtools.root.join('tasks/**/*.rake').to_s)
13
+ .and_return([path_a, path_b])
14
+
15
+ expect(Rake.application).to receive(:add_import).with(path_a)
16
+ expect(Rake.application).to receive(:add_import).with(path_b)
17
+
18
+ expect(subject).to be(described_class)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,52 @@
1
+ describe Devtools::Project::Initializer::Rspec do
2
+ let(:spec_root) { Devtools.root.join('spec') }
3
+ let(:unit_test_timeout) { instance_double(Float) }
4
+
5
+ let(:project) do
6
+ instance_double(
7
+ Devtools::Project,
8
+ spec_root: spec_root,
9
+ devtools: instance_double(
10
+ Devtools::Config::Devtools,
11
+ unit_test_timeout: unit_test_timeout
12
+ )
13
+ )
14
+ end
15
+
16
+ describe '.call' do
17
+ subject do
18
+ described_class.call(project)
19
+ end
20
+
21
+ it 'performs expected rspec initialization' do
22
+ called = false
23
+ example = -> { called = true }
24
+
25
+ expect(Dir).to receive(:glob)
26
+ .with(Devtools.root.join('shared/spec/{shared,support}/**/*.rb'))
27
+ .and_return(%w[shared-a shared-b])
28
+
29
+ expect(Kernel).to receive(:require).with('shared-a')
30
+ expect(Kernel).to receive(:require).with('shared-b')
31
+
32
+ expect(Dir).to receive(:glob)
33
+ .with(Devtools.root.join('spec/{shared,support}/**/*.rb'))
34
+ .and_return(%w[support-a support-b])
35
+
36
+ expect(Kernel).to receive(:require).with('support-a')
37
+ expect(Kernel).to receive(:require).with('support-b')
38
+
39
+ expect(Timeout).to receive(:timeout).with(unit_test_timeout) do |&block|
40
+ block.call
41
+ end
42
+
43
+ expect(RSpec.configuration).to receive(:around)
44
+ .with(file_path: %r{\bspec/unit/})
45
+ .and_yield(example)
46
+
47
+ expect(subject).to be(described_class)
48
+
49
+ expect(called).to be(true)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,34 @@
1
+ RSpec.describe Devtools::Project do
2
+ let(:object) { described_class.new(Devtools.root) }
3
+
4
+ describe '#init_rspec' do
5
+ subject { object.init_rspec }
6
+
7
+ it 'calls the rspec initializer' do
8
+ expect(Devtools::Project::Initializer::Rspec).to receive(:call).with(Devtools.project)
9
+ expect(subject).to be(object)
10
+ end
11
+ end
12
+
13
+ {
14
+ devtools: Devtools::Config::Devtools,
15
+ flay: Devtools::Config::Flay,
16
+ flog: Devtools::Config::Flog,
17
+ reek: Devtools::Config::Reek,
18
+ mutant: Devtools::Config::Mutant,
19
+ rubocop: Devtools::Config::Rubocop,
20
+ yardstick: Devtools::Config::Yardstick
21
+ }.each do |name, klass|
22
+ describe "##{name}" do
23
+ subject { object.send(name) }
24
+
25
+ specify { should eql(klass.new(Devtools.root.join('config'))) }
26
+ end
27
+ end
28
+
29
+ describe '#spec_root' do
30
+ subject { object.spec_root }
31
+
32
+ specify { should eql(Devtools.root.join('spec')) }
33
+ end
34
+ end
@@ -0,0 +1,14 @@
1
+ describe Devtools do
2
+ describe '.project' do
3
+ specify do
4
+ expect(Devtools.project).to equal(Devtools::PROJECT)
5
+ end
6
+ end
7
+
8
+ describe '.init_rake_tasks' do
9
+ specify do
10
+ expect(Devtools::Project::Initializer::Rake).to receive(:call)
11
+ expect(Devtools.init_rake_tasks).to be(Devtools)
12
+ end
13
+ end
14
+ end
@@ -1,7 +1,5 @@
1
- # encoding: utf-8
2
-
3
1
  desc 'Run all specs, metrics and mutant'
4
- task ci: %w[spec ci:metrics metrics:mutant]
2
+ task ci: %w[ci:metrics metrics:mutant]
5
3
 
6
4
  namespace :ci do
7
5
  tasks = %w[
@@ -11,6 +9,7 @@ namespace :ci do
11
9
  metrics:flog
12
10
  metrics:flay
13
11
  metrics:reek
12
+ spec:integration
14
13
  ]
15
14
 
16
15
  desc 'Run metrics (except mutant)'
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  namespace :metrics do
4
2
  desc 'Measure code coverage'
5
3
  task :coverage do
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  namespace :metrics do
4
2
  require 'flay'
5
3
 
@@ -12,37 +10,12 @@ namespace :metrics do
12
10
  task :flay do
13
11
  threshold = config.threshold
14
12
  total_score = config.total_score
15
- files = Flay.expand_dirs_to_files(config.lib_dirs).sort
16
-
17
- # Run flay first to ensure the max mass matches the threshold
18
- flay = Flay.new(fuzzy: false, verbose: false, mass: 0)
19
- flay.process(*files)
20
- flay.analyze
21
-
22
- masses = flay.masses.map do |hash, mass|
23
- Rational(mass, flay.hashes[hash].size)
24
- end
25
-
26
- max = (masses.max || 0).to_i
27
- unless max >= threshold
28
- Devtools.notify_metric_violation "Adjust flay threshold down to #{max}"
29
- end
30
-
31
- total = masses.inject(:+).to_i
32
- unless total == total_score
33
- Devtools.notify_metric_violation "Flay total is now #{total}, but expected #{total_score}"
34
- end
35
-
36
- # Run flay a second time with the threshold set
37
- flay = Flay.new(fuzzy: false, verbose: false, mass: threshold.succ)
38
- flay.process(*files)
39
- flay.analyze
40
-
41
- mass_size = flay.masses.size
42
13
 
43
- if mass_size.nonzero?
44
- flay.report
45
- Devtools.notify_metric_violation "#{mass_size} chunks have a duplicate mass > #{threshold}"
46
- end
14
+ Devtools::Rake::Flay.call(
15
+ threshold: threshold,
16
+ total_score: total_score,
17
+ lib_dirs: config.lib_dirs,
18
+ excludes: config.excludes
19
+ )
47
20
  end
48
21
  end
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  namespace :metrics do
4
2
  require 'flog'
5
3
  require 'flog_cli'
@@ -1,50 +1,42 @@
1
- # encoding: utf-8
2
-
3
1
  namespace :metrics do
4
2
  config = Devtools.project.mutant
5
3
 
6
- if !ENV['DEVTOOLS_SELF']
7
- desc 'Measure mutation coverage'
8
- task mutant: :coverage do
9
- require 'mutant'
4
+ desc 'Measure mutation coverage'
5
+ task mutant: :coverage do
6
+ require 'mutant'
10
7
 
11
- namespace =
12
- if config.zombify
13
- Mutant.zombify
14
- Zombie::Mutant
15
- else
16
- Mutant
17
- end
8
+ namespace =
9
+ if config.zombify
10
+ Mutant.zombify
11
+ Zombie::Mutant
12
+ else
13
+ Mutant
14
+ end
18
15
 
19
- namespaces = Array(config.namespace).map { |n| "#{n}*" }
16
+ namespaces = Array(config.namespace).map { |n| "#{n}*" }
20
17
 
21
- ignore_subjects = config.ignore_subjects.flat_map do |matcher|
22
- %W[--ignore #{matcher}]
23
- end
18
+ ignore_subjects = config.ignore_subjects.flat_map do |matcher|
19
+ %W[--ignore #{matcher}]
20
+ end
24
21
 
25
- since =
26
- if config.since
27
- %W[--since #{config.since}]
28
- else
29
- []
30
- end
31
-
32
- arguments = %W[
33
- --include lib
34
- --require #{config.name}
35
- --expect-coverage #{config.expect_coverage}
36
- --use #{config.strategy}
37
- ].concat(ignore_subjects).concat(namespaces).concat(since)
38
-
39
- status = namespace::CLI.run(arguments)
40
- if status.nonzero?
41
- Devtools.notify_metric_violation 'Mutant task is not successful'
22
+ jobs = ENV.key?('CIRCLECI') ? %w[--jobs 4] : []
23
+
24
+ since =
25
+ if config.since
26
+ %W[--since #{config.since}]
27
+ else
28
+ []
42
29
  end
43
- end
44
- else
45
- desc 'Measure mutation coverage'
46
- task mutant: :coverage do
47
- $stderr.puts 'Mutant is disabled'
30
+
31
+ arguments = %W[
32
+ --include lib
33
+ --require #{config.name}
34
+ --expect-coverage #{config.expect_coverage}
35
+ --use #{config.strategy}
36
+ ].concat(ignore_subjects).concat(namespaces).concat(since).concat(jobs)
37
+
38
+ unless namespace::CLI.run(arguments)
39
+ Devtools.notify_metric_violation('Mutant task is not successful')
48
40
  end
49
41
  end
50
42
  end
@@ -1,7 +1,8 @@
1
- # encoding: utf-8
2
-
3
1
  namespace :metrics do
4
- task :reek do
5
- $stderr.puts 'Reek is disabled due defunct AST processing'
2
+ require 'reek/rake/task'
3
+
4
+ Reek::Rake::Task.new do |reek|
5
+ reek.source_files = '{app,lib}/**/*.rb'
6
+ reek.config_file = 'config/reek.yml'
6
7
  end
7
8
  end
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  namespace :metrics do
4
2
  desc 'Check with code style guide'
5
3
  task :rubocop do
@@ -1,22 +1,8 @@
1
- # encoding: utf-8
2
-
3
1
  namespace :metrics do
4
2
  namespace :yardstick do
5
3
  require 'yardstick/rake/measurement'
6
4
  require 'yardstick/rake/verify'
7
5
 
8
- # Enable the legacy parser for JRuby until ripper is fully supported
9
- if Devtools.jruby?
10
- # Remove when https://github.com/lsegal/yard/issues/681 is resolved
11
- # This code first requires ripper, then removes the constant so
12
- # that it does not trigger a bug in YARD where if it checks if Ripper
13
- # is available and assumes other constants are defined, when JRuby's
14
- # implementation does not yet.
15
- require 'ripper'
16
- Object.send(:remove_const, :Ripper)
17
- YARD::Parser::SourceParser.parser_type = :ruby18
18
- end
19
-
20
6
  options = Devtools.project.yardstick.options
21
7
 
22
8
  Yardstick::Rake::Measurement.new(:measure, options)
data/tasks/spec.rake CHANGED
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  begin
4
2
  require 'rspec/core/rake_task'
5
3
 
data/tasks/yard.rake CHANGED
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  begin
4
2
  require 'yard'
5
3