metior 0.1.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.
@@ -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