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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +37 -0
  3. data/.rspec +6 -0
  4. data/.rubocop.yml +5 -0
  5. data/.ruby-gemset +1 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +191 -0
  8. data/README.md +36 -0
  9. data/Rakefile +8 -0
  10. data/central-devtools.gemspec +44 -0
  11. data/config/container.yml +7 -0
  12. data/config/devtools.yml +2 -0
  13. data/config/flay.yml +3 -0
  14. data/config/flog.yml +2 -0
  15. data/config/mutant.yml +4 -0
  16. data/config/reek.yml +102 -0
  17. data/config/rubocop.yml +117 -0
  18. data/config/yardstick.yml +2 -0
  19. data/lib/central/devtools/config.rb +199 -0
  20. data/lib/central/devtools/flay.rb +91 -0
  21. data/lib/central/devtools/project/initializer/rake.rb +23 -0
  22. data/lib/central/devtools/project/initializer/rspec.rb +70 -0
  23. data/lib/central/devtools/project/initializer.rb +13 -0
  24. data/lib/central/devtools/project.rb +80 -0
  25. data/lib/central/devtools/rake/flay.rb +121 -0
  26. data/lib/central/devtools/spec_helper.rb +5 -0
  27. data/lib/central/devtools/version.rb +7 -0
  28. data/lib/central/devtools.rb +90 -0
  29. data/shared/spec/shared/abstract_type_behavior.rb +16 -0
  30. data/shared/spec/shared/command_method_behavior.rb +5 -0
  31. data/shared/spec/shared/each_method_behaviour.rb +13 -0
  32. data/shared/spec/shared/hash_method_behavior.rb +7 -0
  33. data/shared/spec/shared/idempotent_method_behavior.rb +10 -0
  34. data/shared/spec/support/ice_nine_config.rb +9 -0
  35. data/shared/tasks/docker.rake +80 -0
  36. data/shared/tasks/metrics/ci.rake +19 -0
  37. data/shared/tasks/metrics/coverage.rake +14 -0
  38. data/shared/tasks/metrics/flay.rake +21 -0
  39. data/shared/tasks/metrics/flog.rake +38 -0
  40. data/shared/tasks/metrics/mutant.rake +44 -0
  41. data/shared/tasks/metrics/reek.rake +10 -0
  42. data/shared/tasks/metrics/rubocop.rake +15 -0
  43. data/shared/tasks/metrics/yardstick.rake +15 -0
  44. data/shared/tasks/spec.rake +36 -0
  45. data/shared/tasks/yard.rake +11 -0
  46. data/spec/integration/central/devtools/rake/flay/verify_spec.rb +166 -0
  47. data/spec/spec_helper.rb +27 -0
  48. data/spec/unit/central/devtools/config/yardstick_spec.rb +19 -0
  49. data/spec/unit/central/devtools/config_spec.rb +75 -0
  50. data/spec/unit/central/devtools/flay/file_list/call_spec.rb +21 -0
  51. data/spec/unit/central/devtools/flay/scale/flay_report_spec.rb +19 -0
  52. data/spec/unit/central/devtools/flay/scale/measure_spec.rb +45 -0
  53. data/spec/unit/central/devtools/project/initializer/rake_spec.rb +23 -0
  54. data/spec/unit/central/devtools/project/initializer/rspec_spec.rb +55 -0
  55. data/spec/unit/central/devtools/project_spec.rb +37 -0
  56. data/spec/unit/central/devtools_spec.rb +16 -0
  57. 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,5 @@
1
+ shared_examples_for 'a command method' do
2
+ it 'returns self' do
3
+ should equal(object)
4
+ end
5
+ 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,7 @@
1
+ shared_examples_for 'a hash method' do
2
+ it_should_behave_like 'an idempotent method'
3
+
4
+ it 'is a Fixnum' do
5
+ should be_instance_of(Fixnum)
6
+ end
7
+ 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,9 @@
1
+ if defined?(IceNine)
2
+ module IceNine
3
+ # Freezer namespace
4
+ class Freezer
5
+ # Rspec freezer
6
+ class RSpec < NoFreeze; end
7
+ end # Freezer
8
+ end # IceNine
9
+ 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,10 @@
1
+ # encoding: UTF-8
2
+
3
+ namespace :metrics do
4
+ require 'reek/rake/task'
5
+
6
+ Reek::Rake::Task.new do |reek|
7
+ reek.source_files = '{app,lib}/**/*.rb'
8
+ reek.config_file = 'config/reek.yml'
9
+ end
10
+ 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,11 @@
1
+ # encoding: UTF-8
2
+
3
+ begin
4
+ require 'yard'
5
+
6
+ YARD::Rake::YardocTask.new
7
+ rescue LoadError
8
+ task :yard do
9
+ $stderr.puts 'In order to run yard, you must: gem install yard'
10
+ end
11
+ end
@@ -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
@@ -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