hotspots 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in hotspots.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) <2011> <Chirantan Mitra>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,56 @@
1
+ Overview
2
+ --------
3
+
4
+ This program helps in identifying files with maximum churn in a git repository. The more the number of changes made to a file, the more likelyhood of the file being a source of bug. If the same file is modified many times for bug fixes, it indicates that the file needs some refactoring or redesign love.
5
+
6
+
7
+ Dependencies
8
+ ------------
9
+
10
+ This script depends on ruby. Obviously it also depends on git. Ruby and git executables should be in your execution path for the script to run.
11
+
12
+ Syntax
13
+ ------
14
+
15
+ ``` script
16
+ Usage: ruby hotspots [options]
17
+
18
+ Specific options:
19
+ -t, --time [TIME] Time in days to scan the repository for. Defaults to fifteen
20
+ -r, --repository [PATH] Path to the repository to scan. Defaults to current path
21
+ -f, --file-filter [REGEX] Regular expression to filtering file names. All files are allowed when not specified
22
+ -m, --message-filter [PIPE SEPARATED] Pipe separated values to filter files names against each commit message separated by pipe. All files are allowed when not specified
23
+ -c, --cutoff [CUTOFF] The minimum occurance to consider for a file to appear in the list. Defaults to zero
24
+ -v, --verbose Show verbose output
25
+ --version Show version information
26
+ -h, --help Show this message
27
+ ```
28
+
29
+ Examples
30
+ --------
31
+
32
+ This will give you all file names that contain '.c' and have been modified at least once in the past 15 days in the git repository pointed to by the current path.
33
+
34
+ ``` script
35
+ ruby hotspots --file-filter "/.c"
36
+ ```
37
+
38
+ *Note that the dot "." is escaped as it is a regular expression matcher.*
39
+
40
+ This will give you all file names that contain '.rb' and have been modified at least thrice in the past 5 days in git repository present in 'rails' directory.
41
+
42
+ ``` script
43
+ ruby hotspots --file-filter "/.rb" --path rails --cutoff 3 --time 5
44
+ ```
45
+
46
+ Running tests
47
+ -------------
48
+
49
+ ``` script
50
+ ruby test/hotspots_test.rb
51
+ ```
52
+
53
+ License
54
+ -------
55
+
56
+ This tool is released under the MIT license. Please refer to LICENSE for more details.
data/bin/hotspots ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'hotspots'
7
+
8
+ Repository::Main.new.execute!
data/hotspots.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ $:.push File.expand_path("../lib", __FILE__)
4
+
5
+ require "hotspots/version"
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "hotspots"
9
+ s.version = Hotspots::VERSION
10
+ s.authors = ["Chirantan Mitra"]
11
+ s.email = ["chirantan.mitra@gmail.com"]
12
+ s.homepage = "https://github.com/chiku/Hotspot"
13
+ s.summary = "Find all files that changed over the past in a git repository based on conditions"
14
+ s.description = <<-EOS
15
+ Find all files that changed over the past days for a git repository. If the same file is modified over
16
+ and over again, it may require re-design. Watch out for file changes that don't have a corresponding
17
+ test change.
18
+ EOS
19
+ s.rubyforge_project = "hotspots"
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ end
data/lib/hotspots.rb ADDED
@@ -0,0 +1,75 @@
1
+ require 'hotspots/version'
2
+ require 'hotspots/logger'
3
+ require 'hotspots/store'
4
+ require 'hotspots/options_parser'
5
+ require 'hotspots/repository'
6
+
7
+ module Repository
8
+ class Main
9
+ attr_reader :logger, :options, :repository, :verbose,
10
+ :exit_code, :exit_message,
11
+ :driver, :parser, :store
12
+
13
+ def initialize
14
+ @logger = Hotspots::Logger.new
15
+ @options = Hotspots::OptionsParser.new.parse(*ARGV)
16
+ @repository = options[:repository]
17
+ @verbose = options[:verbose]
18
+ @exit_code = options[:exit][:code]
19
+ @exit_message = options[:exit][:message]
20
+ end
21
+
22
+ def execute!
23
+ validate!
24
+ set
25
+ run
26
+ end
27
+
28
+ def validate!
29
+ validate_exit_code!
30
+ validate_git_repository!
31
+ end
32
+
33
+ def set
34
+ set_logger
35
+ set_path
36
+ assign
37
+ end
38
+
39
+ def run
40
+ puts store.to_s
41
+ end
42
+
43
+ private
44
+
45
+ def validate_exit_code!
46
+ if exit_code
47
+ puts exit_message
48
+ exit exit_code
49
+ end
50
+ end
51
+
52
+ def validate_git_repository!
53
+ if not File.directory?(repository) or not File.directory?(File.join(repository, '.git'))
54
+ puts "'#{repository}' doesn't seem to be a git repository!"
55
+ exit 10
56
+ end
57
+ end
58
+
59
+ def set_logger
60
+ if verbose
61
+ logger.set Hotspots::Logger::Console
62
+ end
63
+ end
64
+
65
+ def set_path
66
+ Dir.chdir(repository)
67
+ end
68
+
69
+ def assign
70
+ @driver = Hotspots::Repository::Driver::Git.new logger
71
+ @parser = Hotspots::Repository::Parser::Git.new driver, options.clone
72
+ @store = Hotspots::Store.new parser.files, options.clone
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,31 @@
1
+ module Hotspots
2
+ class Logger
3
+ class Console
4
+ def self.<<(message)
5
+ $stdout << message
6
+ end
7
+ end
8
+
9
+ class Null
10
+ def self.<<(message)
11
+ end
12
+ end
13
+
14
+ attr_reader :drain
15
+ def initialize
16
+ @drain = Null
17
+ end
18
+
19
+ def set(logger)
20
+ @drain = logger
21
+ end
22
+
23
+ def log(message)
24
+ @drain << format(message)
25
+ end
26
+
27
+ def format(message)
28
+ "\n<#{Time.now}> #{message}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,109 @@
1
+ require 'optparse'
2
+
3
+ require 'hotspots/version'
4
+
5
+ module Hotspots
6
+ class OptionsParser
7
+ def initialize
8
+ @options = {
9
+ :time => 15,
10
+ :repository => ".",
11
+ :file_filter => "",
12
+ :message_filters => [""],
13
+ :cutoff => 0,
14
+ :verbose => false,
15
+ :exit => { :code => nil, :message => "" }
16
+ }
17
+ end
18
+
19
+ def parse(*args)
20
+ parser = new_option_parser
21
+ begin
22
+ parser.parse args
23
+ rescue ::OptionParser::InvalidOption, ::OptionParser::InvalidArgument => ex
24
+ @options[:exit] = { :code => 1, :message => (ex.to_s << "\nUse -h for help\n") }
25
+ end
26
+ @options
27
+ end
28
+
29
+ private
30
+
31
+ def new_option_parser
32
+ ::OptionParser.new do |opts|
33
+ set_banner_on(opts)
34
+ set_version_on(opts)
35
+
36
+ handle_time_on(opts)
37
+ handle_path_on(opts)
38
+ handle_file_filter_on(opts)
39
+ handle_message_filter_on(opts)
40
+ handle_cutoff_on(opts)
41
+ handle_verbosity_on(opts)
42
+ handle_help_on(opts)
43
+ end
44
+ end
45
+
46
+ def set_version_on(opts)
47
+ opts.version = ::Hotspots::VERSION
48
+ end
49
+
50
+ def set_banner_on(opts)
51
+ opts.banner = "Tool to find most modified files over the past few days in a git repository. version #{::Hotspots::VERSION}"
52
+
53
+ opts.separator "Copyright (C) 2011 Chirantan Mitra"
54
+ opts.separator ""
55
+ opts.separator "Usage: ruby hotspots [options]"
56
+ opts.separator ""
57
+ opts.separator "Specific options:"
58
+ end
59
+
60
+ def handle_time_on(opts)
61
+ opts.on("-t", "--time [TIME]", OptionParser::DecimalInteger,
62
+ "Time in days to scan the repository for. Defaults to fifteen") do |o|
63
+ @options[:time] = o.to_i
64
+ end
65
+ end
66
+
67
+ def handle_path_on(opts)
68
+ opts.on("-r", "--repository [PATH]", String,
69
+ "Path to the repository to scan. Defaults to current path") do |o|
70
+ @options[:repository] = o.to_s
71
+ end
72
+ end
73
+
74
+ def handle_file_filter_on(opts)
75
+ opts.on("-f", "--file-filter [REGEX]", String,
76
+ "Regular expression to filtering file names. All files are allowed when not specified") do |o|
77
+ @options[:file_filter] = o.to_s
78
+ end
79
+ end
80
+
81
+ def handle_message_filter_on(opts)
82
+ opts.on("-m", "--message-filter [PIPE SEPARATED]", String,
83
+ "Pipe separated values to filter files names against each commit message separated by pipe. All files are allowed when not specified") do |o|
84
+ @options[:message_filters] = o.to_s.split("|")
85
+ end
86
+ end
87
+
88
+ def handle_cutoff_on(opts)
89
+ opts.on("-c", "--cutoff [CUTOFF]", OptionParser::DecimalInteger,
90
+ "The minimum occurance to consider for a file to appear in the list. Defaults to zero") do |o|
91
+ @options[:cutoff] = o.to_i
92
+ end
93
+ end
94
+
95
+ def handle_verbosity_on(opts)
96
+ opts.on("-v", "--verbose",
97
+ "Show verbose output") do
98
+ @options[:verbose] = true
99
+ end
100
+ end
101
+
102
+ def handle_help_on(opts)
103
+ opts.on_tail("-h", "--help",
104
+ "Show this message") do
105
+ @options[:exit] = { :code => 0, :message => opts.to_s }
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,2 @@
1
+ require 'hotspots/repository/driver'
2
+ require 'hotspots/repository/parser'
@@ -0,0 +1 @@
1
+ require 'hotspots/repository/driver/git'
@@ -0,0 +1,29 @@
1
+ module Hotspots
2
+ module Repository
3
+ module Driver
4
+ class Git
5
+ attr_reader :logger
6
+
7
+ def initialize(logger)
8
+ @logger = logger
9
+ end
10
+
11
+ def pretty_log(options)
12
+ command = %Q(git log --pretty="%H" --since #{options[:since_days]}.days.ago --grep "#{options[:message_filter]}")
13
+ .tap {|raw| logger.log "<Input> #{raw}"}
14
+ %x(#{command})
15
+ .tap {|raw| logger.log raw}
16
+ .gsub("\r", "")
17
+ end
18
+
19
+ def show_one_line_names(options)
20
+ command = %Q(git show --oneline --name-only #{options[:commit_hash]})
21
+ .tap {|raw| logger.log "<Input> #{raw}"}
22
+ %x(#{command})
23
+ .tap {|raw| logger.log "<Output> #{raw}"}
24
+ .gsub("\r", "")
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1 @@
1
+ require 'hotspots/repository/parser/git'
@@ -0,0 +1,25 @@
1
+ module Hotspots
2
+ module Repository
3
+ module Parser
4
+ class Git
5
+ def initialize(driver, options)
6
+ @driver = driver
7
+ @time = options[:time]
8
+ @message_filters = options[:message_filters]
9
+ end
10
+
11
+ def files
12
+ filtered_commit_hashes.map do |commit_hash|
13
+ @driver.show_one_line_names(:commit_hash => commit_hash).split("\n")[1..-1]
14
+ end.flatten
15
+ end
16
+
17
+ def filtered_commit_hashes
18
+ @message_filters.map do |filter|
19
+ @driver.pretty_log(:since_days => @time, :message_filter => filter).split("\n")
20
+ end.flatten.uniq
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ module Hotspots
2
+ class Store
3
+ def initialize(lines, options = {})
4
+ @lines = lines
5
+ @store = Hash.new(0)
6
+ @cutoff = options[:cutoff] || 0
7
+ @filter = options[:file_filter] || ""
8
+
9
+ @lines.map { |line| line.strip.downcase }
10
+ .select{ |line| not line.empty? and line =~ Regexp.new(@filter) }
11
+ .each { |line| @store[line] += 1 }
12
+ end
13
+
14
+ def on(line)
15
+ @store[line]
16
+ end
17
+
18
+ def to_s
19
+ sorted_array.select { |key, value| value >= @cutoff }
20
+ .reduce("") { |acc, (key, value)| acc << "#{key},#{value}\n" }
21
+ end
22
+
23
+ private
24
+
25
+ def sorted_array
26
+ @store.sort do |(key1, value1), (key2, value2)|
27
+ value2 <=> value1
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Hotspots
2
+ VERSION = "0.0.8"
3
+ end
@@ -0,0 +1,152 @@
1
+ require 'hotspots/logger'
2
+ require 'hotspots/options_parser'
3
+
4
+ module Hotspots
5
+ describe "OptionsParser" do
6
+ before do
7
+ @parser = OptionsParser.new
8
+ end
9
+
10
+ describe "defaults" do
11
+ it "repository to current path" do
12
+ @parser.parse[:repository].must_equal "."
13
+ end
14
+
15
+ it "time to 15" do
16
+ @parser.parse[:time].must_equal 15
17
+ end
18
+
19
+ it "file filter to empty string" do
20
+ @parser.parse[:file_filter].must_equal ""
21
+ end
22
+
23
+ it "message filters to array with an empty string" do
24
+ @parser.parse[:message_filters].must_equal [""]
25
+ end
26
+
27
+ it "cutoff to 0" do
28
+ @parser.parse[:cutoff].must_equal 0
29
+ end
30
+
31
+ it "verbose to nil" do
32
+ @parser.parse[:verbose].must_equal false
33
+ end
34
+
35
+ it "exit code to nil" do
36
+ @parser.parse[:exit][:code].must_equal nil
37
+ end
38
+
39
+ it "exit message to empty string" do
40
+ @parser.parse[:exit][:message].must_equal ""
41
+ end
42
+ end
43
+
44
+ ["--repository", "--repo", "-r"].each do |option|
45
+ describe option do
46
+ it "sets the specified value repository" do
47
+ @parser.parse(option, "rails")[:repository].must_equal "rails"
48
+ end
49
+
50
+ it "sets empty repository when missing" do
51
+ @parser.parse(option)[:repository].must_equal ""
52
+ end
53
+ end
54
+ end
55
+
56
+ ["--time", "--ti", "-t"].each do |option|
57
+ describe option do
58
+ it "sets the specified time to consider" do
59
+ @parser.parse(option, "8")[:time].must_equal 8
60
+ end
61
+
62
+ it "sets zero time when missing" do
63
+ @parser.parse(option)[:time].must_equal 0
64
+ end
65
+ end
66
+ end
67
+
68
+ ["--cutoff", "--cut", "-c"].each do |option|
69
+ describe option do
70
+ it "sets the specified cutoff" do
71
+ @parser.parse(option, "5")[:cutoff].must_equal 5
72
+ end
73
+
74
+ it "sets zero cutoff when missing" do
75
+ @parser.parse(option)[:cutoff].must_equal 0
76
+ end
77
+ end
78
+ end
79
+
80
+ ["--file-filter", "--file", "-f"].each do |option|
81
+ describe option do
82
+ it "sets the specified file-filter" do
83
+ @parser.parse(option, "rb")[:file_filter].must_equal "rb"
84
+ end
85
+
86
+ it "sets empty file-filter when missing" do
87
+ @parser.parse(option)[:file_filter].must_equal ""
88
+ end
89
+ end
90
+ end
91
+
92
+ ["--message-filter", "--message", "-m"].each do |option|
93
+ describe option do
94
+ it "sets the specified message filters" do
95
+ @parser.parse(option, "cleanup|defect")[:message_filters].must_equal ["cleanup", "defect"]
96
+ end
97
+
98
+ it "sets empty message-filter when missing" do
99
+ @parser.parse(option)[:message_filters].must_equal []
100
+ end
101
+ end
102
+ end
103
+
104
+ ["--verbose", "-v"].each do |option|
105
+ describe option do
106
+ it "sets the console logger" do
107
+ @parser.parse(option)[:verbose].must_equal true
108
+ end
109
+ end
110
+ end
111
+
112
+ ["--help", "-h"].each do |option|
113
+ describe option do
114
+ it "sets exit code to zero" do
115
+ @parser.parse(option)[:exit][:code].must_equal 0
116
+ end
117
+
118
+ it "sets an exit message" do
119
+ @parser.parse(option)[:exit][:message].wont_be_empty
120
+ end
121
+ end
122
+ end
123
+
124
+ describe "on a invalid option" do
125
+ before do
126
+ @options = @parser.parse("--invalid-option")
127
+ end
128
+
129
+ it "sets an exit code" do
130
+ @options[:exit][:code].must_equal 1
131
+ end
132
+
133
+ it "sets an exit message" do
134
+ @options[:exit][:message].wont_be_empty
135
+ end
136
+ end
137
+
138
+ describe "on a invalid argument" do
139
+ before do
140
+ @options = @parser.parse("--repo", "")
141
+ end
142
+
143
+ it "sets an exit code" do
144
+ @options[:exit][:code].must_equal 1
145
+ end
146
+
147
+ it "sets an exit message" do
148
+ @options[:exit][:message].wont_be_empty
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,74 @@
1
+ require 'hotspots/repository/parser/git'
2
+
3
+ module Hotspots::Repository
4
+ describe "Parser::Git test" do
5
+ it "fetches a commit hash based on filter and time" do
6
+ mock_git_driver = MiniTest::Mock.new
7
+ options = {:time => 10, :message_filters => ["Foo"]}
8
+ git_parser = Parser::Git.new mock_git_driver, options
9
+
10
+ mock_git_driver.expect(:pretty_log, "SHA1\nSHA2", [:since_days => options[:time], :message_filter => "Foo"])
11
+
12
+ git_parser.filtered_commit_hashes.must_equal(["SHA1", "SHA2"])
13
+
14
+ assert mock_git_driver.verify
15
+ end
16
+
17
+ it "fetches multiple commit hashes" do
18
+ options = {:time => 10, :message_filters => ["Foo", "Bar"]}
19
+ git_parser = Parser::Git.new StubGitDriver, options
20
+
21
+ git_parser.filtered_commit_hashes.must_equal(["SHA1", "SHA2", "SHA3"])
22
+ end
23
+
24
+ it "finds all affected files for a commit message" do
25
+ mock_git_driver = MiniTest::Mock.new
26
+ options = {:time => 10, :message_filters => ["Foo"]}
27
+ git_parser = Parser::Git.new mock_git_driver, options
28
+
29
+ mock_git_driver.expect(:pretty_log, "SHA1", [:since_days => options[:time], :message_filter => "Foo"])
30
+ mock_git_driver.expect(:show_one_line_names, "SHA1 CommitMessage\nfile1\nfile2", [:commit_hash => "SHA1"])
31
+
32
+ git_parser.files.must_equal(["file1", "file2"])
33
+
34
+ assert mock_git_driver.verify
35
+ end
36
+
37
+ it "finds all affected files for multiple commit messages" do
38
+ options = {:time => 10, :message_filters => ["Foo", "Bar"]}
39
+ git_parser = Parser::Git.new StubGitDriver, options
40
+
41
+ git_parser.files.must_equal(["file1", "file2", "file2", "file3", "file5", "file4"])
42
+ end
43
+ end
44
+
45
+ describe "git driver stub" do
46
+ it "pretty log is sane" do
47
+ StubGitDriver.pretty_log(:time => 10, :message_filter => "Foo").must_equal "SHA1\nSHA2"
48
+ StubGitDriver.pretty_log(:time => 10, :message_filter => "Bar").must_equal "SHA2\nSHA3"
49
+ end
50
+
51
+ it "show one line names is sane" do
52
+ StubGitDriver.show_one_line_names(:commit_hash => "SHA2").must_equal "SHA1 commit message\nfile2\nfile3\nfile5"
53
+ end
54
+ end
55
+
56
+ class StubGitDriver
57
+ @pretty_log_enum = ["SHA1\nSHA2", "SHA2\nSHA3"].cycle
58
+ @commits = {
59
+ "SHA1" => "SHA1 commit message\nfile1\nfile2",
60
+ "SHA2" => "SHA1 commit message\nfile2\nfile3\nfile5",
61
+ "SHA3" => "SHA1 commit message\nfile4",
62
+ }
63
+
64
+ class << self
65
+ def pretty_log(options)
66
+ @pretty_log_enum.next
67
+ end
68
+
69
+ def show_one_line_names(options)
70
+ @commits[options[:commit_hash]]
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,115 @@
1
+ require 'hotspots/store'
2
+
3
+ module Hotspots
4
+ describe "Store test" do
5
+ it "counts occurances of a line present once" do
6
+ lines = [
7
+ "abc.txt",
8
+ "efg.txt"
9
+ ]
10
+ store = Store.new(lines)
11
+ store.on("abc.txt").must_equal 1
12
+ store.on("efg.txt").must_equal 1
13
+ end
14
+
15
+ it "counts occurances of a line present multiple times" do
16
+ lines = [
17
+ "abc.txt",
18
+ "abc.txt",
19
+ "efg.txt"
20
+ ]
21
+ store = Store.new(lines)
22
+ store.on("abc.txt").must_equal 2
23
+ end
24
+
25
+ it "identifies zero occurances" do
26
+ lines = [
27
+ "abc.txt",
28
+ "efg.txt"
29
+ ]
30
+ store = Store.new(lines)
31
+ store.on("absent.txt").must_equal 0
32
+ end
33
+
34
+ it "disregards file case" do
35
+ lines = [
36
+ "ABC.TXT"
37
+ ]
38
+ store = Store.new(lines)
39
+ store.on("abc.txt").must_equal 1
40
+ end
41
+
42
+ it "neglects empty lines" do
43
+ lines = [
44
+ " "
45
+ ]
46
+ store = Store.new(lines)
47
+ store.on(" ").must_equal 0
48
+ end
49
+
50
+ it "neglects spaces at the extremes of the line" do
51
+ lines = [
52
+ " abc.txt "
53
+ ]
54
+ store = Store.new(lines)
55
+ store.on("abc.txt").must_equal 1
56
+ end
57
+
58
+ it "neglects linefeeds at the extremes of the line" do
59
+ lines = [
60
+ "\n\n abc.txt \n\r",
61
+ "\n\n abc.txt \r\n"
62
+ ]
63
+ store = Store.new(lines)
64
+ store.on("abc.txt").must_equal 2
65
+ end
66
+
67
+ it "has a string representation" do
68
+ lines = [
69
+ "abc.txt",
70
+ "efg.txt"
71
+ ]
72
+ store = Store.new(lines)
73
+ store.to_s.must_equal "abc.txt,1\nefg.txt,1\n"
74
+ end
75
+
76
+ it "string representation has maximum occuring string at the top" do
77
+ lines = [
78
+ "abc.txt",
79
+ "abc.txt",
80
+ "efg.txt",
81
+ "efg.txt",
82
+ "efg.txt"
83
+ ]
84
+ store = Store.new(lines)
85
+ store.to_s.must_equal "efg.txt,3\nabc.txt,2\n"
86
+ end
87
+
88
+ it "doesn't display setting below cut-off" do
89
+ lines = [
90
+ "abc.txt",
91
+ "abc.txt",
92
+ "efg.txt",
93
+ "efg.txt",
94
+ "efg.txt"
95
+ ]
96
+ store = Store.new(lines, :cutoff => 3)
97
+ store.to_s.must_equal "efg.txt,3\n"
98
+ end
99
+
100
+ it "doesn't display lines that don't match criteria" do
101
+ lines = [
102
+ "abc.txt",
103
+ "abc.txt",
104
+ "abc.txt",
105
+ "dont.exist",
106
+ "abc.log",
107
+ "efg.txt",
108
+ "efg.txt",
109
+ "missing.txt"
110
+ ]
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"
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'minitest/autorun'
4
+ require 'minitest/spec'
5
+
6
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
7
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
8
+
9
+ Dir[File.join(File.expand_path(File.dirname(__FILE__)), "hotspots", "**", "*_test.rb")].each do |file|
10
+ require file
11
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hotspots
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.8
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Chirantan Mitra
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-25 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! 'Find all files that changed over the past days for a git repository.
15
+ If the same file is modified over
16
+
17
+ and over again, it may require re-design. Watch out for file changes that don''t
18
+ have a corresponding
19
+
20
+ test change.
21
+
22
+ '
23
+ email:
24
+ - chirantan.mitra@gmail.com
25
+ executables:
26
+ - hotspots
27
+ extensions: []
28
+ extra_rdoc_files: []
29
+ files:
30
+ - Gemfile
31
+ - LICENSE
32
+ - README.markdown
33
+ - bin/hotspots
34
+ - hotspots.gemspec
35
+ - lib/hotspots.rb
36
+ - lib/hotspots/logger.rb
37
+ - lib/hotspots/options_parser.rb
38
+ - lib/hotspots/repository.rb
39
+ - lib/hotspots/repository/driver.rb
40
+ - lib/hotspots/repository/driver/git.rb
41
+ - lib/hotspots/repository/parser.rb
42
+ - lib/hotspots/repository/parser/git.rb
43
+ - lib/hotspots/store.rb
44
+ - lib/hotspots/version.rb
45
+ - test/hotspots/options_parser_test.rb
46
+ - test/hotspots/repository/parser/git_test.rb
47
+ - test/hotspots/store_test.rb
48
+ - test/hotspots_test.rb
49
+ homepage: https://github.com/chiku/Hotspot
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project: hotspots
69
+ rubygems_version: 1.8.10
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Find all files that changed over the past in a git repository based on conditions
73
+ test_files: []