rubycritic 3.4.0 → 3.5.0

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -2
  3. data/README.md +12 -10
  4. data/lib/rubycritic/cli/options.rb +25 -3
  5. data/lib/rubycritic/command_factory.rb +3 -0
  6. data/lib/rubycritic/commands/ci.rb +2 -8
  7. data/lib/rubycritic/commands/compare.rb +105 -0
  8. data/lib/rubycritic/commands/utils/build_number_file.rb +35 -0
  9. data/lib/rubycritic/configuration.rb +18 -1
  10. data/lib/rubycritic/core/analysed_modules_collection.rb +23 -3
  11. data/lib/rubycritic/generators/html/assets/stylesheets/application.css +20 -0
  12. data/lib/rubycritic/generators/html/base.rb +1 -1
  13. data/lib/rubycritic/generators/html/code_file.rb +11 -0
  14. data/lib/rubycritic/generators/html/code_index.rb +7 -0
  15. data/lib/rubycritic/generators/html/overview.rb +7 -0
  16. data/lib/rubycritic/generators/html/smells_index.rb +7 -0
  17. data/lib/rubycritic/generators/html/templates/code_index.html.erb +10 -0
  18. data/lib/rubycritic/generators/html/templates/layouts/application.html.erb +7 -0
  19. data/lib/rubycritic/generators/html/view_helpers.rb +10 -1
  20. data/lib/rubycritic/source_control_systems/git.rb +27 -1
  21. data/lib/rubycritic/version.rb +1 -1
  22. metadata +7 -131
  23. data/.gitignore +0 -23
  24. data/.rubocop.yml +0 -37
  25. data/.rubocop_todo.yml +0 -45
  26. data/.todo.reek +0 -142
  27. data/.travis.yml +0 -29
  28. data/.yardopts +0 -5
  29. data/docs/building-own-code-climate.md +0 -156
  30. data/docs/core-metrics.md +0 -72
  31. data/docs/jenkins-pr-reviews.md +0 -64
  32. data/features/command_line_interface/minimum_score.feature +0 -39
  33. data/features/command_line_interface/options.feature +0 -37
  34. data/features/rake_task.feature +0 -65
  35. data/features/step_definitions/rake_task_steps.rb +0 -5
  36. data/features/step_definitions/rubycritic_steps.rb +0 -33
  37. data/features/step_definitions/sample_file_steps.rb +0 -32
  38. data/features/support/env.rb +0 -43
  39. data/images/churn-vs-complexity.png +0 -0
  40. data/images/code.png +0 -0
  41. data/images/logo.png +0 -0
  42. data/images/overview.png +0 -0
  43. data/images/rating.png +0 -0
  44. data/images/reek.png +0 -0
  45. data/images/smell-details.png +0 -0
  46. data/images/smells.png +0 -0
  47. data/images/whitesmith.png +0 -0
  48. data/rubycritic.gemspec +0 -45
  49. data/test/analysers_test_helper.rb +0 -12
  50. data/test/lib/rubycritic/analysers/churn_test.rb +0 -35
  51. data/test/lib/rubycritic/analysers/complexity_test.rb +0 -18
  52. data/test/lib/rubycritic/analysers/helpers/methods_counter_test.rb +0 -31
  53. data/test/lib/rubycritic/analysers/helpers/modules_locator_test.rb +0 -55
  54. data/test/lib/rubycritic/analysers/smells/flay_test.rb +0 -41
  55. data/test/lib/rubycritic/analysers/smells/flog_test.rb +0 -28
  56. data/test/lib/rubycritic/analysers/smells/reek_test.rb +0 -32
  57. data/test/lib/rubycritic/analysis_summary_test.rb +0 -30
  58. data/test/lib/rubycritic/browser_test.rb +0 -18
  59. data/test/lib/rubycritic/commands/status_reporter_test.rb +0 -81
  60. data/test/lib/rubycritic/configuration_test.rb +0 -31
  61. data/test/lib/rubycritic/core/analysed_module_test.rb +0 -90
  62. data/test/lib/rubycritic/core/analysed_modules_collection_test.rb +0 -111
  63. data/test/lib/rubycritic/core/location_test.rb +0 -39
  64. data/test/lib/rubycritic/core/smell_test.rb +0 -105
  65. data/test/lib/rubycritic/core/smells_array_test.rb +0 -30
  66. data/test/lib/rubycritic/generators/console_report_test.rb +0 -83
  67. data/test/lib/rubycritic/generators/json_report_test.rb +0 -38
  68. data/test/lib/rubycritic/generators/lint_report_test.rb +0 -37
  69. data/test/lib/rubycritic/generators/turbulence_test.rb +0 -19
  70. data/test/lib/rubycritic/generators/view_helpers_test.rb +0 -85
  71. data/test/lib/rubycritic/revision_comparator_test.rb +0 -66
  72. data/test/lib/rubycritic/smells_status_setter_test.rb +0 -24
  73. data/test/lib/rubycritic/source_control_systems/base_test.rb +0 -31
  74. data/test/lib/rubycritic/source_control_systems/double_test.rb +0 -13
  75. data/test/lib/rubycritic/source_control_systems/git_test.rb +0 -15
  76. data/test/lib/rubycritic/source_control_systems/interfaces/basic.rb +0 -9
  77. data/test/lib/rubycritic/source_control_systems/interfaces/time_travel.rb +0 -9
  78. data/test/lib/rubycritic/source_control_systems/mercurial_test.rb +0 -13
  79. data/test/lib/rubycritic/source_control_systems/perforce_test.rb +0 -176
  80. data/test/lib/rubycritic/source_locator_test.rb +0 -80
  81. data/test/lib/rubycritic/version_test.rb +0 -10
  82. data/test/samples/empty.rb +0 -0
  83. data/test/samples/flay/smelly.rb +0 -8
  84. data/test/samples/flay/smelly2.rb +0 -8
  85. data/test/samples/flog/complex.rb +0 -11
  86. data/test/samples/flog/smelly.rb +0 -11
  87. data/test/samples/location/dir1/file1.rb +0 -0
  88. data/test/samples/location/file0.rb +0 -0
  89. data/test/samples/location/file0_symlink.rb +0 -1
  90. data/test/samples/location/file_with_different_extension.py +0 -0
  91. data/test/samples/location/file_with_no_extension +0 -0
  92. data/test/samples/methods_count.rb +0 -7
  93. data/test/samples/module_names.rb +0 -18
  94. data/test/samples/no_methods.rb +0 -4
  95. data/test/samples/reek/not_smelly.rb +0 -35
  96. data/test/samples/reek/smelly.rb +0 -17
  97. data/test/samples/unparsable.rb +0 -1
  98. data/test/test_helper.rb +0 -64
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/core/location'
5
-
6
- describe RubyCritic::Location do
7
- describe 'attribute readers' do
8
- before do
9
- @path = './foo.rb'
10
- @line = '42'
11
- @location = RubyCritic::Location.new(@path, @line)
12
- end
13
-
14
- it 'has a pathname' do
15
- @location.pathname.must_equal Pathname.new(@path)
16
- end
17
-
18
- it 'has a line number' do
19
- @location.line.must_equal @line.to_i
20
- end
21
-
22
- it 'has a file name' do
23
- @location.file_name.must_equal 'foo'
24
- end
25
- end
26
-
27
- it 'is comparable' do
28
- location1 = RubyCritic::Location.new('./foo', 42)
29
- location2 = RubyCritic::Location.new('./foo', 42)
30
- location1.must_equal location2
31
- end
32
-
33
- it 'is sortable' do
34
- location1 = RubyCritic::Location.new('./foo', 42)
35
- location2 = RubyCritic::Location.new('./bar', 23)
36
- location3 = RubyCritic::Location.new('./bar', 16)
37
- [location1, location2, location3].sort.must_equal [location3, location2, location1]
38
- end
39
- end
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/core/smell'
5
-
6
- describe RubyCritic::Smell do
7
- describe 'attribute readers' do
8
- before do
9
- @locations = [RubyCritic::Location.new('./foo', '42')]
10
- @context = '#bar'
11
- @message = 'This smells'
12
- @score = 0
13
- @type = :complexity
14
- @smell = RubyCritic::Smell.new(
15
- locations: @locations,
16
- context: @context,
17
- message: @message,
18
- score: @score,
19
- type: @type
20
- )
21
- end
22
-
23
- it 'has a context reader' do
24
- @smell.context.must_equal @context
25
- end
26
-
27
- it 'has a locations reader' do
28
- @smell.locations.must_equal @locations
29
- end
30
-
31
- it 'has a message reader' do
32
- @smell.message.must_equal @message
33
- end
34
-
35
- it 'has a score reader' do
36
- @smell.score.must_equal @score
37
- end
38
-
39
- it 'has a type reader' do
40
- @smell.type.must_equal @type
41
- end
42
- end
43
-
44
- describe '#at_location?' do
45
- it 'returns true if the smell has a location that matches the location passed as argument' do
46
- location = RubyCritic::Location.new('./foo', '42')
47
- smell = RubyCritic::Smell.new(locations: [location])
48
- smell.at_location?(location).must_equal true
49
- end
50
- end
51
-
52
- describe '#multiple_locations?' do
53
- it 'returns true if the smell has more than one location' do
54
- location1 = RubyCritic::Location.new('./foo', '42')
55
- location2 = RubyCritic::Location.new('./foo', '23')
56
- smell = RubyCritic::Smell.new(locations: [location1, location2])
57
- smell.multiple_locations?.must_equal true
58
- end
59
- end
60
-
61
- describe '#==' do
62
- it 'returns true if two smells have the same base attributes' do
63
- attributes = {
64
- context: '#bar',
65
- message: 'This smells',
66
- score: 0,
67
- type: :complexity
68
- }
69
- smell1 = RubyCritic::Smell.new(attributes)
70
- smell2 = RubyCritic::Smell.new(attributes)
71
- smell1.must_equal smell2
72
- end
73
- end
74
-
75
- describe '#doc_url' do
76
- it 'handles one word type names for reek smells' do
77
- smell = RubyCritic::Smell.new(type: 'Complexity', analyser: 'reek')
78
-
79
- smell.doc_url.must_equal('https://github.com/troessner/reek/blob/master/docs/Complexity.md')
80
- end
81
-
82
- it 'handles multiple word type names for reek smells' do
83
- smell = RubyCritic::Smell.new(type: 'TooManyStatements', analyser: 'reek')
84
-
85
- smell.doc_url.must_equal('https://github.com/troessner/reek/blob/master/docs/Too-Many-Statements.md')
86
- end
87
-
88
- it 'handles flay smells' do
89
- smell = RubyCritic::Smell.new(type: 'DuplicateCode', analyser: 'flay')
90
-
91
- smell.doc_url.must_equal('http://docs.seattlerb.org/flay/')
92
- end
93
-
94
- it 'handles flog smells' do
95
- smell = RubyCritic::Smell.new(type: 'VeryHighComplexity', analyser: 'flog')
96
-
97
- smell.doc_url.must_equal('http://docs.seattlerb.org/flog/')
98
- end
99
-
100
- it 'raises an error for unknown analysers' do
101
- smell = RubyCritic::Smell.new(type: 'FooSmell', analyser: 'foo')
102
- assert_raises { smell.doc_url }
103
- end
104
- end
105
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/core/smell'
5
-
6
- describe 'Array of Smells' do
7
- it 'is sortable' do
8
- location1 = RubyCritic::Location.new('./foo', 42)
9
- location2 = RubyCritic::Location.new('./bar', 23)
10
- location3 = RubyCritic::Location.new('./bar', 16)
11
- smell1 = RubyCritic::Smell.new(locations: [location1])
12
- smell2 = RubyCritic::Smell.new(locations: [location2])
13
- smell3 = RubyCritic::Smell.new(locations: [location3])
14
- [smell1, smell2, smell3].sort.must_equal [smell3, smell2, smell1]
15
- end
16
-
17
- it 'implements set intersection' do
18
- smell1 = RubyCritic::Smell.new(context: '#bar')
19
- smell2 = RubyCritic::Smell.new(context: '#bar')
20
- smell3 = RubyCritic::Smell.new(context: '#foo')
21
- ([smell1, smell3] & [smell2]).must_equal [smell1]
22
- end
23
-
24
- it 'implements set union' do
25
- smell1 = RubyCritic::Smell.new(context: '#bar')
26
- smell2 = RubyCritic::Smell.new(context: '#bar')
27
- smell3 = RubyCritic::Smell.new(context: '#foo')
28
- ([smell1, smell3] | [smell2]).must_equal [smell1, smell3]
29
- end
30
- end
@@ -1,83 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/generators/console_report'
5
- require 'rubycritic/core/rating'
6
- require 'rubycritic/core/smell'
7
-
8
- describe RubyCritic::Generator::ConsoleReport do
9
- describe '#generate_report' do
10
- before do
11
- @mock_analysed_module = mock_analysed_module
12
- capture_output_streams do
13
- report = RubyCritic::Generator::ConsoleReport.new([@mock_analysed_module])
14
- report.generate_report
15
- @output = $stdout.tap(&:rewind).read
16
- end
17
- end
18
-
19
- it 'outputs the report to the stdout' do
20
- assert !@output.empty?, 'expected report to be output to stdout'
21
- end
22
-
23
- it "starts the report with the module's name" do
24
- lines = @output.split("\n")
25
- assert lines[0][/#{mock_analysed_module.name}/]
26
- end
27
-
28
- it "includes the module's rating in the report" do
29
- assert output_contains?('Rating', @mock_analysed_module.rating)
30
- end
31
-
32
- it "includes the module's churn metric in the report" do
33
- assert output_contains?('Churn', @mock_analysed_module.churn)
34
- end
35
-
36
- it "includes the module's complexity in the report" do
37
- assert output_contains?('Complexity', @mock_analysed_module.complexity)
38
- end
39
-
40
- it "includes the module's duplication metric in the report" do
41
- assert output_contains?('Duplication', @mock_analysed_module.duplication)
42
- end
43
-
44
- it 'includes the number of smells in the report' do
45
- assert output_contains?('Smells', @mock_analysed_module.smells.count)
46
- end
47
-
48
- it 'includes the smell and its attributes in the report' do
49
- @mock_analysed_module.smells.each do |smell|
50
- assert output_contains?(smell), 'expected smell type and context to be reported'
51
- smell.locations.each do |location|
52
- assert output_contains?(location), 'expected all smell locations to be reported'
53
- end
54
- end
55
- end
56
-
57
- def output_contains?(*strs)
58
- @lines ||= @output.split("\n")
59
- expr = strs.map(&:to_s).map! { |s| Regexp.escape(s) }.join('.*')
60
- @lines.any? { |l| l[/#{expr}/] }
61
- end
62
-
63
- def mock_analysed_module
64
- OpenStruct.new(
65
- name: 'TestModule',
66
- rating: RubyCritic::Rating.from_cost(3),
67
- churn: 10,
68
- complexity: 0,
69
- duplication: 20,
70
- smells: [mock_smell]
71
- )
72
- end
73
-
74
- def mock_smell
75
- smell = RubyCritic::Smell.new
76
- smell.locations << RubyCritic::Location.new(__FILE__, 3)
77
- smell.type = 'SmellySmell'
78
- smell.context = 'You'
79
- smell.message = 'Seriously, take a shower or something'
80
- smell
81
- end
82
- end
83
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/analysers_runner'
5
- require 'rubycritic/generators/json_report'
6
- require 'json'
7
- require 'fakefs/safe'
8
-
9
- describe RubyCritic::Generator::JsonReport do
10
- describe '#generate_report' do
11
- around do |example|
12
- capture_output_streams do
13
- with_cloned_fs(&example)
14
- end
15
- end
16
-
17
- it 'creates a report file with JSON data inside' do
18
- sample_files = Dir['test/samples/**/*.rb']
19
- create_analysed_modules_collection
20
- generate_report
21
- data = JSON.parse(File.read('test/samples/report.json'))
22
- analysed_files = data['analysed_modules'].map { |h| h['path'] }.uniq
23
- assert_matched_arrays analysed_files, sample_files
24
- end
25
- end
26
-
27
- def create_analysed_modules_collection
28
- RubyCritic::Config.root = 'test/samples'
29
- RubyCritic::Config.source_control_system = RubyCritic::SourceControlSystem::Git.new
30
- analyser_runner = RubyCritic::AnalysersRunner.new('test/samples/')
31
- @analysed_modules_collection = analyser_runner.run
32
- end
33
-
34
- def generate_report
35
- report = RubyCritic::Generator::JsonReport.new(@analysed_modules_collection)
36
- report.generate_report
37
- end
38
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/analysers_runner'
5
- require 'rubycritic/generators/lint_report'
6
- require 'fakefs/safe'
7
-
8
- describe RubyCritic::Generator::LintReport do
9
- describe '#generate_report' do
10
- around do |example|
11
- capture_output_streams do
12
- with_cloned_fs(&example)
13
- end
14
- end
15
-
16
- it 'report file has data inside' do
17
- sample_files = Dir['test/samples/**/*.rb'].reject { |f| File.zero?(f) }
18
- create_analysed_modules_collection
19
- generate_report
20
- lines = File.readlines('test/samples/lint.txt').map(&:strip).reject(&:empty?)
21
- analysed_files = lines.map { |line| line.split(':').first }.uniq
22
- assert_matched_arrays analysed_files, sample_files
23
- end
24
- end
25
-
26
- def create_analysed_modules_collection
27
- RubyCritic::Config.root = 'test/samples'
28
- RubyCritic::Config.source_control_system = RubyCritic::SourceControlSystem::Git.new
29
- analyser_runner = RubyCritic::AnalysersRunner.new('test/samples/')
30
- @analysed_modules_collection = analyser_runner.run
31
- end
32
-
33
- def generate_report
34
- report = RubyCritic::Generator::LintReport.new(@analysed_modules_collection)
35
- report.generate_report
36
- end
37
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/generators/html/turbulence'
5
-
6
- describe RubyCritic::Turbulence do
7
- describe '::data' do
8
- it 'returns json data that maps pathname, churn and complexity to name, x and y' do
9
- files = [AnalysedModuleDouble.new(name: 'Foo', churn: 1, complexity: 2)]
10
- turbulence_data = RubyCritic::Turbulence.data(files)
11
- instance_parsed_json = JSON.parse(turbulence_data).first
12
- instance_parsed_json['name'].must_equal 'Foo'
13
- instance_parsed_json['x'].must_equal 1
14
- instance_parsed_json['y'].must_equal 2
15
- end
16
- end
17
- end
18
-
19
- class AnalysedModuleDouble < OpenStruct; end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/generators/html/view_helpers'
5
- require 'pathname'
6
-
7
- describe RubyCritic::ViewHelpers do
8
- context 'when the file is in the root directory' do
9
- let(:generator) { GeneratorDouble.new('foo.html') }
10
-
11
- describe '#file_path' do
12
- context 'when the other file is in the same directory' do
13
- it 'creates a relative path to a file' do
14
- generator.file_path('bar.html').to_s.must_equal 'bar.html'
15
- end
16
- end
17
-
18
- context 'when the other file is in a subdirectory' do
19
- it 'creates a relative path to a file' do
20
- generator.file_path('subdirectory/bar.html').to_s.must_equal 'subdirectory/bar.html'
21
- end
22
- end
23
- end
24
-
25
- describe '#asset_path' do
26
- it 'creates a relative path to an asset' do
27
- generator.asset_path('stylesheets/application.css').to_s
28
- .must_equal 'assets/stylesheets/application.css'
29
- end
30
- end
31
- end
32
-
33
- context 'when the file is n directories deep' do
34
- let(:generator) { GeneratorDouble.new('lets/go/crazy/foo.html') }
35
-
36
- describe '#file_path' do
37
- context 'when the other file is in the same directory' do
38
- it 'creates a relative path to a file' do
39
- generator.file_path('lets/go/crazy/bar.html').to_s.must_equal 'bar.html'
40
- end
41
- end
42
-
43
- context 'when the other file is in the root directory' do
44
- it 'creates a relative path to a file' do
45
- generator.file_path('bar.html').to_s.must_equal '../../../bar.html'
46
- end
47
- end
48
-
49
- context 'when the other file has n-1 directories in common' do
50
- it 'creates a relative path to a file' do
51
- generator.file_path('lets/go/home/bar.html').to_s.must_equal '../home/bar.html'
52
- end
53
- end
54
-
55
- context 'when the other file is one directory deeper' do
56
- it 'creates a relative path to a file' do
57
- generator.file_path('lets/go/crazy/everybody/bar.html').to_s.must_equal 'everybody/bar.html'
58
- end
59
- end
60
- end
61
-
62
- describe '#asset_path' do
63
- it 'creates a relative path to an asset' do
64
- generator.asset_path('stylesheets/application.css').to_s
65
- .must_equal '../../../assets/stylesheets/application.css'
66
- end
67
- end
68
- end
69
- end
70
-
71
- class GeneratorDouble
72
- include RubyCritic::ViewHelpers
73
-
74
- def initialize(file)
75
- @file = Pathname.new(file)
76
- end
77
-
78
- def file_directory
79
- root_directory + @file.dirname
80
- end
81
-
82
- def root_directory
83
- Pathname.new('root_directory')
84
- end
85
- end
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/revision_comparator'
5
-
6
- describe RubyCritic::RevisionComparator do
7
- subject { RubyCritic::RevisionComparator.new([]) }
8
-
9
- describe '#set_statuses' do
10
- context 'in a SCS with :revision? == false' do
11
- before do
12
- RubyCritic::Config.expects(:source_control_system)
13
- .at_least_once
14
- .returns(stub(revision?: false))
15
- end
16
-
17
- it 'does not attempt to compare with previous results' do
18
- subject.expects(:load_cached_analysed_modules).never
19
- result = subject.set_statuses([])
20
- result.must_equal([])
21
- end
22
- end
23
-
24
- context 'in a SCS with :revision? == true' do
25
- before do
26
- RubyCritic::Config.expects(:source_control_system)
27
- .at_least_once
28
- .returns(stub(revision?: true))
29
- end
30
-
31
- context 'without previously cached results' do
32
- before do
33
- subject.expects(:revision_file).returns('foo')
34
- File.expects(:file?).with('foo').returns(false)
35
- end
36
-
37
- it 'does not load them' do
38
- RubyCritic::Serializer.expects(:new).never
39
- subject.set_statuses([])
40
- end
41
-
42
- it 'does not invoke RubyCritic::SmellsStatusSetter' do
43
- RubyCritic::SmellsStatusSetter.expects(:set).never
44
- subject.set_statuses([])
45
- end
46
- end
47
-
48
- context 'with previously cached results' do
49
- before do
50
- subject.expects(:revision_file).twice.returns('foo')
51
- File.expects(:file?).with('foo').returns(true)
52
- RubyCritic::Serializer.expects(:new).with('foo').returns(stub(load: []))
53
- end
54
-
55
- it 'loads them' do
56
- subject.set_statuses([])
57
- end
58
-
59
- it 'invokes RubyCritic::SmellsStatusSetter' do
60
- RubyCritic::SmellsStatusSetter.expects(:set).once
61
- subject.set_statuses([])
62
- end
63
- end
64
- end
65
- end
66
- end