hotspots 1.1.0 → 1.2.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -1
- data/LICENSE +1 -1
- data/README.md +7 -15
- data/TODO.md +0 -2
- data/bin/hotspots +9 -3
- data/lib/hotspots/configuration.rb +36 -0
- data/lib/hotspots/{option_based_exit.rb → exit.rb} +4 -4
- data/lib/hotspots/options_parser.rb +21 -39
- data/lib/hotspots/repository/git.rb +17 -0
- data/lib/hotspots/repository/git_command.rb +38 -0
- data/lib/hotspots/repository/git_driver.rb +35 -0
- data/lib/hotspots/repository/git_parser.rb +25 -0
- data/lib/hotspots/repository.rb +5 -3
- data/lib/hotspots/store.rb +2 -2
- data/lib/hotspots/version.rb +1 -1
- data/lib/hotspots.rb +32 -53
- data/test/hotspots/configuration_test.rb +65 -0
- data/test/hotspots/options_parser_test.rb +91 -119
- data/test/hotspots/repository/git_command_test.rb +39 -0
- data/test/hotspots/repository/git_parser_test.rb +88 -0
- data/test/hotspots/store_test.rb +13 -13
- data/test/minitest_helper.rb +8 -5
- metadata +57 -73
- data/lib/hotspots/compatibility.rb +0 -4
- data/lib/hotspots/logger.rb +0 -58
- data/lib/hotspots/repository/command/git.rb +0 -40
- data/lib/hotspots/repository/command.rb +0 -1
- data/lib/hotspots/repository/driver/git.rb +0 -27
- data/lib/hotspots/repository/driver.rb +0 -1
- data/lib/hotspots/repository/parser/git.rb +0 -27
- data/lib/hotspots/repository/parser.rb +0 -1
- data/test/hotspots/repository/command/git_test.rb +0 -39
- data/test/hotspots/repository/parser/git_test.rb +0 -84
@@ -1,163 +1,135 @@
|
|
1
|
-
require
|
1
|
+
require "logger"
|
2
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "..", "minitest_helper")
|
2
3
|
|
3
4
|
class Hotspots
|
4
5
|
describe "OptionsParser" do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
it "defaults message filters to array with an empty string" do
|
23
|
-
@parser.parse[:message_filters].must_equal [""]
|
24
|
-
end
|
25
|
-
|
26
|
-
it "defaults cutoff to 0" do
|
27
|
-
@parser.parse[:cutoff].must_equal 0
|
6
|
+
let(:info_log_level) { ::Logger::INFO }
|
7
|
+
let(:error_log_level) { ::Logger::ERROR }
|
8
|
+
let(:logger) { ::Logger.new(STDOUT) }
|
9
|
+
let(:configuration) { Configuration.new(:logger => logger, :info_log_level => info_log_level, :error_log_level => error_log_level) }
|
10
|
+
let(:parser) { OptionsParser.new(:configuration => configuration) }
|
11
|
+
|
12
|
+
describe "#parse" do
|
13
|
+
["--repository", "-r"].each do |option|
|
14
|
+
describe option do
|
15
|
+
it "sets the specified value repository" do
|
16
|
+
expect(parser.parse(option, "rails").repository).must_equal "rails"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "sets empty repository when missing" do
|
20
|
+
expect(parser.parse(option).repository).must_equal ""
|
21
|
+
end
|
22
|
+
end
|
28
23
|
end
|
29
24
|
|
30
|
-
|
31
|
-
|
32
|
-
|
25
|
+
["--time", "-t"].each do |option|
|
26
|
+
describe option do
|
27
|
+
it "sets the specified time to consider" do
|
28
|
+
expect(parser.parse(option, "8").time).must_equal 8
|
29
|
+
end
|
33
30
|
|
34
|
-
|
35
|
-
|
31
|
+
it "sets zero time when missing" do
|
32
|
+
expect(parser.parse(option).time).must_equal 0
|
33
|
+
end
|
34
|
+
end
|
36
35
|
end
|
37
36
|
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
["--cutoff", "-c"].each do |option|
|
38
|
+
describe option do
|
39
|
+
it "sets the specified cutoff" do
|
40
|
+
expect(parser.parse(option, "5").cutoff).must_equal 5
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
43
|
+
it "sets zero cutoff when missing" do
|
44
|
+
expect(parser.parse(option).cutoff).must_equal 0
|
45
|
+
end
|
46
|
+
end
|
44
47
|
end
|
45
|
-
end
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
["--file-filter", "-f"].each do |option|
|
50
|
+
describe option do
|
51
|
+
it "sets the specified file-filter" do
|
52
|
+
expect(parser.parse(option, "rb").file_filter).must_equal "rb"
|
53
|
+
end
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
+
it "sets empty file-filter when missing" do
|
56
|
+
expect(parser.parse(option).file_filter).must_equal ""
|
57
|
+
end
|
55
58
|
end
|
56
59
|
end
|
57
|
-
end
|
58
60
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
["--message-filter", "-m"].each do |option|
|
62
|
+
describe option do
|
63
|
+
it "sets the specified message filters" do
|
64
|
+
expect(parser.parse(option, "cleanup|defect").message_filters).must_equal ["cleanup", "defect"]
|
65
|
+
end
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
+
it "sets empty message-filter when missing" do
|
68
|
+
expect(parser.parse(option).message_filters).must_equal []
|
69
|
+
end
|
67
70
|
end
|
68
71
|
end
|
69
|
-
end
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
it "sets zero cutoff when missing" do
|
78
|
-
@parser.parse(option)[:cutoff].must_equal 0
|
73
|
+
["--verbose", "-v"].each do |option|
|
74
|
+
describe option do
|
75
|
+
it "sets the log level to info" do
|
76
|
+
expect(parser.parse(option).log_level).must_equal info_log_level
|
77
|
+
end
|
79
78
|
end
|
80
79
|
end
|
81
|
-
end
|
82
80
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
81
|
+
["--version"].each do |option|
|
82
|
+
describe option do
|
83
|
+
it "sets exit code to zero" do
|
84
|
+
expect(parser.parse(option).exit_strategy.code).must_equal 0
|
85
|
+
end
|
88
86
|
|
89
|
-
|
90
|
-
|
87
|
+
it "sets a version message" do
|
88
|
+
expect(parser.parse(option).exit_strategy.message).must_be :include?, ::Hotspots::VERSION
|
89
|
+
end
|
91
90
|
end
|
92
91
|
end
|
93
|
-
end
|
94
92
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
93
|
+
["--help", "-h"].each do |option|
|
94
|
+
describe option do
|
95
|
+
it "sets exit code to zero" do
|
96
|
+
expect(parser.parse(option).exit_strategy.code).must_equal 0
|
97
|
+
end
|
100
98
|
|
101
|
-
|
102
|
-
|
99
|
+
it "sets an exit message" do
|
100
|
+
expect(parser.parse(option).exit_strategy.message).wont_be_empty
|
101
|
+
end
|
103
102
|
end
|
104
103
|
end
|
105
|
-
end
|
106
104
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
@parser.parse(option)[:verbose].must_equal true
|
111
|
-
end
|
105
|
+
it "doesn't mutate options" do
|
106
|
+
parser.parse("--help")
|
107
|
+
expect(configuration.exit_strategy.code).must_be :nil?
|
112
108
|
end
|
113
|
-
end
|
114
109
|
|
115
|
-
|
116
|
-
|
117
|
-
it "sets colours" do
|
118
|
-
@parser.parse(option)[:colour].must_equal true
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
110
|
+
describe "when parsing an invalid option" do
|
111
|
+
let(:options) { parser.parse("--invalid-option") }
|
122
112
|
|
123
|
-
|
124
|
-
|
125
|
-
it "sets exit code to zero" do
|
126
|
-
@parser.parse(option)[:exit_strategy].code.must_equal 0
|
113
|
+
it "sets an exit code" do
|
114
|
+
expect(options.exit_strategy.code).must_equal 1
|
127
115
|
end
|
128
116
|
|
129
117
|
it "sets an exit message" do
|
130
|
-
|
118
|
+
expect(options.exit_strategy.message).wont_be_empty
|
131
119
|
end
|
132
120
|
end
|
133
|
-
end
|
134
|
-
|
135
|
-
describe "on an invalid option" do
|
136
|
-
before do
|
137
|
-
@options = @parser.parse("--invalid-option")
|
138
|
-
end
|
139
|
-
|
140
|
-
it "sets an exit code" do
|
141
|
-
@options[:exit_strategy].code.must_equal 1
|
142
|
-
end
|
143
121
|
|
144
|
-
|
145
|
-
|
146
|
-
end
|
147
|
-
end
|
122
|
+
describe "when parsing an invalid argument" do
|
123
|
+
let(:options) { parser.parse("--repository", "") }
|
148
124
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
153
|
-
|
154
|
-
it "sets an exit code" do
|
155
|
-
@options[:exit_strategy].code.must_equal 1
|
156
|
-
end
|
125
|
+
it "sets an exit code" do
|
126
|
+
expect(options.exit_strategy.code).must_equal 1
|
127
|
+
end
|
157
128
|
|
158
|
-
|
159
|
-
|
129
|
+
it "sets an exit message" do
|
130
|
+
expect(options.exit_strategy.message).wont_be_empty
|
131
|
+
end
|
160
132
|
end
|
161
133
|
end
|
162
134
|
end
|
163
|
-
end
|
135
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "..", "..", "minitest_helper")
|
2
|
+
|
3
|
+
module Hotspots::Repository
|
4
|
+
describe "GitCommand" do
|
5
|
+
describe "Log" do
|
6
|
+
describe "#to_s" do
|
7
|
+
describe "when message filter exists" do
|
8
|
+
it "includes a grep clause" do
|
9
|
+
log_command = GitCommand::Log.new :since_days => 20, :message_filter => "Foo|Bar"
|
10
|
+
expect(log_command.to_s).must_equal 'git log --pretty="%H" --since="20 days ago" --grep "Foo|Bar"'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "when message filter doesn't exist" do
|
15
|
+
it "doesn't have a grep clause" do
|
16
|
+
log_command = GitCommand::Log.new :since_days => 20
|
17
|
+
expect(log_command.to_s).must_equal 'git log --pretty="%H" --since="20 days ago"'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "when message filter is set to an empty string" do
|
22
|
+
it "grep clause is ignored" do
|
23
|
+
log_command = GitCommand::Log.new :since_days => 20, :message_filter => ""
|
24
|
+
expect(log_command.to_s).must_equal 'git log --pretty="%H" --since="20 days ago"'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "Show" do
|
31
|
+
describe "#to_s" do
|
32
|
+
it "constructs a git show with one-line format" do
|
33
|
+
show_command = GitCommand::Show.new :commit_hash => "abc123"
|
34
|
+
expect(show_command.to_s).must_equal 'git show --oneline --name-only abc123'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "..", "..", "minitest_helper")
|
2
|
+
|
3
|
+
module Hotspots::Repository
|
4
|
+
class GitDriverStub
|
5
|
+
def initialize
|
6
|
+
@pretty_log = ["SHA1\nSHA2", "SHA2\nSHA3"]
|
7
|
+
@commits = {
|
8
|
+
"SHA1" => "SHA1 commit message\nfile1\nfile2",
|
9
|
+
"SHA2" => "SHA1 commit message\nfile2\nfile3\nfile5",
|
10
|
+
"SHA3" => "SHA1 commit message\nfile4",
|
11
|
+
}
|
12
|
+
@pretty_log_cycle = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def pretty_log(options)
|
16
|
+
@pretty_log[@pretty_log_cycle].tap do
|
17
|
+
@pretty_log_cycle = (@pretty_log_cycle + 1) % 2
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def show_one_line_names(options)
|
22
|
+
@commits[options[:commit_hash]]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe GitDriverStub do
|
27
|
+
subject { GitDriverStub.new }
|
28
|
+
|
29
|
+
describe "#pretty_log" do
|
30
|
+
it "is sane" do
|
31
|
+
expect(subject.pretty_log(:time => 10, :message_filter => "Foo")).must_equal "SHA1\nSHA2"
|
32
|
+
expect(subject.pretty_log(:time => 10, :message_filter => "Bar")).must_equal "SHA2\nSHA3"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#show_one_line_names" do
|
37
|
+
it "is sane" do
|
38
|
+
expect(subject.show_one_line_names(:commit_hash => "SHA2")).
|
39
|
+
must_equal "SHA1 commit message\nfile2\nfile3\nfile5"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "GitParser" do
|
45
|
+
describe "#filtered_commit_hashes" do
|
46
|
+
it "fetches a commit hash based on filter and time" do
|
47
|
+
mock_git_driver = Minitest::Mock.new
|
48
|
+
options = {:time => 10, :message_filters => ["Foo"]}
|
49
|
+
git_parser = GitParser.new mock_git_driver, options
|
50
|
+
|
51
|
+
mock_git_driver.expect(:pretty_log, "SHA1\nSHA2", [{:since_days => options[:time], :message_filter => "Foo"}])
|
52
|
+
|
53
|
+
expect(git_parser.filtered_commit_hashes).must_equal(["SHA1", "SHA2"])
|
54
|
+
|
55
|
+
expect(mock_git_driver.verify).must_equal true
|
56
|
+
end
|
57
|
+
|
58
|
+
it "fetches multiple commit hashes" do
|
59
|
+
options = {:time => 10, :message_filters => ["Foo", "Bar"]}
|
60
|
+
git_parser = GitParser.new GitDriverStub.new, options
|
61
|
+
|
62
|
+
expect(git_parser.filtered_commit_hashes).must_equal(["SHA1", "SHA2", "SHA3"])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#files" do
|
67
|
+
it "finds all affected files for a commit message" do
|
68
|
+
mock_git_driver = Minitest::Mock.new
|
69
|
+
options = {:time => 10, :message_filters => ["Foo"]}
|
70
|
+
git_parser = GitParser.new mock_git_driver, options
|
71
|
+
|
72
|
+
mock_git_driver.expect(:pretty_log, "SHA1", [{:since_days => options[:time], :message_filter => "Foo"}])
|
73
|
+
mock_git_driver.expect(:show_one_line_names, "SHA1 CommitMessage\nfile1\nfile2", [{:commit_hash => "SHA1"}])
|
74
|
+
|
75
|
+
expect(git_parser.files).must_equal(["file1", "file2"])
|
76
|
+
|
77
|
+
expect(mock_git_driver.verify).must_equal true
|
78
|
+
end
|
79
|
+
|
80
|
+
it "finds all affected files for multiple commit messages" do
|
81
|
+
options = {:time => 10, :message_filters => ["Foo", "Bar"]}
|
82
|
+
git_parser = GitParser.new GitDriverStub.new, options
|
83
|
+
|
84
|
+
expect(git_parser.files).must_equal(["file1", "file2", "file2", "file3", "file5", "file4"])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/test/hotspots/store_test.rb
CHANGED
@@ -8,8 +8,8 @@ class Hotspots
|
|
8
8
|
"efg.txt"
|
9
9
|
]
|
10
10
|
store = Store.new(lines)
|
11
|
-
store.on("abc.txt").must_equal 1
|
12
|
-
store.on("efg.txt").must_equal 1
|
11
|
+
expect(store.on("abc.txt")).must_equal 1
|
12
|
+
expect(store.on("efg.txt")).must_equal 1
|
13
13
|
end
|
14
14
|
|
15
15
|
it "counts occurances of a line present multiple times" do
|
@@ -19,7 +19,7 @@ class Hotspots
|
|
19
19
|
"efg.txt"
|
20
20
|
]
|
21
21
|
store = Store.new(lines)
|
22
|
-
store.on("abc.txt").must_equal 2
|
22
|
+
expect(store.on("abc.txt")).must_equal 2
|
23
23
|
end
|
24
24
|
|
25
25
|
it "identifies zero occurances" do
|
@@ -28,7 +28,7 @@ class Hotspots
|
|
28
28
|
"efg.txt"
|
29
29
|
]
|
30
30
|
store = Store.new(lines)
|
31
|
-
store.on("absent.txt").must_equal 0
|
31
|
+
expect(store.on("absent.txt")).must_equal 0
|
32
32
|
end
|
33
33
|
|
34
34
|
it "neglects empty lines" do
|
@@ -36,7 +36,7 @@ class Hotspots
|
|
36
36
|
" "
|
37
37
|
]
|
38
38
|
store = Store.new(lines)
|
39
|
-
store.on(" ").must_equal 0
|
39
|
+
expect(store.on(" ")).must_equal 0
|
40
40
|
end
|
41
41
|
|
42
42
|
it "neglects spaces at the extremes of the line" do
|
@@ -44,7 +44,7 @@ class Hotspots
|
|
44
44
|
" abc.txt "
|
45
45
|
]
|
46
46
|
store = Store.new(lines)
|
47
|
-
store.on("abc.txt").must_equal 1
|
47
|
+
expect(store.on("abc.txt")).must_equal 1
|
48
48
|
end
|
49
49
|
|
50
50
|
it "neglects linefeeds at the extremes of the line" do
|
@@ -53,7 +53,7 @@ class Hotspots
|
|
53
53
|
"\n\n abc.txt \r\n"
|
54
54
|
]
|
55
55
|
store = Store.new(lines)
|
56
|
-
store.on("abc.txt").must_equal 2
|
56
|
+
expect(store.on("abc.txt")).must_equal 2
|
57
57
|
end
|
58
58
|
|
59
59
|
it "has a string representation" do
|
@@ -62,7 +62,7 @@ class Hotspots
|
|
62
62
|
"efg.txt"
|
63
63
|
]
|
64
64
|
store = Store.new(lines)
|
65
|
-
store.to_s.must_equal "abc.txt,1\nefg.txt,1\n"
|
65
|
+
expect(store.to_s).must_equal "abc.txt,1\nefg.txt,1\n"
|
66
66
|
end
|
67
67
|
|
68
68
|
it "has a case-sensitive string representation" do
|
@@ -70,7 +70,7 @@ class Hotspots
|
|
70
70
|
"aBc.tXt"
|
71
71
|
]
|
72
72
|
store = Store.new(lines)
|
73
|
-
store.to_s.must_equal "aBc.tXt,1\n"
|
73
|
+
expect(store.to_s).must_equal "aBc.tXt,1\n"
|
74
74
|
end
|
75
75
|
|
76
76
|
it "string representation has maximum occuring string at the top" do
|
@@ -82,7 +82,7 @@ class Hotspots
|
|
82
82
|
"efg.txt"
|
83
83
|
]
|
84
84
|
store = Store.new(lines)
|
85
|
-
store.to_s.must_equal "efg.txt,3\nabc.txt,2\n"
|
85
|
+
expect(store.to_s).must_equal "efg.txt,3\nabc.txt,2\n"
|
86
86
|
end
|
87
87
|
|
88
88
|
it "doesn't display setting below cut-off" do
|
@@ -94,7 +94,7 @@ class Hotspots
|
|
94
94
|
"efg.txt"
|
95
95
|
]
|
96
96
|
store = Store.new(lines, :cutoff => 3)
|
97
|
-
store.to_s.must_equal "efg.txt,3\n"
|
97
|
+
expect(store.to_s).must_equal "efg.txt,3\n"
|
98
98
|
end
|
99
99
|
|
100
100
|
it "doesn't display lines that don't match criteria" do
|
@@ -109,7 +109,7 @@ class Hotspots
|
|
109
109
|
"missing.txt"
|
110
110
|
]
|
111
111
|
store = Store.new(lines, :file_filter => "abc|efg")
|
112
|
-
store.to_s.must_equal "abc.txt,3\nefg.txt,2\nabc.log,1\n"
|
112
|
+
expect(store.to_s).must_equal "abc.txt,3\nefg.txt,2\nabc.log,1\n"
|
113
113
|
end
|
114
114
|
end
|
115
|
-
end
|
115
|
+
end
|
data/test/minitest_helper.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
|
-
lib = File.expand_path(
|
1
|
+
lib = File.expand_path("../../lib/", __FILE__)
|
2
2
|
$:.unshift lib unless $:.include?(lib)
|
3
3
|
|
4
|
+
require "ansi/code"
|
5
|
+
|
4
6
|
if ENV["coverage"] == "true"
|
5
7
|
begin
|
6
|
-
require
|
8
|
+
require "simplecov"
|
7
9
|
SimpleCov.start do
|
8
10
|
add_filter "/test/"
|
9
11
|
end
|
10
12
|
rescue LoadError
|
11
|
-
puts "
|
13
|
+
$stderr.puts ::ANSI::Code.red("Please install simplecov to generate a coverage report!")
|
14
|
+
exit 1
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
@@ -19,5 +22,5 @@ end
|
|
19
22
|
|
20
23
|
require_relative "../lib/hotspots"
|
21
24
|
|
22
|
-
|
23
|
-
require
|
25
|
+
gem "minitest"
|
26
|
+
require "minitest/autorun"
|