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,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/analysers/smells/flay'
5
- require 'rubycritic/core/analysed_module'
6
- require 'pathname'
7
-
8
- describe RubyCritic::Analyser::FlaySmells do
9
- context 'when analysing a bunch of files with duplicate code' do
10
- before do
11
- @analysed_modules = [
12
- RubyCritic::AnalysedModule.new(pathname: Pathname.new('test/samples/flay/smelly.rb')),
13
- RubyCritic::AnalysedModule.new(pathname: Pathname.new('test/samples/flay/smelly2.rb'))
14
- ]
15
- RubyCritic::Analyser::FlaySmells.new(@analysed_modules).run
16
- end
17
-
18
- it 'detects its smells' do
19
- @analysed_modules.first.smells?.must_equal true
20
- end
21
-
22
- it 'creates smells with messages' do
23
- smell = @analysed_modules.first.smells.first
24
- smell.message.must_be_instance_of String
25
- end
26
-
27
- it 'creates smells with scores' do
28
- smell = @analysed_modules.first.smells.first
29
- smell.score.must_be_kind_of Numeric
30
- end
31
-
32
- it 'creates smells with more than one location' do
33
- smell = @analysed_modules.first.smells.first
34
- smell.multiple_locations?.must_equal true
35
- end
36
-
37
- it 'calculates the mass of duplicate code' do
38
- @analysed_modules.first.duplication.must_be(:>, 0)
39
- end
40
- end
41
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'analysers_test_helper'
4
- require 'rubycritic/analysers/smells/flog'
5
-
6
- describe RubyCritic::Analyser::FlogSmells do
7
- context 'when analysing a complex file' do
8
- before do
9
- @analysed_module = AnalysedModuleDouble.new(path: 'test/samples/flog/smelly.rb', smells: [])
10
- analysed_modules = [@analysed_module]
11
- RubyCritic::Analyser::FlogSmells.new(analysed_modules).run
12
- end
13
-
14
- it 'detects its smells' do
15
- @analysed_module.smells.length.must_equal 1
16
- end
17
-
18
- it 'creates smells with messages' do
19
- smell = @analysed_module.smells.first
20
- smell.message.must_be_instance_of String
21
- end
22
-
23
- it 'creates smells with scores' do
24
- smell = @analysed_module.smells.first
25
- smell.score.must_be :>, 0
26
- end
27
- end
28
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'analysers_test_helper'
4
- require 'rubycritic/analysers/smells/reek'
5
-
6
- describe RubyCritic::Analyser::ReekSmells do
7
- context 'when analysing a smelly file' do
8
- before do
9
- pathname = Pathname.new('test/samples/reek/smelly.rb')
10
- @analysed_module = AnalysedModuleDouble.new(pathname: pathname, smells: [])
11
- analysed_modules = [@analysed_module]
12
- RubyCritic::Analyser::ReekSmells.new(analysed_modules).run
13
- end
14
-
15
- it 'detects its smells' do
16
- @analysed_module.smells.length.must_equal 2
17
- end
18
-
19
- it 'respects the .reek file' do
20
- messages = @analysed_module.smells.map(&:message)
21
- messages.wont_include "has the parameter name 'a'"
22
- end
23
-
24
- it 'creates smells with messages' do
25
- first_smell = @analysed_module.smells.first
26
- first_smell.message.must_equal "has boolean parameter 'reek'"
27
-
28
- last_smell = @analysed_module.smells.last
29
- last_smell.message.must_equal 'has no descriptive comment'
30
- end
31
- end
32
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/analysis_summary'
5
-
6
- module RubyCritic
7
- describe AnalysisSummary do
8
- before do
9
- analysed_modules = AnalysedModulesCollectionDouble.new(
10
- [
11
- AnalysedModuleDouble.new(rating: 'A', churn: 2, smells: %i[a b c]),
12
- AnalysedModuleDouble.new(rating: 'A', churn: 3, smells: [:b]),
13
- AnalysedModuleDouble.new(rating: 'A', churn: 4, smells: %i[x y]),
14
- AnalysedModuleDouble.new(rating: 'B', churn: 5, smells: %i[a z])
15
- ]
16
- )
17
- @summary = RubyCritic::AnalysisSummary.generate(analysed_modules)
18
- end
19
-
20
- describe '.root' do
21
- it 'computes correct summary' do
22
- @summary['A'].to_a.must_equal({ files: 3, churns: 9, smells: 6 }.to_a)
23
- @summary['B'].to_a.must_equal({ files: 1, churns: 5, smells: 2 }.to_a)
24
- @summary['C'].to_a.must_equal({ files: 0, churns: 0, smells: 0 }.to_a)
25
- @summary['D'].to_a.must_equal({ files: 0, churns: 0, smells: 0 }.to_a)
26
- @summary['F'].to_a.must_equal({ files: 0, churns: 0, smells: 0 }.to_a)
27
- end
28
- end
29
- end
30
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/browser'
5
-
6
- describe RubyCritic::Browser do
7
- before do
8
- @report_path = 'tmp/rubycritic/overview.html'
9
- @browser = RubyCritic::Browser.new @report_path
10
- end
11
-
12
- describe '#open' do
13
- it 'should be open report with launch browser' do
14
- Launchy.stubs(:open).returns(true)
15
- @browser.open.must_equal true
16
- end
17
- end
18
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/commands/status_reporter'
5
- require 'rubycritic/cli/options'
6
-
7
- describe RubyCritic::Command::StatusReporter do
8
- let(:success_status) { RubyCritic::Command::StatusReporter::SUCCESS }
9
- let(:score_below_minimum) { RubyCritic::Command::StatusReporter::SCORE_BELOW_MINIMUM }
10
-
11
- describe 'with default options' do
12
- before do
13
- @options = RubyCritic::Cli::Options.new([])
14
- @options.parse
15
- @reporter = RubyCritic::Command::StatusReporter.new(@options.to_h)
16
- end
17
-
18
- it 'has a default' do
19
- @reporter.status.must_equal success_status
20
- @reporter.status_message.must_be_nil
21
- end
22
-
23
- it 'accept a score' do
24
- @reporter.score = 50.0
25
- @reporter.status.must_equal success_status
26
- @reporter.status_message.must_equal 'Score: 50.0'
27
- end
28
-
29
- it 'should format the score' do
30
- @reporter.score = 98.95258620689656
31
- @reporter.status.must_equal success_status
32
- @reporter.status_message.must_equal 'Score: 98.95'
33
- end
34
- end
35
-
36
- describe 'with minimum-score option' do
37
- before do
38
- @options = RubyCritic::Cli::Options.new(['-s', '99'])
39
- @options.parse
40
- @reporter = RubyCritic::Command::StatusReporter.new(@options.to_h)
41
- end
42
-
43
- it 'has a default' do
44
- @reporter.status.must_equal success_status
45
- @reporter.status_message.must_be_nil
46
- end
47
-
48
- describe 'when score is below minimum' do
49
- let(:score) { 98.0 }
50
- it 'should return the correct status' do
51
- @reporter.score = score
52
- @reporter.status.must_equal score_below_minimum
53
- @reporter.status_message.must_equal 'Score (98.0) is below the minimum 99.0'
54
- end
55
-
56
- it 'should format the score' do
57
- @reporter.score = 98.95258620689656
58
- @reporter.status.must_equal score_below_minimum
59
- @reporter.status_message.must_equal 'Score (98.95) is below the minimum 99.0'
60
- end
61
- end
62
-
63
- describe 'when score is equal the minimum' do
64
- let(:score) { 99.0 }
65
- it 'should return the correct status' do
66
- @reporter.score = score
67
- @reporter.status.must_equal success_status
68
- @reporter.status_message.must_equal 'Score: 99.0'
69
- end
70
- end
71
-
72
- describe 'when score is above the minimum' do
73
- let(:score) { 100.0 }
74
- it 'should return the correct status' do
75
- @reporter.score = score
76
- @reporter.status.must_equal success_status
77
- @reporter.status_message.must_equal 'Score: 100.0'
78
- end
79
- end
80
- end
81
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/configuration'
5
-
6
- describe RubyCritic::Configuration do
7
- describe '#root' do
8
- before do
9
- RubyCritic::Config.set
10
- @default = RubyCritic::Config.root
11
- end
12
-
13
- it 'has a default' do
14
- RubyCritic::Config.root.must_be_instance_of String
15
- end
16
-
17
- it 'can be set to a relative path' do
18
- RubyCritic::Config.root = 'foo'
19
- RubyCritic::Config.root.must_equal File.expand_path('foo')
20
- end
21
-
22
- it 'can be set to an absolute path' do
23
- RubyCritic::Config.root = '/foo'
24
- RubyCritic::Config.root.must_equal '/foo'
25
- end
26
-
27
- after do
28
- RubyCritic::Config.root = @default
29
- end
30
- end
31
- end
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/core/analysed_module'
5
- require 'rubycritic/core/smell'
6
-
7
- describe RubyCritic::AnalysedModule do
8
- describe 'attribute readers' do
9
- before do
10
- @name = 'Foo'
11
- @pathname = Pathname.new('foo.rb')
12
- @smells = []
13
- @churn = 1
14
- @complexity = 2
15
- @analysed_module = RubyCritic::AnalysedModule.new(
16
- name: @name,
17
- pathname: @pathname,
18
- smells: @smells,
19
- churn: @churn,
20
- complexity: @complexity
21
- )
22
- end
23
-
24
- it 'has a name reader' do
25
- @analysed_module.name.must_equal @name
26
- end
27
-
28
- it 'has a pathname reader' do
29
- @analysed_module.pathname.must_equal @pathname
30
- end
31
-
32
- it 'has a path reader' do
33
- @analysed_module.path.must_equal @pathname.to_s
34
- end
35
-
36
- it 'has a smells reader' do
37
- @analysed_module.smells.must_equal @smells
38
- end
39
-
40
- it 'has a churn reader' do
41
- @analysed_module.churn.must_equal @churn
42
- end
43
-
44
- it 'has a complexity reader' do
45
- @analysed_module.complexity.must_equal @complexity
46
- end
47
- end
48
-
49
- describe '#cost' do
50
- it 'returns the remediation cost of fixing the analysed_module' do
51
- smells = [SmellDouble.new(cost: 1), SmellDouble.new(cost: 2)]
52
- analysed_module = RubyCritic::AnalysedModule.new(smells: smells, complexity: 0)
53
- analysed_module.cost.must_equal 3
54
- end
55
- end
56
-
57
- describe '#complexity_per_method' do
58
- context 'when the file has no methods' do
59
- it 'returns a placeholder' do
60
- analysed_module = RubyCritic::AnalysedModule.new(complexity: 0, methods_count: 0)
61
- analysed_module.complexity_per_method.must_equal 'N/A'
62
- end
63
- end
64
-
65
- context 'when the file has at least one method' do
66
- it 'returns the average complexity per method' do
67
- analysed_module = RubyCritic::AnalysedModule.new(complexity: 10, methods_count: 2)
68
- analysed_module.complexity_per_method.must_equal 5
69
- end
70
- end
71
- end
72
-
73
- describe '#smells?' do
74
- it 'returns true if the analysed_module has at least one smell' do
75
- analysed_module = RubyCritic::AnalysedModule.new(smells: [SmellDouble.new])
76
- analysed_module.smells?.must_equal true
77
- end
78
- end
79
-
80
- describe '#smells_at_location' do
81
- it 'returns the smells of an analysed_module at a certain location' do
82
- location = RubyCritic::Location.new('./foo', '42')
83
- smells = [RubyCritic::Smell.new(locations: [location])]
84
- analysed_module = RubyCritic::AnalysedModule.new(smells: smells)
85
- analysed_module.smells_at_location(location).must_equal smells
86
- end
87
- end
88
- end
89
-
90
- class SmellDouble < OpenStruct; end
@@ -1,111 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'rubycritic/core/analysed_modules_collection'
5
-
6
- describe RubyCritic::AnalysedModulesCollection do
7
- subject { RubyCritic::AnalysedModulesCollection.new(paths) }
8
-
9
- describe '.new' do
10
- context 'with an empty path' do
11
- let(:paths) { '' }
12
-
13
- it 'returns an empty collection' do
14
- subject.count.must_equal 0
15
- end
16
- end
17
-
18
- context 'with a list of files' do
19
- let(:paths) { %w[test/samples/doesnt_exist.rb test/samples/unparsable.rb test/samples/empty.rb] }
20
-
21
- it 'registers one AnalysedModule element per existent file' do
22
- subject.count.must_equal 2
23
- subject.all? { |a| a.is_a?(RubyCritic::AnalysedModule) }.must_equal true
24
- end
25
- end
26
-
27
- context 'with a directory' do
28
- let(:paths) { 'test/samples/' }
29
-
30
- it 'recursively registers all files' do
31
- subject.count.must_equal Dir['test/samples/**/*.rb'].count
32
- end
33
- end
34
-
35
- context 'with redundant paths' do
36
- let(:paths) { %w[test/samples/flog test/samples/flog/complex.rb] }
37
-
38
- it 'returns a redundant collection' do
39
- subject.count.must_equal 3
40
- end
41
- end
42
- end
43
-
44
- describe '#score' do
45
- context 'with no modules' do
46
- let(:paths) { '' }
47
-
48
- it 'returns zero' do
49
- subject.score.must_equal 0.0
50
- end
51
- end
52
-
53
- context 'with not analysed modules' do
54
- let(:paths) { 'test/samples/flog' }
55
-
56
- it 'returns zero' do
57
- subject.score.must_equal 0.0
58
- end
59
- end
60
-
61
- context 'with analysed modules' do
62
- before do
63
- subject.each_with_index do |mod, i|
64
- mod.expects(:cost).returns costs[i]
65
- end
66
- end
67
-
68
- let(:paths) { %w[test/samples/flog test/samples/flay] }
69
-
70
- context 'with perfect modules' do
71
- let(:costs) { [0.0, 0.0, 0.0, 0.0] }
72
-
73
- it 'returns the maximum score' do
74
- subject.score.must_equal 100.0
75
- end
76
- end
77
-
78
- context 'with very bad modules' do
79
- let(:costs) { [16.0, 16.0, 16.0, 16.0] }
80
-
81
- it 'returns zero' do
82
- subject.score.must_equal 0.0
83
- end
84
- end
85
-
86
- context 'with horrible modules' do
87
- let(:costs) { [32.0, 32.0, 32.0, 32.0] }
88
-
89
- it 'returns zero' do
90
- subject.score.must_equal 0.0
91
- end
92
- end
93
-
94
- context 'with mixed modules' do
95
- let(:costs) { [32.0, 2.0, 0.0, 2.0] }
96
-
97
- it 'properly calculates the score' do
98
- subject.score.must_equal 43.75
99
- end
100
- end
101
-
102
- context 'with a module above the cost limit' do
103
- let(:costs) { [220.0, 2.0, 0.0, 2.0] }
104
-
105
- it 'reduces the impact in the result' do
106
- subject.score.must_equal 43.75
107
- end
108
- end
109
- end
110
- end
111
- end