defect-density-heatmap 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/{bin/heatmap → heatmap} +0 -0
- metadata +9 -28
- data/.gitignore +0 -4
- data/Gemfile +0 -4
- data/Guardfile +0 -8
- data/Rakefile +0 -7
- data/defect-density-heatmap.gemspec +0 -25
- data/lib/command_line_runner.rb +0 -34
- data/lib/defect-density-heatmap.rb +0 -5
- data/lib/heatmap.rb +0 -5
- data/lib/heatmap/version.rb +0 -3
- data/lib/heatmap_formatter.rb +0 -71
- data/readme.markdown +0 -36
- data/screenshot.png +0 -0
- data/spec/example-data.csv +0 -56
- data/spec/heatmap_formatter_spec.rb +0 -59
- data/spec/spec_helper.rb +0 -33
data/{bin/heatmap → heatmap}
RENAMED
File without changes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: defect-density-heatmap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-12 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70143080302440 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70143080302440
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: guard
|
27
|
-
requirement: &
|
27
|
+
requirement: &70143080302020 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,31 +32,15 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70143080302020
|
36
36
|
description: Generates a tag cloud based on project file churn.
|
37
37
|
email:
|
38
38
|
- bcarlso@gmail.com
|
39
|
-
executables:
|
40
|
-
- heatmap
|
39
|
+
executables: []
|
41
40
|
extensions: []
|
42
41
|
extra_rdoc_files: []
|
43
42
|
files:
|
44
|
-
-
|
45
|
-
- Gemfile
|
46
|
-
- Guardfile
|
47
|
-
- Rakefile
|
48
|
-
- bin/heatmap
|
49
|
-
- defect-density-heatmap.gemspec
|
50
|
-
- lib/command_line_runner.rb
|
51
|
-
- lib/defect-density-heatmap.rb
|
52
|
-
- lib/heatmap.rb
|
53
|
-
- lib/heatmap/version.rb
|
54
|
-
- lib/heatmap_formatter.rb
|
55
|
-
- readme.markdown
|
56
|
-
- screenshot.png
|
57
|
-
- spec/example-data.csv
|
58
|
-
- spec/heatmap_formatter_spec.rb
|
59
|
-
- spec/spec_helper.rb
|
43
|
+
- heatmap
|
60
44
|
homepage: http://github.com/bcarlso/defect-density-heatmap
|
61
45
|
licenses: []
|
62
46
|
post_install_message:
|
@@ -81,7 +65,4 @@ rubygems_version: 1.8.6
|
|
81
65
|
signing_key:
|
82
66
|
specification_version: 3
|
83
67
|
summary: Generates a 'heatmap' from files changed in the SCM system.
|
84
|
-
test_files:
|
85
|
-
- spec/example-data.csv
|
86
|
-
- spec/heatmap_formatter_spec.rb
|
87
|
-
- spec/spec_helper.rb
|
68
|
+
test_files: []
|
data/.gitignore
DELETED
data/Gemfile
DELETED
data/Guardfile
DELETED
data/Rakefile
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
require "heatmap/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |s|
|
6
|
-
s.name = "defect-density-heatmap"
|
7
|
-
s.version = Heatmap::VERSION
|
8
|
-
s.authors = ["Brandon Carlson"]
|
9
|
-
s.email = ["bcarlso@gmail.com"]
|
10
|
-
s.homepage = "http://github.com/bcarlso/defect-density-heatmap"
|
11
|
-
s.summary = %q{Generates a 'heatmap' from files changed in the SCM system.}
|
12
|
-
s.description = %q{Generates a tag cloud based on project file churn.}
|
13
|
-
|
14
|
-
s.rubyforge_project = "defect-density-heatmap"
|
15
|
-
|
16
|
-
s.files = `git ls-files`.split("\n")
|
17
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
-
s.require_paths = ["lib"]
|
20
|
-
|
21
|
-
# specify any dependencies here; for example:
|
22
|
-
s.add_development_dependency "rspec"
|
23
|
-
s.add_development_dependency "guard"
|
24
|
-
# s.add_runtime_dependency "rest-client"
|
25
|
-
end
|
data/lib/command_line_runner.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require 'heatmap_formatter'
|
3
|
-
|
4
|
-
class CommandLineRunner
|
5
|
-
def self.run
|
6
|
-
options = {
|
7
|
-
:input_stream => STDIN,
|
8
|
-
:output_stream => STDOUT,
|
9
|
-
:regular_expression => "(?<total>(\\d+)),(?<defects>(\\d+)),(?<filename>(.+))"
|
10
|
-
}
|
11
|
-
|
12
|
-
OptionParser.new do | opts |
|
13
|
-
opts.banner = "Usage: heatmap [options]"
|
14
|
-
|
15
|
-
opts.on("-r", "--record-format pattern", "Regular expression with named captures describing the record") do | regex |
|
16
|
-
options[:regular_expression] = regex
|
17
|
-
end
|
18
|
-
|
19
|
-
opts.on("-f", "--file inputfile", "File containing commit data") do | filename, f2 |
|
20
|
-
options[:input_stream] = File.new(filename, 'r')
|
21
|
-
end
|
22
|
-
|
23
|
-
opts.on("-o", "--outfile outputfile", "File to write output to") do | filename |
|
24
|
-
options[:output_stream] = File.new(filename, 'w')
|
25
|
-
end
|
26
|
-
end.parse!
|
27
|
-
|
28
|
-
formatter = HeatmapFormatter.new(options[:regular_expression],
|
29
|
-
options[:input_stream],
|
30
|
-
options[:output_stream])
|
31
|
-
|
32
|
-
formatter.render
|
33
|
-
end
|
34
|
-
end
|
data/lib/heatmap.rb
DELETED
data/lib/heatmap/version.rb
DELETED
data/lib/heatmap_formatter.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
class HeatmapFormatter
|
2
|
-
def initialize(line_format, input_stream, output_stream)
|
3
|
-
@output_stream = output_stream
|
4
|
-
|
5
|
-
format = Regexp.new(line_format)
|
6
|
-
|
7
|
-
items_to_render = parse(input_stream, format)
|
8
|
-
@entries = items_to_render.sort do | first, second |
|
9
|
-
File.basename(first[:filename]) <=> File.basename(second[:filename])
|
10
|
-
end
|
11
|
-
|
12
|
-
@floor, ceiling = (@entries.collect { | e | e[:total].to_i }).minmax
|
13
|
-
@range = (ceiling - @floor)
|
14
|
-
end
|
15
|
-
|
16
|
-
def render
|
17
|
-
tags = @entries.inject("") do | result, e |
|
18
|
-
result + format_entry(e[:filename], e[:total].to_i, e[:defects])
|
19
|
-
end
|
20
|
-
|
21
|
-
html = <<-EOS
|
22
|
-
<html>
|
23
|
-
<head>
|
24
|
-
<title>SCM Heatmap</title>
|
25
|
-
<style type='text/css'>
|
26
|
-
body { font-family: sans-serif; }
|
27
|
-
ol li { display: inline; margin: 2px; }
|
28
|
-
</style>
|
29
|
-
</head>
|
30
|
-
<body>
|
31
|
-
<ol>
|
32
|
-
#{tags}
|
33
|
-
</ol>
|
34
|
-
</body>
|
35
|
-
</html>
|
36
|
-
EOS
|
37
|
-
|
38
|
-
@output_stream << html
|
39
|
-
end
|
40
|
-
|
41
|
-
def format_entry(filename, changes, defects)
|
42
|
-
style = compute_style(changes, defects)
|
43
|
-
title = "#{File.dirname(filename)} Changes: #{changes} Defects: #{defects}"
|
44
|
-
"<li style='#{style}' title='#{title}'>#{File.basename(filename)} </li>"
|
45
|
-
end
|
46
|
-
|
47
|
-
def size_for(number)
|
48
|
-
((((number - @floor).to_f / @range) * 10).to_i * 3) + 6
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def parse(stream, format)
|
54
|
-
stream.collect() do | line |
|
55
|
-
format.match(line)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def calculate_filename_color(total_changes, total_defects)
|
60
|
-
percent_of_defects = total_defects.to_f / total_changes.to_f
|
61
|
-
(percent_of_defects * 255).to_i
|
62
|
-
end
|
63
|
-
|
64
|
-
def compute_style(total_changes, total_defects)
|
65
|
-
"font-size: #{size_for(total_changes)}; color: rgb(#{calculate_filename_color(total_changes,total_defects)}, 0, 0);"
|
66
|
-
end
|
67
|
-
|
68
|
-
def determine_relative_size_for(value)
|
69
|
-
@size_calculator.size_for(value)
|
70
|
-
end
|
71
|
-
end
|
data/readme.markdown
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
Defect Density Heatmap - Visualizing Code Churn
|
2
|
-
-----------------------------------------------
|
3
|
-
|
4
|
-
This is the defect density heatmap and it is used to identify files in your codebase that change often as a result of working on features and fixing defects. Before using the heatmap generator some background information is needed.
|
5
|
-
|
6
|
-
In our day to day development efforts we often commit code to our repository and give it a commit message. What if we add an additional piece of information to our commit message, such as a discriminator, identifying whether this change was related to a feature or defect? It can be anything, like starting the commit message with an 'F' or 'D'. If you're in corporate IT, your auditors would love it if you put ticket numbers in your commits (such as FEA-1234, DEF-1234)
|
7
|
-
|
8
|
-
Take a simple example:
|
9
|
-
|
10
|
-
# ListController.java, ColumnSorter.js, ListView.jsp, ListRepository.java
|
11
|
-
git commit -m 'f Adding clickable column headings for sorting'
|
12
|
-
|
13
|
-
# ListController.java, ColumnSorter.js
|
14
|
-
git commit -m 'd Prevented NPE when invalid product code filter entered'
|
15
|
-
|
16
|
-
# ListController.java, ListView.jsp
|
17
|
-
git commit -m 'd Fix spelling error'
|
18
|
-
|
19
|
-
This simple practice can give us some interesting insights into our code. Doing a bit of log crunching we can end up with a list of total changes and total defects a given file is associated with:
|
20
|
-
|
21
|
-
# change-data.csv
|
22
|
-
1,0,src/main/java/com/example/ListRepository.java
|
23
|
-
3,2,src/main/java/com/example/ListController.java
|
24
|
-
2,1,src/main/webapp/ListView.jsp
|
25
|
-
2,1,src/main/webapp/js/ColumnSorter.js
|
26
|
-
|
27
|
-
In this example, the first column is the total number of changes and the second column is the number of defects this file was involved with.
|
28
|
-
|
29
|
-
Using the heatmap generator, we can create a tag cloud giving us a visualization of our code over time. The heatmap can be invoked in the usual ways:
|
30
|
-
|
31
|
-
cat change-data.csv | heatmap > results.html
|
32
|
-
heatmap -f change-data.csv -o results.html
|
33
|
-
|
34
|
-
The command renders a tag cloud formatted as HTML. The larger the text, the more that source file has changed. The text color changes from black to red based on the ratio of defects to changes. The more defects, the more red you see. Big + Red = Bad
|
35
|
-
|
36
|
-
![Heatmap Screenshot](./screenshot.png)
|
data/screenshot.png
DELETED
Binary file
|
data/spec/example-data.csv
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
21,15,src/main/java/com/example/File1.java
|
2
|
-
8,2,src/main/java/com/example/File2.java
|
3
|
-
3,0,src/main/java/com/example/File3.java
|
4
|
-
23,10,src/main/java/com/example/File4.java
|
5
|
-
22,17,src/main/java/com/example/File5.java
|
6
|
-
21,13,src/main/java/com/example/File6.java
|
7
|
-
20,12,src/main/java/com/example/File7.java
|
8
|
-
16,8,src/main/java/com/example/File8.java
|
9
|
-
20,12,src/main/java/com/example/File9.java
|
10
|
-
15,9,src/main/java/com/example/File10.java
|
11
|
-
3,1,src/main/java/com/example/File11.java
|
12
|
-
3,3,src/main/java/com/example/File12.java
|
13
|
-
11,1,src/main/java/com/example/File13.java
|
14
|
-
9,7,src/main/java/com/example/File14.java
|
15
|
-
9,6,src/main/java/com/example/File15.java
|
16
|
-
15,1,src/main/java/com/example/File16.java
|
17
|
-
4,4,src/main/java/com/example/File17.java
|
18
|
-
2,0,src/main/java/com/example/File18.java
|
19
|
-
20,20,src/main/java/com/example/File19.java
|
20
|
-
4,0,src/main/java/com/example/File20.java
|
21
|
-
1,0,src/main/java/com/example/File21.java
|
22
|
-
11,0,src/main/java/com/example/File22.java
|
23
|
-
21,6,src/main/java/com/example/File23.java
|
24
|
-
8,5,src/main/java/com/example/File24.java
|
25
|
-
20,2,src/main/java/com/example/File25.java
|
26
|
-
11,8,src/main/java/com/example/File26.java
|
27
|
-
1,0,src/main/java/com/example/File27.java
|
28
|
-
22,14,src/main/java/com/example/File28.java
|
29
|
-
22,0,src/main/java/com/example/File29.java
|
30
|
-
17,5,src/main/java/com/example/File30.java
|
31
|
-
17,8,src/main/java/com/example/File31.java
|
32
|
-
24,7,src/main/java/com/example/File32.java
|
33
|
-
8,4,src/main/java/com/example/File33.java
|
34
|
-
9,0,src/main/java/com/example/File34.java
|
35
|
-
1,0,src/main/java/com/example/File35.java
|
36
|
-
14,1,src/main/java/com/example/File36.java
|
37
|
-
12,3,src/main/java/com/example/File37.java
|
38
|
-
24,5,src/main/java/com/example/File38.java
|
39
|
-
11,2,src/main/java/com/example/File39.java
|
40
|
-
8,4,src/main/java/com/example/File40.java
|
41
|
-
10,10,src/main/java/com/example/File41.java
|
42
|
-
19,4,src/main/java/com/example/File42.java
|
43
|
-
8,1,src/main/java/com/example/File43.java
|
44
|
-
8,2,src/main/java/com/example/File44.java
|
45
|
-
11,7,src/main/java/com/example/File45.java
|
46
|
-
21,4,src/main/java/com/example/File46.java
|
47
|
-
22,19,src/main/java/com/example/File47.java
|
48
|
-
12,8,src/main/java/com/example/File48.java
|
49
|
-
5,0,src/main/java/com/example/File49.java
|
50
|
-
19,6,src/main/java/com/example/File50.java
|
51
|
-
16,12,src/main/java/com/example/File51.java
|
52
|
-
3,1,src/main/java/com/example/File52.java
|
53
|
-
1,0,src/main/java/com/example/File53.java
|
54
|
-
1,1,src/main/java/com/example/File54.java
|
55
|
-
16,2,src/main/java/com/example/File55.java
|
56
|
-
5,3,src/main/java/com/example/File56.java
|
@@ -1,59 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
|
3
|
-
describe HeatmapFormatter do
|
4
|
-
before(:each) do
|
5
|
-
example_data = <<-EOS
|
6
|
-
3:0 src/main/resources/com/example/SomeFile.java: DE3981,DE4386
|
7
|
-
12:6 src/main/java/com/example/AnotherFile.java: US11743
|
8
|
-
6:6 src/main/java/com/example/YetAnotherFile.java: DE3317,DE4077,US12070
|
9
|
-
EOS
|
10
|
-
|
11
|
-
@input_stream = StringIO.new(example_data)
|
12
|
-
@output_stream = StringIO.new
|
13
|
-
|
14
|
-
@formatter = HeatmapFormatter.new("(?<total>(\\d+)):(?<defects>(\\d+)) (?<filename>(.+)):.+",
|
15
|
-
@input_stream,
|
16
|
-
@output_stream)
|
17
|
-
end
|
18
|
-
|
19
|
-
it "Orders the tag cloud based on filename" do
|
20
|
-
@formatter.render
|
21
|
-
@output_stream.string.should match(".*AnotherFile.*SomeFile.*YetAnotherFile")
|
22
|
-
end
|
23
|
-
|
24
|
-
|
25
|
-
it "Renders each file as a tag" do
|
26
|
-
results = @formatter.format_entry 'path/to/SomeFile.java', 3, 0
|
27
|
-
results.should match("<li style='.+'>SomeFile.java </li>")
|
28
|
-
end
|
29
|
-
|
30
|
-
it "Adds file details to the title attribute for display when hovered over" do
|
31
|
-
results = @formatter.format_entry 'src/main/java/com/example/SomeFile.java', 3, 2
|
32
|
-
results.should have_title_containing("src/main/java/com/example Changes: 3 Defects: 2")
|
33
|
-
end
|
34
|
-
|
35
|
-
it "Adjusts the font size of each tag relative to the number of changes in the file" do
|
36
|
-
file_with( :changes => 3 ).should have_font_size(6)
|
37
|
-
file_with( :changes => 6 ).should have_font_size(15)
|
38
|
-
file_with( :changes => 12 ).should have_font_size(36)
|
39
|
-
end
|
40
|
-
|
41
|
-
it "Adjusts the font color of each tag based on the number of changes that were defects" do
|
42
|
-
file_with( :changes => 3 ).should have_color('0, 0, 0')
|
43
|
-
|
44
|
-
file_with( :changes => 3,
|
45
|
-
:defects => 3 ).should have_color('255, 0, 0')
|
46
|
-
|
47
|
-
file_with( :changes => 7,
|
48
|
-
:defects => 5 ).should have_color('182, 0, 0')
|
49
|
-
end
|
50
|
-
|
51
|
-
it "Generates a range of font sizes based on a min/max" do
|
52
|
-
@formatter.size_for(12).should == 36
|
53
|
-
@formatter.size_for(11).should == 30
|
54
|
-
@formatter.size_for(8).should == 21
|
55
|
-
@formatter.size_for(7).should == 18
|
56
|
-
@formatter.size_for(4).should == 9
|
57
|
-
@formatter.size_for(3).should == 6
|
58
|
-
end
|
59
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
-
|
4
|
-
require 'rspec'
|
5
|
-
require 'heatmap_formatter'
|
6
|
-
require 'command_line_runner'
|
7
|
-
|
8
|
-
RSpec.configure do | config |
|
9
|
-
config.color_enabled = true
|
10
|
-
end
|
11
|
-
|
12
|
-
RSpec::Matchers.define :have_font_size do | expected |
|
13
|
-
match do | actual |
|
14
|
-
actual =~ Regexp.new(".*font-size: #{expected};.*")
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
RSpec::Matchers.define :have_color do | expected |
|
19
|
-
match do | actual |
|
20
|
-
actual =~ Regexp.new(".*color: rgb\\(#{expected}\\);.*")
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
RSpec::Matchers.define :have_title_containing do | expected |
|
25
|
-
match do | actual |
|
26
|
-
actual =~ Regexp.new(".*title='.*#{expected}.*'.*")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def file_with(details)
|
31
|
-
@formatter.format_entry 'SomeFile.java', details[:changes], details[:defects] || 0
|
32
|
-
end
|
33
|
-
|