MFOL-bugspots 0.2.1
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/.gitignore +5 -0
- data/Gemfile +6 -0
- data/README.md +89 -0
- data/Rakefile +1 -0
- data/bin/bugspots +81 -0
- data/bin/git-bugspots +7 -0
- data/bugspots.gemspec +24 -0
- data/lib/bugspots.rb +2 -0
- data/lib/bugspots/scanner.rb +63 -0
- data/lib/bugspots/version.rb +3 -0
- metadata +84 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8954c0d8725adbc49e46c0b41f04baf63ee79c3b
|
4
|
+
data.tar.gz: d1ae42eff16eaf6999feb4197ce435b4ef8d66ec
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7f8afae0ea150a481fd8d97d19983cda64d55a55b9b22f9ece1f6e7e7bee8a8efef7fdf3eb3adc72405e7943eedd848266357d8c0d8904acb727daf5a6f35e42
|
7
|
+
data.tar.gz: 369da80bdcf592809f7868fc7a2e054e3a23c11e764347a54f355df606aa5cac9aea30523093eb1fa1398b4a436622a28efad872bf5cb1244813ec443d81150c
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Bugspots - Bug Prediction Heuristic
|
2
|
+
|
3
|
+
An implementation of the simple bug prediction heuristic outlined by the Google Engineering team: [Bug Prediction at Google](http://google-engtools.blogspot.com/2011/12/bug-prediction-at-google.html)
|
4
|
+
|
5
|
+
> Well, we actually have a great, authoritative record of where code has been requiring fixes: our bug tracker and our source control commit log! The research indicates that predicting bugs from the source history works very well, so we decided to deploy it at Google.
|
6
|
+
|
7
|
+
Point bugspots at any git repo and it will identify the hotspots for you.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
```bash
|
12
|
+
$> gem install bugspots
|
13
|
+
$> bugspots /path/to/repo
|
14
|
+
$> git bugspots (in root of current git project, --help for options)
|
15
|
+
```
|
16
|
+
|
17
|
+
## Results
|
18
|
+
|
19
|
+
```bash
|
20
|
+
$> cd /your/git/repo
|
21
|
+
$> git bugspots -d 500
|
22
|
+
|
23
|
+
.. example output ..
|
24
|
+
|
25
|
+
Scanning /git/eventmachine repo
|
26
|
+
Found 31 bugfix commits, with 23 hotspots:
|
27
|
+
|
28
|
+
Fixes:
|
29
|
+
- Revert "Write maximum of 16KB of data to an SSL connection per tick (fixes #233)" for #273
|
30
|
+
- Do not close attached sockets (fixes #200)
|
31
|
+
- Write maximum of 16KB of data to an SSL connection per tick (fixes #233)
|
32
|
+
- Merge branch 'master' into close_schedule_fix
|
33
|
+
- Remove dependency on readbytes.rb for ruby 1.9 (fixes #167, #234)
|
34
|
+
- Fix compilation on MSVC2008 (fixes #253)
|
35
|
+
- EM::Deferrable#(callback|errback|timeout) now return self so you can chain them (closes #177)
|
36
|
+
- Make EventMachine::Connection#get_peername and #get_sockname valid for IPv6 (closes #132)
|
37
|
+
- reconnect DNS socket if closed
|
38
|
+
- Use String#bytesize in EM::Connection#send_datagram for 1.9 (closes #153)
|
39
|
+
- Fix an issue that would cause the EM process to block when the loopbreak pipe filled up (closes #158)
|
40
|
+
- namespace std is already included, so just call min(). fixes vc6 issue with min macro
|
41
|
+
- Use close() instead of closesocket() to prevent FD leaks on windows.
|
42
|
+
- Stop advertising non-available authentication mechanisms, allow multi-line authentication - fixes compatibility with javamail
|
43
|
+
- typo fixes and undef fstat for ruby on Windows
|
44
|
+
- Deprecate now aged info, as it's fixed
|
45
|
+
- Some fixes for Solaris and Nexenta (opensolaris kernel + linux userland)
|
46
|
+
- Some fixes for solaris
|
47
|
+
- Minor fixes for rbx compatibility
|
48
|
+
- Reduce the size of the RunEpollOnce stack frame by 800kb. This fixes the long-standing epoll+threads issue (#84)
|
49
|
+
- Fixed aggregated event handling for kqueue and notify, fixed path for ifconfig.
|
50
|
+
- More win32 fixes
|
51
|
+
- Added test for reactor_thread? and fixed up EM.schedule for pre-reactor schedules
|
52
|
+
- Merge branch 'master' of git@github.com:eventmachine/eventmachine
|
53
|
+
- Use read instead of recv in ConnectionDescriptor::Read (fixes EM.attach issues with pipes)
|
54
|
+
- Use false to indicated a cancelled timer instead of using an empty proc. Reduces mem usage in certain situations.
|
55
|
+
- Inotify fixes: file_delete only fires after fds have been closed, use syscall hackery for older linux distributions (*cough* debian)
|
56
|
+
- Clean up deferrable.rb: fixed rdoc, alias method wrappers, remove unnecessary forwardable
|
57
|
+
- More solaris build fixes.
|
58
|
+
- More solaris build issues fixed
|
59
|
+
- fixed a small bug with basic auth (cherry-pick conflict merge from mmmurf (closes #92))
|
60
|
+
|
61
|
+
Hotspots:
|
62
|
+
0.9723 - ext/ed.cpp
|
63
|
+
0.3311 - ext/ed.h
|
64
|
+
0.3271 - ext/em.cpp
|
65
|
+
0.3034 - lib/eventmachine.rb
|
66
|
+
0.2433 - lib/em/protocols/postgres3.rb
|
67
|
+
0.2403 - ext/project.h
|
68
|
+
0.0431 - lib/em/deferrable.rb
|
69
|
+
0.029 - ext/cmain.cpp
|
70
|
+
0.0278 - ext/rubymain.cpp
|
71
|
+
0.0277 - ext/eventmachine.h
|
72
|
+
0.0241 - lib/em/resolver.rb
|
73
|
+
0.0241 - tests/test_resolver.rb
|
74
|
+
0.0225 - lib/em/connection.rb
|
75
|
+
0.0013 - lib/em/protocols/smtpserver.rb
|
76
|
+
0.0003 - ext/extconf.rb
|
77
|
+
0.0002 - tests/test_basic.rb
|
78
|
+
0.0001 - ext/em.h
|
79
|
+
0.0001 - ext/cplusplus.cpp
|
80
|
+
0.0001 - ext/fastfilereader/extconf.rb
|
81
|
+
0.0 - lib/em/filewatcher.rb
|
82
|
+
0.0 - tests/test_file_watch.rb
|
83
|
+
0.0 - ext/fastfilereader/mapper.cpp
|
84
|
+
0.0 - lib/protocols/httpclient.rb
|
85
|
+
```
|
86
|
+
|
87
|
+
### License
|
88
|
+
|
89
|
+
(MIT License) - Copyright (c) 2011 Ilya Grigorik
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/bugspots
ADDED
@@ -0,0 +1,81 @@
|
|
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 'bugspots'
|
7
|
+
require 'optparse'
|
8
|
+
require 'rainbow'
|
9
|
+
require 'rainbow/ext/string'
|
10
|
+
|
11
|
+
ARGV << '--help' if ARGV.empty?
|
12
|
+
|
13
|
+
options = {}
|
14
|
+
OptionParser.new do |opts|
|
15
|
+
opts.banner = "Usage: bugspots /path/to/git/repo"
|
16
|
+
|
17
|
+
# Option: Set Branch
|
18
|
+
opts.on('-b', '--branch [name]', 'branch to crawl') do |b|
|
19
|
+
options[:branch] = b.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
# Option: Set Bugfix Indicator
|
23
|
+
opts.on('-f', '--file [file extension]', 'The type of file to scan') do |f|
|
24
|
+
options[:fileExt] = f.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
# Option: Set Depth
|
28
|
+
opts.on('-d', '--depth [depth]', 'depth of log crawl (integer)') do |d|
|
29
|
+
options[:depth] = d.to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
# Option: Set Bugfix Indicator
|
33
|
+
opts.on('-w', '--words ["w1,w2"]', 'bugfix indicator word list, ie: "fixes,closed"') do |words|
|
34
|
+
options[:regex] = Regexp.new(words.split(',').join('|'))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Option: Set Bugfix Indicator
|
38
|
+
opts.on('-r', '--regex [regex]', Regexp, 'bugfix indicator regex, ie: "fix(es|ed)?" or "/fixes #(\d+)/i"') do |regex|
|
39
|
+
options[:regex] = regex
|
40
|
+
end
|
41
|
+
|
42
|
+
# Option: Set Timestamp Display
|
43
|
+
opts.on('--display-timestamps', 'show timestamps of each identified fix commit') do |dt|
|
44
|
+
options[:display_timestamps] = true
|
45
|
+
end
|
46
|
+
end.parse!
|
47
|
+
|
48
|
+
# Set a reasonable default of depth
|
49
|
+
options[:depth] ||= 500
|
50
|
+
|
51
|
+
# Set master as the default branch
|
52
|
+
options[:branch] ||= "master"
|
53
|
+
|
54
|
+
# Set the default file extionsion to include to be php
|
55
|
+
options[:fileExt] ||= ".php"
|
56
|
+
|
57
|
+
puts "Scanning #{ARGV[0]} repo with #{options[:fileExt]} files".foreground(:green)
|
58
|
+
|
59
|
+
begin
|
60
|
+
fixes, spots = Bugspots.scan(ARGV[0], options[:branch], options[:fileExt], options[:depth], options[:regex])
|
61
|
+
|
62
|
+
puts "\tFound #{fixes.size} bugfix commits, with #{spots.size} hotspots:".foreground(:yellow)
|
63
|
+
puts
|
64
|
+
|
65
|
+
puts "\tFixes:".foreground(:green).underline
|
66
|
+
fixes.each do |fix|
|
67
|
+
message = "\t\t- "
|
68
|
+
message << "#{fix.date} " if options[:display_timestamps]
|
69
|
+
message << "#{fix.message}"
|
70
|
+
puts message.foreground(:yellow)
|
71
|
+
end
|
72
|
+
|
73
|
+
puts "\n"
|
74
|
+
puts "\tHotspots:".foreground(:green).underline
|
75
|
+
spots.each do |spot|
|
76
|
+
puts "\t\t#{spot.score}".foreground(:red) + " - #{spot.file}".foreground(:yellow)
|
77
|
+
end
|
78
|
+
|
79
|
+
rescue Rugged::RepositoryError
|
80
|
+
puts "Invalid Git repository - please run from or specify the full path to the root of the project.".foreground(:red)
|
81
|
+
end
|
data/bin/git-bugspots
ADDED
data/bugspots.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "bugspots/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.license = "MIT"
|
7
|
+
s.name = "MFOL-bugspots"
|
8
|
+
s.version = Bugspots::VERSION
|
9
|
+
s.authors = ["Xiaoming Cai"]
|
10
|
+
s.email = ["ming.cxm@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/cxm0000/MFOL-bugspots"
|
12
|
+
s.summary = "Implementation of simple bug prediction hotspot heuristic"
|
13
|
+
s.description = s.summary
|
14
|
+
|
15
|
+
s.rubyforge_project = "MFOL-bugspots"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency "rugged", ">= 0.21.0"
|
23
|
+
s.add_dependency "rainbow"
|
24
|
+
end
|
data/lib/bugspots.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require "rugged"
|
2
|
+
|
3
|
+
module Bugspots
|
4
|
+
Fix = Struct.new(:message, :date, :files)
|
5
|
+
Spot = Struct.new(:file, :score)
|
6
|
+
|
7
|
+
# Filter all results before this threshold
|
8
|
+
ScoreThreshold = 0.01
|
9
|
+
|
10
|
+
def self.scan(repo, branch = "master", fileExt = ".php", depth = 500, regex = nil)
|
11
|
+
|
12
|
+
regex ||= /\b(fix(es|ed)?|close(s|d)?)\b/i
|
13
|
+
fixes = []
|
14
|
+
|
15
|
+
repo = Rugged::Repository.new(repo)
|
16
|
+
unless repo.branches.each_name(:local).sort.find { |b| b == branch }
|
17
|
+
raise ArgumentError, "no such branch in the repo: #{branch}"
|
18
|
+
end
|
19
|
+
|
20
|
+
walker = Rugged::Walker.new(repo)
|
21
|
+
walker.sorting(Rugged::SORT_TOPO | Rugged::SORT_REVERSE)
|
22
|
+
walker.push(repo.branches[branch].target)
|
23
|
+
walker.each do |commit|
|
24
|
+
if commit.message =~ regex
|
25
|
+
files = commit.diff(commit.parents.first).deltas.collect do |d|
|
26
|
+
d.old_file[:path]
|
27
|
+
end
|
28
|
+
fixes << Fix.new(commit.message.split("\n").first, commit.time, files)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
hotspots = Hash.new(0)
|
33
|
+
fixes.each do |fix|
|
34
|
+
fix.files.each do |file|
|
35
|
+
|
36
|
+
if File.extname(file).eql? fileExt
|
37
|
+
|
38
|
+
# The timestamp used in the equation is normalized from 0 to 1, where
|
39
|
+
# 0 is the earliest point in the code base, and 1 is now (where now is
|
40
|
+
# when the algorithm was run). Note that the score changes over time
|
41
|
+
# with this algorithm due to the moving normalization; it's not meant
|
42
|
+
# to provide some objective score, only provide a means of comparison
|
43
|
+
# between one file and another at any one point in time
|
44
|
+
t = 1 - ((Time.now - fix.date).to_f / (Time.now - fixes.first.date))
|
45
|
+
hotspots[file] += 1/(1+Math.exp((-12*t)+12))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# filter out the files with very low scores
|
51
|
+
hotspots.each do |file, score|
|
52
|
+
if score <= ScoreThreshold
|
53
|
+
hotspots.delete(file)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
spots = hotspots.sort_by {|k,v| v}.reverse.collect do |spot|
|
58
|
+
Spot.new(spot.first, sprintf('%.4f', spot.last))
|
59
|
+
end
|
60
|
+
|
61
|
+
return fixes, spots
|
62
|
+
end
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: MFOL-bugspots
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Xiaoming Cai
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rugged
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.21.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.21.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rainbow
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Implementation of simple bug prediction hotspot heuristic
|
42
|
+
email:
|
43
|
+
- ming.cxm@gmail.com
|
44
|
+
executables:
|
45
|
+
- bugspots
|
46
|
+
- git-bugspots
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- .gitignore
|
51
|
+
- Gemfile
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- bin/bugspots
|
55
|
+
- bin/git-bugspots
|
56
|
+
- bugspots.gemspec
|
57
|
+
- lib/bugspots.rb
|
58
|
+
- lib/bugspots/scanner.rb
|
59
|
+
- lib/bugspots/version.rb
|
60
|
+
homepage: https://github.com/cxm0000/MFOL-bugspots
|
61
|
+
licenses:
|
62
|
+
- MIT
|
63
|
+
metadata: {}
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project: MFOL-bugspots
|
80
|
+
rubygems_version: 2.0.14
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Implementation of simple bug prediction hotspot heuristic
|
84
|
+
test_files: []
|