central-devtools 0.8.3
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.
- checksums.yaml +7 -0
- data/.gitignore +37 -0
- data/.rspec +6 -0
- data/.rubocop.yml +5 -0
- data/.ruby-gemset +1 -0
- data/Gemfile +3 -0
- data/LICENSE +191 -0
- data/README.md +36 -0
- data/Rakefile +8 -0
- data/central-devtools.gemspec +44 -0
- data/config/container.yml +7 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +4 -0
- data/config/reek.yml +102 -0
- data/config/rubocop.yml +117 -0
- data/config/yardstick.yml +2 -0
- data/lib/central/devtools/config.rb +199 -0
- data/lib/central/devtools/flay.rb +91 -0
- data/lib/central/devtools/project/initializer/rake.rb +23 -0
- data/lib/central/devtools/project/initializer/rspec.rb +70 -0
- data/lib/central/devtools/project/initializer.rb +13 -0
- data/lib/central/devtools/project.rb +80 -0
- data/lib/central/devtools/rake/flay.rb +121 -0
- data/lib/central/devtools/spec_helper.rb +5 -0
- data/lib/central/devtools/version.rb +7 -0
- data/lib/central/devtools.rb +90 -0
- data/shared/spec/shared/abstract_type_behavior.rb +16 -0
- data/shared/spec/shared/command_method_behavior.rb +5 -0
- data/shared/spec/shared/each_method_behaviour.rb +13 -0
- data/shared/spec/shared/hash_method_behavior.rb +7 -0
- data/shared/spec/shared/idempotent_method_behavior.rb +10 -0
- data/shared/spec/support/ice_nine_config.rb +9 -0
- data/shared/tasks/docker.rake +80 -0
- data/shared/tasks/metrics/ci.rake +19 -0
- data/shared/tasks/metrics/coverage.rake +14 -0
- data/shared/tasks/metrics/flay.rake +21 -0
- data/shared/tasks/metrics/flog.rake +38 -0
- data/shared/tasks/metrics/mutant.rake +44 -0
- data/shared/tasks/metrics/reek.rake +10 -0
- data/shared/tasks/metrics/rubocop.rake +15 -0
- data/shared/tasks/metrics/yardstick.rake +15 -0
- data/shared/tasks/spec.rake +36 -0
- data/shared/tasks/yard.rake +11 -0
- data/spec/integration/central/devtools/rake/flay/verify_spec.rb +166 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/unit/central/devtools/config/yardstick_spec.rb +19 -0
- data/spec/unit/central/devtools/config_spec.rb +75 -0
- data/spec/unit/central/devtools/flay/file_list/call_spec.rb +21 -0
- data/spec/unit/central/devtools/flay/scale/flay_report_spec.rb +19 -0
- data/spec/unit/central/devtools/flay/scale/measure_spec.rb +45 -0
- data/spec/unit/central/devtools/project/initializer/rake_spec.rb +23 -0
- data/spec/unit/central/devtools/project/initializer/rspec_spec.rb +55 -0
- data/spec/unit/central/devtools/project_spec.rb +37 -0
- data/spec/unit/central/devtools_spec.rb +16 -0
- metadata +393 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
shared_examples_for 'an abstract type' do
|
2
|
+
context 'called on a subclass' do
|
3
|
+
let(:object) { Class.new(described_class) }
|
4
|
+
|
5
|
+
it { should be_instance_of(object) }
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'called on the class' do
|
9
|
+
let(:object) { described_class }
|
10
|
+
|
11
|
+
specify do
|
12
|
+
expect { subject }
|
13
|
+
.to raise_error(NotImplementedError, "#{object} is an abstract type")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
shared_examples_for 'an #each method' do
|
2
|
+
it_should_behave_like 'a command method'
|
3
|
+
|
4
|
+
context 'with no block' do
|
5
|
+
subject { object.each }
|
6
|
+
|
7
|
+
it { should be_instance_of(to_enum.class) }
|
8
|
+
|
9
|
+
it 'yields the expected values' do
|
10
|
+
expect(subject.to_a).to eql(object.to_a)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
shared_examples_for 'an idempotent method' do
|
2
|
+
it 'is idempotent' do
|
3
|
+
first = subject
|
4
|
+
fail 'RSpec not configured for threadsafety' unless RSpec.configuration.threadsafe?
|
5
|
+
mutex = __memoized.instance_variable_get(:@mutex)
|
6
|
+
memoized = __memoized.instance_variable_get(:@memoized)
|
7
|
+
mutex.synchronize { memoized.delete(:subject) }
|
8
|
+
should equal(first)
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'colorize'
|
4
|
+
require 'tty'
|
5
|
+
|
6
|
+
options = Central::Devtools.project.container.options
|
7
|
+
version = options['build_tag'].to_s
|
8
|
+
versions = if version.nil? || version == 'latest'
|
9
|
+
['latest']
|
10
|
+
else
|
11
|
+
['latest', version.match(/(\d+\.\d+)/)[1]].freeze
|
12
|
+
end
|
13
|
+
name = "#{options['prefix']}/#{options['component']}"
|
14
|
+
from = "#{options['from_image']}:#{options['from_version']}"
|
15
|
+
|
16
|
+
def msg(text)
|
17
|
+
ttable = TTY::Table.new
|
18
|
+
ttable << [text]
|
19
|
+
renderer = TTY::Table::Renderer::Unicode.new(ttable)
|
20
|
+
renderer.border.style = :red
|
21
|
+
puts renderer.render
|
22
|
+
end
|
23
|
+
|
24
|
+
def pull
|
25
|
+
" ↓ ".colorize(:red)
|
26
|
+
end
|
27
|
+
|
28
|
+
def push
|
29
|
+
" ↑ ".colorize(:light_red)
|
30
|
+
end
|
31
|
+
|
32
|
+
def tag
|
33
|
+
" ✓ ".colorize(:green)
|
34
|
+
end
|
35
|
+
|
36
|
+
def spin
|
37
|
+
" ↻ ".colorize(:cyan)
|
38
|
+
end
|
39
|
+
|
40
|
+
def sync
|
41
|
+
" ⎘ ".colorize(:light_green)
|
42
|
+
end
|
43
|
+
|
44
|
+
def build
|
45
|
+
" ◴ ".colorize(:light_magenta)
|
46
|
+
end
|
47
|
+
|
48
|
+
namespace :docker do
|
49
|
+
desc "Build #{name} container from #{from}"
|
50
|
+
task build: ['docker:build:task']
|
51
|
+
|
52
|
+
namespace :build do
|
53
|
+
task :task, :name, :version do |_t, _args|
|
54
|
+
msg "#{pull} docker pull #{from.colorize(:cyan)} "
|
55
|
+
sh "docker pull #{from}"
|
56
|
+
msg "#{build} docker build --no-cache --force-rm -t #{name.colorize(:cyan)}:#{version.colorize(:cyan)} "
|
57
|
+
sh "docker build --no-cache --force-rm -t #{name}:#{version} ."
|
58
|
+
|
59
|
+
versions.each do |v|
|
60
|
+
msg "#{tag} docker tag #{name.colorize(:yellow)}:#{version.colorize(:cyan)} #{name.colorize(:yellow)}:#{v.colorize(:cyan)} "
|
61
|
+
sh "docker tag #{name}:#{version} #{name}:#{v}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
desc "Push image #{name}"
|
67
|
+
task push: ['docker:push:task']
|
68
|
+
|
69
|
+
namespace :push do
|
70
|
+
task task: ['docker:build'] do
|
71
|
+
msg "#{push} docker push #{name.colorize(:cyan)}:#{version.colorize(:cyan)} "
|
72
|
+
sh "docker push #{name}:#{version}"
|
73
|
+
|
74
|
+
versions.each do |v|
|
75
|
+
msg "#{push} docker push #{name.colorize(:yellow)}:#{v.colorize(:cyan)} "
|
76
|
+
sh "docker push #{name}:#{v}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
desc 'Run all specs, metrics and mutant'
|
4
|
+
task ci: %w(ci:metrics metrics:mutant)
|
5
|
+
|
6
|
+
namespace :ci do
|
7
|
+
tasks = %w(
|
8
|
+
metrics:coverage
|
9
|
+
metrics:yardstick:verify
|
10
|
+
metrics:rubocop
|
11
|
+
metrics:flog
|
12
|
+
metrics:flay
|
13
|
+
metrics:reek
|
14
|
+
spec:integration
|
15
|
+
)
|
16
|
+
|
17
|
+
desc 'Run metrics (except mutant)'
|
18
|
+
task metrics: tasks
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
namespace :metrics do
|
4
|
+
desc 'Measure code coverage'
|
5
|
+
task :coverage do
|
6
|
+
begin
|
7
|
+
original = ENV['COVERAGE']
|
8
|
+
ENV['COVERAGE'] = 'true'
|
9
|
+
Rake::Task['spec:unit'].execute
|
10
|
+
ensure
|
11
|
+
ENV['COVERAGE'] = original
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
namespace :metrics do
|
4
|
+
require 'flay'
|
5
|
+
|
6
|
+
project = Central::Devtools.project
|
7
|
+
config = project.flay
|
8
|
+
|
9
|
+
desc 'Measure code duplication'
|
10
|
+
task :flay do
|
11
|
+
threshold = config.threshold
|
12
|
+
total_score = config.total_score
|
13
|
+
|
14
|
+
Central::Devtools::Rake::Flay.call(
|
15
|
+
threshold: threshold,
|
16
|
+
total_score: total_score,
|
17
|
+
lib_dirs: config.lib_dirs,
|
18
|
+
excludes: config.excludes
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
namespace :metrics do
|
4
|
+
require 'flog'
|
5
|
+
require 'flog_cli'
|
6
|
+
|
7
|
+
project = Central::Devtools.project
|
8
|
+
config = project.flog
|
9
|
+
|
10
|
+
desc 'Measure code complexity'
|
11
|
+
task :flog do
|
12
|
+
threshold = config.threshold.to_f.round(1)
|
13
|
+
flog = Flog.new
|
14
|
+
flog.flog(*FlogCLI.expand_dirs_to_files(config.lib_dirs))
|
15
|
+
|
16
|
+
totals = flog.totals.select { |name, _score| name[-5, 5] != '#none' }
|
17
|
+
.map { |name, score| [name, score.round(1)] }
|
18
|
+
.sort_by { |_name, score| score }
|
19
|
+
|
20
|
+
if totals.any?
|
21
|
+
max = totals.last[1]
|
22
|
+
unless max >= threshold
|
23
|
+
Central::Devtools.notify_metric_violation "Adjust flog score down to #{max}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
bad_methods = totals.select { |_name, score| score > threshold }
|
28
|
+
if bad_methods.any?
|
29
|
+
bad_methods.reverse_each do |name, score|
|
30
|
+
printf "%8.1f: %s\n", score, name
|
31
|
+
end
|
32
|
+
|
33
|
+
Central::Devtools.notify_metric_violation(
|
34
|
+
"#{bad_methods.size} methods have a flog complexity > #{threshold}"
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
namespace :metrics do
|
4
|
+
config = Central::Devtools.project.mutant
|
5
|
+
|
6
|
+
desc 'Measure mutation coverage'
|
7
|
+
task mutant: :coverage do
|
8
|
+
require 'mutant'
|
9
|
+
|
10
|
+
namespace =
|
11
|
+
if config.zombify
|
12
|
+
Mutant.zombify
|
13
|
+
Zombie::Mutant
|
14
|
+
else
|
15
|
+
Mutant
|
16
|
+
end
|
17
|
+
|
18
|
+
namespaces = Array(config.namespace).map { |n| "#{n}*" }
|
19
|
+
|
20
|
+
ignore_subjects = config.ignore_subjects.flat_map do |matcher|
|
21
|
+
%W(--ignore #{matcher})
|
22
|
+
end
|
23
|
+
|
24
|
+
jobs = ENV.key?('CIRCLECI') ? %w(--jobs 4) : []
|
25
|
+
|
26
|
+
since =
|
27
|
+
if config.since
|
28
|
+
%W(--since #{config.since})
|
29
|
+
else
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
33
|
+
arguments = %W(
|
34
|
+
--include lib
|
35
|
+
--require #{config.name}
|
36
|
+
--expect-coverage #{config.expect_coverage}
|
37
|
+
--use #{config.strategy}
|
38
|
+
).concat(ignore_subjects).concat(namespaces).concat(since).concat(jobs)
|
39
|
+
|
40
|
+
unless namespace::CLI.run(arguments)
|
41
|
+
Central::Devtools.notify_metric_violation('Mutant task is not successful')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
namespace :metrics do
|
4
|
+
desc 'Check with code style guide'
|
5
|
+
task :rubocop do
|
6
|
+
require 'rubocop'
|
7
|
+
config = Central::Devtools.project.rubocop
|
8
|
+
begin
|
9
|
+
exit_status = RuboCop::CLI.new.run(%W(--config #{config.config_file}))
|
10
|
+
fail 'Rubocop not successful' unless exit_status.zero?
|
11
|
+
rescue Encoding::CompatibilityError => exception
|
12
|
+
Central::Devtools.notify_metric_violation exception.message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
namespace :metrics do
|
4
|
+
namespace :yardstick do
|
5
|
+
require 'yardstick/rake/measurement'
|
6
|
+
require 'yardstick/rake/verify'
|
7
|
+
|
8
|
+
options = Central::Devtools.project.yardstick.options
|
9
|
+
|
10
|
+
if options['threshold']
|
11
|
+
Yardstick::Rake::Measurement.new(:measure, options)
|
12
|
+
Yardstick::Rake::Verify.new(:verify, options)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
# Remove existing same-named tasks
|
7
|
+
%w(spec spec:unit spec:integration).each do |task|
|
8
|
+
klass = Rake::Task
|
9
|
+
klass[task].clear if klass.task_defined?(task)
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Run all specs'
|
13
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
14
|
+
task.pattern = 'spec/{unit,integration}/**/*_spec.rb'
|
15
|
+
end
|
16
|
+
|
17
|
+
namespace :spec do
|
18
|
+
desc 'Run unit specs'
|
19
|
+
RSpec::Core::RakeTask.new(:unit) do |task|
|
20
|
+
task.pattern = 'spec/unit/**/*_spec.rb'
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'Run integration specs'
|
24
|
+
RSpec::Core::RakeTask.new(:integration) do |task|
|
25
|
+
task.pattern = 'spec/integration/**/*_spec.rb'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
rescue LoadError
|
29
|
+
%w(spec spec:unit spec:integration).each do |name|
|
30
|
+
task name do
|
31
|
+
$stderr.puts "In order to run #{name}, do: gem install rspec"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
task test: :spec
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
describe Central::Devtools::Rake::Flay, '#verify' do
|
4
|
+
let(:tempfile) { Tempfile.new(%w(file .rb), Dir.mktmpdir) }
|
5
|
+
let(:file) { Pathname(tempfile.path) }
|
6
|
+
let(:directories) { [file.dirname.to_s] }
|
7
|
+
|
8
|
+
let(:ruby) do
|
9
|
+
<<-ERUBY
|
10
|
+
def foo; end
|
11
|
+
def bar; end
|
12
|
+
ERUBY
|
13
|
+
end
|
14
|
+
|
15
|
+
around(:each) do |example|
|
16
|
+
begin
|
17
|
+
# silence other flay output
|
18
|
+
$stdout = $stderr = StringIO.new
|
19
|
+
|
20
|
+
tempfile.write(ruby)
|
21
|
+
tempfile.close
|
22
|
+
|
23
|
+
example.run
|
24
|
+
ensure
|
25
|
+
$stdout = STDOUT
|
26
|
+
$stderr = STDERR
|
27
|
+
|
28
|
+
file.unlink
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'reporting' do
|
33
|
+
let(:options) do
|
34
|
+
{ threshold: 3, total_score: 3, lib_dirs: directories, excludes: [] }.freeze
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:instance) { described_class.new(options) }
|
38
|
+
|
39
|
+
it 'measures total mass' do
|
40
|
+
allow(::Flay).to receive(:new).and_call_original
|
41
|
+
|
42
|
+
instance.verify
|
43
|
+
|
44
|
+
expect(::Flay).to have_received(:new).with(hash_including(mass: 0))
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not report the files it is processing' do
|
48
|
+
expect { instance.verify }.to_not output(/Processing #{file}/).to_stderr
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when theshold is too low' do
|
53
|
+
let(:instance) do
|
54
|
+
described_class.new(threshold: 0, total_score: 0, lib_dirs: directories, excludes: [])
|
55
|
+
end
|
56
|
+
|
57
|
+
specify do
|
58
|
+
expect { instance.verify }
|
59
|
+
.to raise_error(SystemExit)
|
60
|
+
.with_message('Flay total is now 3, but expected 0')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when threshold is too high' do
|
65
|
+
let(:instance) do
|
66
|
+
described_class.new(threshold: 1000, total_score: 0, lib_dirs: directories, excludes: [])
|
67
|
+
end
|
68
|
+
|
69
|
+
specify do
|
70
|
+
expect { instance.verify }
|
71
|
+
.to raise_error(SystemExit)
|
72
|
+
.with_message('Adjust flay threshold down to 3')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when total is too high' do
|
77
|
+
let(:instance) do
|
78
|
+
described_class.new(threshold: 3, total_score: 50, lib_dirs: directories, excludes: [])
|
79
|
+
end
|
80
|
+
|
81
|
+
specify do
|
82
|
+
expect { instance.verify }
|
83
|
+
.to raise_error(SystemExit)
|
84
|
+
.with_message('Flay total is now 3, but expected 50')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when duplicate mass is greater than 0' do
|
89
|
+
let(:ruby) do
|
90
|
+
<<-ERUBY
|
91
|
+
def foo
|
92
|
+
:hi if baz?
|
93
|
+
end
|
94
|
+
|
95
|
+
def bar
|
96
|
+
:hi if baz?
|
97
|
+
end
|
98
|
+
ERUBY
|
99
|
+
end
|
100
|
+
|
101
|
+
let(:report) do
|
102
|
+
<<-REPORT
|
103
|
+
Total score (lower is better) = 10
|
104
|
+
|
105
|
+
Similar code found in :defn (mass = 10)
|
106
|
+
#{file}:1
|
107
|
+
#{file}:5
|
108
|
+
REPORT
|
109
|
+
end
|
110
|
+
|
111
|
+
let(:instance) do
|
112
|
+
described_class.new(threshold: 3, total_score: 5, lib_dirs: directories, excludes: [])
|
113
|
+
end
|
114
|
+
|
115
|
+
specify do
|
116
|
+
expect { instance.verify }
|
117
|
+
.to raise_error(SystemExit)
|
118
|
+
.with_message('1 chunks have a duplicate mass > 3')
|
119
|
+
end
|
120
|
+
|
121
|
+
specify do
|
122
|
+
expect { instance.verify }
|
123
|
+
.to raise_error(SystemExit)
|
124
|
+
.and output(report).to_stdout
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'when multiple duplicate masses' do
|
129
|
+
let(:ruby) do
|
130
|
+
<<-ERUBY
|
131
|
+
def foo; end
|
132
|
+
def bar; end
|
133
|
+
|
134
|
+
class Foo
|
135
|
+
def initialize
|
136
|
+
@a = 1
|
137
|
+
end
|
138
|
+
end
|
139
|
+
class Bar
|
140
|
+
def initialize
|
141
|
+
@a = 1
|
142
|
+
end
|
143
|
+
end
|
144
|
+
ERUBY
|
145
|
+
end
|
146
|
+
|
147
|
+
let(:instance) do
|
148
|
+
described_class.new(threshold: 5, total_score: 8, lib_dirs: directories, excludes: [])
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'sums masses for total' do
|
152
|
+
expect { instance.verify }.to_not raise_error
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'when no duplication masses' do
|
157
|
+
let(:ruby) { '' }
|
158
|
+
let(:instance) do
|
159
|
+
described_class.new(threshold: 0, total_score: 0, lib_dirs: directories, excludes: [])
|
160
|
+
end
|
161
|
+
|
162
|
+
specify do
|
163
|
+
expect { instance.verify }.to_not raise_error
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'central/devtools/spec_helper'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'tmpdir'
|
6
|
+
|
7
|
+
if ENV['COVERAGE'] == 'true'
|
8
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
9
|
+
SimpleCov::Formatter::HTMLFormatter
|
10
|
+
]
|
11
|
+
|
12
|
+
SimpleCov.start do
|
13
|
+
command_name 'spec:unit'
|
14
|
+
|
15
|
+
add_filter 'config'
|
16
|
+
add_filter 'spec'
|
17
|
+
add_filter 'vendor'
|
18
|
+
|
19
|
+
minimum_coverage 100
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec.configure do |config|
|
24
|
+
config.expect_with :rspec do |expect_with|
|
25
|
+
expect_with.syntax = :expect
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
RSpec.describe Central::Devtools::Config::Yardstick do
|
4
|
+
let(:object) { described_class.new(Central::Devtools.root.join('config')) }
|
5
|
+
|
6
|
+
describe '#options' do
|
7
|
+
subject { object.options }
|
8
|
+
|
9
|
+
specify do
|
10
|
+
should eql(
|
11
|
+
'threshold' => 100,
|
12
|
+
'rules' => nil,
|
13
|
+
'verbose' => nil,
|
14
|
+
'path' => nil,
|
15
|
+
'require_exact_threshold' => nil
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
RSpec.describe Central::Devtools::Config do
|
4
|
+
describe '.attribute' do
|
5
|
+
let(:raw) do
|
6
|
+
{ 'a' => 'bar', 'c' => [] }
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:config_path) { instance_double(Pathname) }
|
10
|
+
|
11
|
+
let(:class_under_test) do
|
12
|
+
expect(config_path).to receive(:file?)
|
13
|
+
.and_return(file?)
|
14
|
+
expect(config_path).to receive(:frozen?)
|
15
|
+
.and_return(true)
|
16
|
+
expect(config_path).to receive(:join)
|
17
|
+
.with('bar.yml')
|
18
|
+
.and_return(config_path)
|
19
|
+
|
20
|
+
Class.new(described_class) do
|
21
|
+
attribute :a, [String]
|
22
|
+
attribute :b, [Array], default: []
|
23
|
+
attribute :c, [TrueClass, FalseClass]
|
24
|
+
|
25
|
+
const_set(:FILE, 'bar.yml')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
subject do
|
30
|
+
class_under_test.new(config_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'on present config' do
|
34
|
+
let(:class_under_test) do
|
35
|
+
# Setup message expectation in a lasy way, not in a before
|
36
|
+
# block to make sure the around hook setting timeouts from the
|
37
|
+
# code under test is not affected.
|
38
|
+
expect(YAML).to receive(:load_file)
|
39
|
+
.with(config_path)
|
40
|
+
.and_return(raw)
|
41
|
+
|
42
|
+
expect(IceNine).to receive(:deep_freeze)
|
43
|
+
.with(raw)
|
44
|
+
.and_return(raw)
|
45
|
+
|
46
|
+
super()
|
47
|
+
end
|
48
|
+
|
49
|
+
let(:file?) { true }
|
50
|
+
|
51
|
+
it 'allows to receive existing keys' do
|
52
|
+
expect(subject.a).to eql('bar')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'allows to receive absent keys with defaults' do
|
56
|
+
expect(subject.b).to eql([])
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'executes checks when configured' do
|
60
|
+
expect { subject.c }.to raise_error(
|
61
|
+
Central::Devtools::Config::TypeError,
|
62
|
+
'c: Got instance of Array expected TrueClass,FalseClass'
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'on absent config' do
|
68
|
+
let(:file?) { false }
|
69
|
+
|
70
|
+
it 'defaults to absent keys' do
|
71
|
+
expect(subject.b).to eql([])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
describe Central::Devtools::Flay::FileList, '.call' do
|
4
|
+
subject(:output) { described_class.call([tmpdir.to_s], [exclude]) }
|
5
|
+
|
6
|
+
let(:tmpdir) { Dir.mktmpdir }
|
7
|
+
let(:one) { Pathname(tmpdir).join('1.rb') }
|
8
|
+
let(:two) { Pathname(tmpdir).join('2.erb') }
|
9
|
+
let(:three) { Pathname(tmpdir).join('3.rb') }
|
10
|
+
let(:exclude) { Pathname(tmpdir).join('3*').to_s }
|
11
|
+
|
12
|
+
around(:each) do |example|
|
13
|
+
[one, two, three].map(&FileUtils.method(:touch))
|
14
|
+
|
15
|
+
example.run
|
16
|
+
|
17
|
+
FileUtils.rm_rf(tmpdir)
|
18
|
+
end
|
19
|
+
|
20
|
+
it { should eql(Set.new([one, two])) }
|
21
|
+
end
|