rubycritic 3.4.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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