metior 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ module Metior
7
+
8
+ # The current version of the Metior gem
9
+ VERSION = '0.1.1'
10
+
11
+ end
data/metior.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ require 'bundler'
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/lib/metior/version')
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "metior"
7
+ s.version = Metior::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = [ 'Sebastian Staudt' ]
10
+ s.email = [ 'koraktor@gmail.com' ]
11
+ s.homepage = 'http://koraktor.de/metior'
12
+ s.summary = 'A source code history analyzer API'
13
+ s.description = 'Metior is a source code history analyzer that provides various statistics about a source code repository and its change over time.'
14
+
15
+ Bundler.definition.dependencies.each do |dep|
16
+ if dep.groups.include?(:development) || dep.groups.include?(:test)
17
+ s.add_development_dependency(dep.name, dep.requirement.to_s)
18
+ else
19
+ s.add_dependency(dep.name, dep.requirement.to_s)
20
+ end
21
+ end
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- test/*`.split("\n")
25
+ s.require_paths = [ 'lib' ]
26
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,26 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
7
+ $LOAD_PATH.unshift File.dirname(__FILE__)
8
+ require 'metior'
9
+ include Metior
10
+
11
+ Bundler.require :test
12
+
13
+ # Extends TestCase functionality
14
+ class Test::Unit::TestCase
15
+
16
+ # Provides a negative assertion that's easier on the eyes
17
+ #
18
+ # The assertion fails, if the given value is +true+.
19
+ #
20
+ # @param [true, false] boolean The value that should be +false+
21
+ # @param [String] message The message that should be displayed
22
+ def assert_not(boolean, message = '')
23
+ assert !boolean, message
24
+ end
25
+
26
+ end
@@ -0,0 +1,28 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'metior/actor'
7
+
8
+ module Metior
9
+
10
+ module MockVCS
11
+
12
+ class Actor < Metior::Actor
13
+
14
+ def self.id_for(actor)
15
+ actor.last
16
+ end
17
+
18
+ def initialize(repo, actor)
19
+ super repo
20
+
21
+ @id = actor.last
22
+ @name = actor.first
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,53 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'mock_vcs/actor'
7
+ require 'metior/commit'
8
+
9
+ module Metior
10
+
11
+ module MockVCS
12
+
13
+ class Commit < Metior::Commit
14
+
15
+ def initialize(repo, range, commit)
16
+ super repo, range
17
+
18
+ @additions = commit[:impact].first.to_i
19
+ @authored_date = Time.at commit[:info][6].to_i
20
+ @committed_date = Time.at commit[:info][3].to_i
21
+ @deletions = commit[:impact].last.to_i
22
+ @id = commit[:ids].first
23
+ @message = commit[:info].first
24
+
25
+ authors = repo.authors range
26
+ @author = authors[Actor.id_for commit[:info][4..5]]
27
+ @author = Actor.new repo, commit[:info][4..5] if author.nil?
28
+ @author.add_commit self
29
+
30
+ committers = repo.committers range
31
+ @committer = committers[Actor.id_for commit[:info][1..2]]
32
+ @committer = Actor.new repo, commit[:info][1..2] if @committer.nil?
33
+ @committer.add_commit self
34
+
35
+ @added_files = []
36
+ @modified_files = []
37
+ @deleted_files = []
38
+ commit[:files].each do |file|
39
+ if file.first == 'A'
40
+ @added_files << file.last
41
+ elsif file.first == 'A'
42
+ @deleted_files << file.last
43
+ else
44
+ @modified_files << file.last
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,72 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'metior/repository'
7
+ require 'mock_vcs/commit'
8
+
9
+ module Metior
10
+
11
+ module MockVCS
12
+
13
+ NAME = :mock
14
+
15
+ include VCS
16
+
17
+ class Repository < Metior::Repository
18
+
19
+ DEFAULT_BRANCH = 'master'
20
+
21
+ include Metior::MockVCS
22
+
23
+ def initialize(path)
24
+ super path
25
+
26
+ @file = File.open(File.expand_path(path), 'r')
27
+ @file.set_encoding 'utf-8' if @file.respond_to? :set_encoding
28
+ end
29
+
30
+ def load_commits(range)
31
+ commits = []
32
+
33
+ commit = {}
34
+ @file.lines.each do |line|
35
+ line.strip!
36
+
37
+ next if line.empty?
38
+
39
+ if line.match /^[AMD]\0/
40
+ commit[:files] = [] unless commit.key? :files
41
+ commit[:files] << line.split("\0")
42
+ elsif line.match /\d+\0\d+$/
43
+ commit[:impact] = line.split("\0")
44
+
45
+ if commits.empty?
46
+ if range.first != '' && !commit[:ids].include?(range.last)
47
+ commit = {}
48
+ next
49
+ end
50
+ elsif commit[:ids].include? range.first
51
+ break
52
+ end
53
+
54
+ commits << commit
55
+ commit = {}
56
+ else
57
+ line = line.split("\0")
58
+ ids = [line[0]]
59
+ ids += line[1][2..-2].split(', ') unless line[1].empty?
60
+ commit[:ids] = ids
61
+ commit[:info] = line[2..-1]
62
+ end
63
+ end
64
+
65
+ commits
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,61 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'helper'
7
+
8
+ class TestClassLoading < Test::Unit::TestCase
9
+
10
+ context 'Metior' do
11
+
12
+ should 'load basic modules and dependencies first' do
13
+ assert Object.const_defined? :Metior
14
+ assert Metior.const_defined? :Git
15
+ assert Metior.const_defined? :GitHub
16
+ assert Metior.const_defined? :VCS
17
+ assert Metior.const_defined? :VERSION
18
+
19
+ assert !Metior.const_defined?(:Actor)
20
+ assert !Metior.const_defined?(:Commit)
21
+ assert !Metior.const_defined?(:Repository)
22
+ assert !Metior::Git.const_defined?(:Actor)
23
+ assert !Metior::Git.const_defined?(:Commit)
24
+ assert !Metior::Git.const_defined?(:Repository)
25
+ assert !Metior::GitHub.const_defined?(:Actor)
26
+ assert !Metior::GitHub.const_defined?(:Commit)
27
+ assert !Metior::GitHub.const_defined?(:Repository)
28
+
29
+ assert !Object.const_defined?(:Grit)
30
+ assert !Object.const_defined?(:Octokit)
31
+ end
32
+
33
+ should 'load requirements when using Git' do
34
+ Metior::Git::Repository
35
+
36
+ assert Metior.const_defined? :Actor
37
+ assert Metior.const_defined? :Commit
38
+ assert Metior.const_defined? :Repository
39
+ assert Metior::Git.const_defined? :Actor
40
+ assert Metior::Git.const_defined? :Commit
41
+ assert Metior::Git.const_defined? :Repository
42
+
43
+ assert Object.const_defined? :Grit
44
+ end
45
+
46
+ should 'load requirements when using GitHub' do
47
+ Metior::GitHub::Repository
48
+
49
+ assert Metior.const_defined? :Actor
50
+ assert Metior.const_defined? :Commit
51
+ assert Metior.const_defined? :Repository
52
+ assert Metior::GitHub.const_defined? :Actor
53
+ assert Metior::GitHub.const_defined? :Commit
54
+ assert Metior::GitHub.const_defined? :Repository
55
+
56
+ assert Object.const_defined? :Octokit
57
+ end
58
+
59
+ end
60
+
61
+ end
data/test/test_git.rb ADDED
@@ -0,0 +1,22 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'helper'
7
+
8
+ class TestGit < Test::Unit::TestCase
9
+
10
+ context 'The Git implementation' do
11
+
12
+ should 'support file stats' do
13
+ assert Metior::Git.supports? :file_stats
14
+ end
15
+
16
+ should 'support line stats' do
17
+ assert Metior::Git.supports? :line_stats
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,43 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'helper'
7
+
8
+ class TestGitHub < Test::Unit::TestCase
9
+
10
+ context 'The GitHub implementation' do
11
+
12
+ should 'not support file stats' do
13
+ assert_not Metior::GitHub.supports? :file_stats
14
+ end
15
+
16
+ should 'not support line stats' do
17
+ assert_not Metior::GitHub.supports? :line_stats
18
+ end
19
+
20
+ should 'not be able to get file stats of a repository' do
21
+ repo = Metior::GitHub::Repository.new 'koraktor', 'rubikon'
22
+ assert_raises UnsupportedError do
23
+ repo.file_stats
24
+ end
25
+ end
26
+
27
+ should 'not be able to get the most significant authors of a repository' do
28
+ repo = Metior::GitHub::Repository.new 'koraktor', 'rubikon'
29
+ assert_raises UnsupportedError do
30
+ repo.significant_authors
31
+ end
32
+ end
33
+
34
+ should 'not be able to get the most significant commits of a repository' do
35
+ repo = Metior::GitHub::Repository.new 'koraktor', 'rubikon'
36
+ assert_raises UnsupportedError do
37
+ repo.significant_commits
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,34 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'helper'
7
+
8
+ class TestMetior < Test::Unit::TestCase
9
+
10
+ context 'Metior' do
11
+
12
+ should 'should provide several VCS implementations' do
13
+ assert_equal({ :git => Git, :github => GitHub }, Metior.vcs_types)
14
+ assert_equal Git, Metior.vcs(:git)
15
+ end
16
+
17
+ should 'raise an error if an implementation does not exist' do
18
+ begin
19
+ Metior.vcs :unknown
20
+ assert false
21
+ rescue
22
+ assert $!.is_a? RuntimeError
23
+ assert_equal 'No VCS registered for :unknown', $!.message
24
+ end
25
+ end
26
+
27
+ should 'allow easy creation of a implementation specific repository' do
28
+ assert_kind_of Git::Repository, Metior.repository(:git, File.dirname(File.dirname(__FILE__)))
29
+ assert_kind_of GitHub::Repository, Metior.repository(:github, 'some', 'user')
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,150 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'helper'
7
+
8
+ class TestRepository < Test::Unit::TestCase
9
+
10
+ context 'The base class for Metior VCS repositories' do
11
+
12
+ setup do
13
+ require 'metior/repository'
14
+
15
+ @repo = Metior::Repository.new('dummy')
16
+ end
17
+
18
+ should 'not implement the #load_commits method' do
19
+ assert_raise NotImplementedError do
20
+ @repo.send(:load_commits, nil)
21
+ end
22
+ end
23
+
24
+ should 'parse commit ranges correctly' do
25
+ assert_equal 'master'..'development', @repo.send(:parse_range, 'master'..'development')
26
+ assert_equal 'master'..'development', @repo.send(:parse_range, 'master..development')
27
+ assert_equal ''..'master', @repo.send(:parse_range, 'master')
28
+ end
29
+
30
+ should 'hit the cache when loading the same commit range' do
31
+ @repo.expects(:load_commits).once.returns([])
32
+
33
+ @repo.commits 'master'
34
+ @repo.commits 'master'
35
+ end
36
+
37
+ should 'miss the cache when loading a different commit range' do
38
+ @repo.expects(:load_commits).twice.returns([])
39
+
40
+ @repo.commits 'master'
41
+ @repo.commits 'HEAD'
42
+ end
43
+
44
+ end
45
+
46
+ context 'A repository implementation' do
47
+
48
+ setup do
49
+ require 'mock_vcs/repository'
50
+
51
+ repo_file = "#{File.dirname(__FILE__)}/fixtures/mojombo-grit-master-1b2fe77.txt"
52
+ @repo = MockVCS::Repository.new repo_file
53
+ end
54
+
55
+ should 'be able to load all commits from the repository' do
56
+ commits = @repo.commits
57
+ assert_equal 415, commits.size
58
+ assert commits.all? { |commit| commit.is_a? Metior::Commit }
59
+
60
+ head = commits.first
61
+ assert_equal '4c592b4', head.id
62
+ end
63
+
64
+ should 'be able to load a range of commits from the repository' do
65
+ commits = @repo.commits 'ef2870b'..'4c592b4'
66
+ assert_equal 5, commits.size
67
+ assert commits.all? { |commit| commit.is_a? Metior::Commit }
68
+ assert_equal '4c592b4', commits.first.id
69
+ assert_equal 'f0cc7f7', commits.last.id
70
+ end
71
+
72
+ should 'know the authors of the repository' do
73
+ authors = @repo.authors
74
+ assert_equal 37, authors.size
75
+ assert authors.values.all? { |author| author.is_a? Metior::Actor }
76
+
77
+ assert_equal %w{
78
+ adam@therealadam.com aman@tmm1.net antonin@hildebrand.cz
79
+ bobbywilson0@gmail.com bryce@worldmedia.net cehoffman@gmail.com
80
+ chapados@sciencegeeks.org cho45@lowreal.net chris@ozmm.org
81
+ davetron5000@gmail.com david.kowis@rackspace.com dustin@spy.net
82
+ engel@engel.uk.to evil@che.lu gram.gibson@uky.edu
83
+ hiroshi3110@gmail.com igor@wiedler.ch johan@johansorensen.com
84
+ jos@catnook.com jpriddle@nevercraft.net kamal.fariz@gmail.com
85
+ koraktor@gmail.com mtraverso@acm.org ohnobinki@ohnopublishing.net
86
+ paul+git@mjr.org pjhyett@gmail.com rsanheim@gmail.com
87
+ rtomayko@gmail.com schacon@gmail.com scott@railsnewbie.com
88
+ technoweenie@gmail.com tim@dysinger.net tim@spork.in
89
+ tom@mojombo.com tom@taco.(none) voker57@gmail.com wayne@larsen.st
90
+ }, authors.keys.sort
91
+ end
92
+
93
+ should 'know the committers of the repository' do
94
+ committers = @repo.committers
95
+ assert_equal 29, committers.size
96
+ assert committers.values.all? { |committer| committer.is_a? Metior::Actor }
97
+
98
+ assert_equal %w{
99
+ adam@therealadam.com aman@tmm1.net antonin@hildebrand.cz
100
+ bobbywilson0@gmail.com bryce@worldmedia.net chris@ozmm.org
101
+ davetron5000@gmail.com david.kowis@rackspace.com dustin@spy.net
102
+ engel@engel.uk.to evil@che.lu hiroshi3110@gmail.com
103
+ johan@johansorensen.com jos@catnook.com kamal.fariz@gmail.com
104
+ koraktor@gmail.com mtraverso@acm.org ohnobinki@ohnopublishing.net
105
+ paul+git@mjr.org pjhyett@gmail.com rsanheim@gmail.com
106
+ rtomayko@gmail.com schacon@gmail.com technoweenie@gmail.com
107
+ tim@dysinger.net tim@spork.in tom@mojombo.com tom@taco.(none)
108
+ voker57@gmail.com
109
+ }, committers.keys.sort
110
+ end
111
+
112
+ should 'know the most significant authors of the repository' do
113
+ authors = @repo.significant_authors
114
+ assert_equal 3, authors.size
115
+ assert authors.all? { |author| author.is_a? Metior::Actor }
116
+
117
+ assert_equal [
118
+ "tom@mojombo.com", "schacon@gmail.com", "rtomayko@gmail.com"
119
+ ], authors.collect { |author| author.id }
120
+ end
121
+
122
+ should 'know the most significant commits of the repository' do
123
+ commits = @repo.significant_commits('master', 8)
124
+ assert_equal 8, commits.size
125
+ assert commits.all? { |commit| commit.is_a? Metior::Commit }
126
+
127
+ assert_equal %w{
128
+ c0f0b4f 47ab25c f3a24ae 18ec70e 242253b c87612b 6bb41e4 4d9c7be
129
+ }, commits.collect { |commit| commit.id }
130
+
131
+ modifications = commits.first.modifications
132
+ commits[1..-1].each do |commit|
133
+ assert modifications >= commit.modifications
134
+ modifications = commit.modifications
135
+ end
136
+ end
137
+
138
+ should 'know the top authors of the repository' do
139
+ authors = @repo.top_authors
140
+ assert_equal 3, authors.size
141
+ assert authors.all? { |author| author.is_a? Metior::Actor }
142
+
143
+ assert_equal [
144
+ "tom@mojombo.com", "schacon@gmail.com", "technoweenie@gmail.com"
145
+ ], authors.collect { |author| author.id }
146
+ end
147
+
148
+ end
149
+
150
+ end