header-inserter 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/../../lib/header-inserter/project'
2
+ require File.dirname(__FILE__) + '/../../lib/header-inserter/project_file'
3
+ require 'ftools'
4
+
5
+ non_existing = "/tmp/non_existing_project" + rand(1000000).to_s
6
+
7
+ empty = "/tmp/empty_project" + rand(1000000).to_s
8
+ Dir.mkdir empty
9
+
10
+ non_structured = "/tmp/non_structured_project" + rand(1000000).to_s
11
+ Dir.mkdir non_structured
12
+ main = File.new non_structured+"/Main.java", "w"
13
+ main.puts "class Main {}"
14
+ main.close
15
+ mainTest = File.new non_structured+"/MainTest.java", "w"
16
+ mainTest.puts "class MainTest {}"
17
+ mainTest.close
18
+ mainTestClass = File.new non_structured+"/MainTest.class", "w"
19
+ array = (1..500).to_a.map{|x| rand(256)}
20
+ mainTestClass.puts array.pack("c*")
21
+ mainTestClass.close
22
+
23
+ structured = "/tmp/structured_project" + rand(1000000).to_s
24
+ File.makedirs structured+"/src/my/project/internal"
25
+ File.makedirs structured+"/bin/my/project/internal"
26
+ File.makedirs structured+"/test/my/project/internal"
27
+ main = File.new structured+"/src/my/project/Main.java", "w"
28
+ main.puts "package my.project; class Main {}"
29
+ main.close
30
+ logic = File.new structured+"/src/my/project/internal/Logic.java", "w"
31
+ logic.puts "package my.project.internal; class Logic {}"
32
+ logic.close
33
+ logicClass = File.new structured+"/bin/my/project/internal/Logic.class", "w"
34
+ array = (1..500).to_a.map{|x| rand(256)}
35
+ logicClass.puts array.pack("c*")
36
+ logicClass.close
37
+ logicTest = File.new structured+"/test/my/project/internal/LogicTest.java", "w"
38
+ logicTest.puts "package my.project.internal; class LogicTest {}"
39
+ logicTest.close
40
+
41
+ Given /a[n]? (\S*) project/ do |type|
42
+ @project = Project.new eval(type.gsub("-","_"))
43
+ end
44
+
45
+ When /I list "(.*)" files/ do |type|
46
+ @files = @project.list(type.to_sym)
47
+ end
48
+
49
+ Then /I should get nothing/ do
50
+ @files.should == []
51
+ end
52
+
53
+ Then /I should get (\[".*"(?:, ".*")?\])/ do |list|
54
+ @files.sort.should == eval(list).map{|path| ProjectFile.new @project, path }.sort
55
+ end
56
+
57
+ at_exit do
58
+ FileUtils.rm_rf empty
59
+ FileUtils.rm_rf non_structured
60
+ FileUtils.rm_rf structured
61
+ end
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + "/../../lib/header-inserter"
2
+
3
+ gem 'cucumber'
4
+ require 'cucumber'
5
+ gem 'rspec'
6
+ require 'spec'
@@ -0,0 +1,6 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module HeaderInserter
5
+ VERSION = '1.0.1'
6
+ end
@@ -0,0 +1,31 @@
1
+ require 'date'
2
+
3
+ class Modification
4
+ include Comparable
5
+
6
+ @@revision_format = /r(\d{1,}) \| (\S+) \| (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ([+|-]?\d{2})(\d{2}) .* \| \d{1,} line[s]?\n\n(.*)\n/m
7
+ @@svn_time_format = "%Y-%m-%d %H:%M:%S %z"
8
+ attr_reader :revision, :author, :date, :log
9
+
10
+ def Modification.parse text_log
11
+ if @@revision_format.match text_log
12
+ revision = $1.to_i
13
+ author = $2
14
+ time_zone = ($4 + ":" + $5)
15
+ date = DateTime.strptime($3 + " " + time_zone, @@svn_time_format)
16
+ log = $6
17
+ Modification.new revision, author, date, log
18
+ end
19
+ end
20
+
21
+ def initialize revision, author, date, log
22
+ @revision = revision
23
+ @author = author
24
+ @date = date
25
+ @log = log
26
+ end
27
+
28
+ def <=> other
29
+ revision <=> other.revision
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ class NilVersionControl
2
+ @@instance = self.new
3
+
4
+ def NilVersionControl.new
5
+ @@instance
6
+ end
7
+
8
+ def history file_path
9
+ []
10
+ end
11
+ end
@@ -0,0 +1,47 @@
1
+ require 'find'
2
+ require File.dirname(__FILE__) + '/project_file'
3
+ require File.dirname(__FILE__) + '/nil_version_control'
4
+ require File.dirname(__FILE__) + '/svn_version_control'
5
+
6
+ class Project
7
+ include Comparable
8
+ attr_reader :path
9
+
10
+ def initialize path
11
+ @path = File.expand_path(path)
12
+ end
13
+
14
+ def <=> other
15
+ @path <=> other.path
16
+ end
17
+
18
+ def list file_pattern
19
+ if file_pattern.is_a? Symbol
20
+ file_pattern = /.*\.#{file_pattern.to_s}$/
21
+ end
22
+ files = []
23
+ Find.find @path do |entry|
24
+ short_entry = entry.sub /^#{@path+File::SEPARATOR}/, ""
25
+ if file_pattern.match short_entry
26
+ version_control = obtain_version_control short_entry
27
+ files << ProjectFile.new(self, short_entry, version_control)
28
+ end
29
+ end
30
+ files
31
+ end
32
+
33
+ protected
34
+
35
+ def obtain_version_control path
36
+ entries_path = @path + "/#{File.dirname(path)}/.svn/entries"
37
+ file_name = File.basename(path)
38
+ entries = File.new entries_path, "r"
39
+ contents = []
40
+ entries.each{|line| contents << line}
41
+ svn_path = contents[4].strip.gsub(/#{File.dirname(path)}/, "")
42
+ entry_for_file = contents.collect{|line| /^#{file_name}$/.match line }
43
+ return SvnVersionControl.new(svn_path) unless entry_for_file.empty?
44
+ rescue Errno::ENOENT
45
+ NilVersionControl.new
46
+ end
47
+ end
@@ -0,0 +1,89 @@
1
+ require File.dirname(__FILE__) + '/project'
2
+ require File.dirname(__FILE__) + '/nil_version_control'
3
+ require File.dirname(__FILE__) + '/svn_version_control'
4
+ require 'date'
5
+ require 'etc'
6
+
7
+ class ProjectFile
8
+ include Comparable
9
+ attr_reader :project, :path, :version_control
10
+
11
+ def initialize project, path, version_control = NilVersionControl.new
12
+ if project.nil?
13
+ @project = Project.new "/"
14
+ else
15
+ @project = project
16
+ end
17
+ @path = path
18
+ @version_control = version_control
19
+ end
20
+
21
+ def <=> other
22
+ absolute_path <=> other.absolute_path
23
+ end
24
+
25
+ def absolute_path
26
+ absolute = @project.path
27
+ absolute += File::SEPARATOR if @project.path != "/"
28
+ absolute + @path
29
+ end
30
+
31
+ def original_header
32
+ file = File.new absolute_path, "r"
33
+ header = ""
34
+ while (line = file.readline) and not(/\s*(?:package|class|import)\s*/.match line)
35
+ header += line
36
+ end
37
+ header
38
+ rescue Errno::ENOENT
39
+ return nil
40
+ end
41
+
42
+ def created_on
43
+ return Date.today if modifications.empty?
44
+ modifications[0].date
45
+ end
46
+
47
+ def contributors
48
+ return [username.strip] if modifications.empty?
49
+ modifications.map{ |mod| mod.author.strip }.uniq
50
+ end
51
+
52
+ def generate_header header, hooks
53
+ generated_header = header
54
+ hooks.keys.each do |key|
55
+ value = hooks[key].call(self)
56
+ if not value.is_a?(String)
57
+ puts "Error while evaluating the hook for #{key}. Returned a #{value.class} valued as '#{value}'. Calling to_s."
58
+ end
59
+ generated_header = generated_header.gsub key.to_s, value.to_s
60
+ end
61
+ generated_header
62
+ end
63
+
64
+ def add_header header, old_header = nil
65
+ content = header
66
+ file = File.new absolute_path, "r"
67
+ file.each do |line|
68
+ content += line
69
+ end
70
+ file.close
71
+
72
+ content.gsub(old_header, "") unless old_header.nil?
73
+
74
+ file = File.new absolute_path, "w"
75
+ file.puts content
76
+ file.close
77
+ end
78
+
79
+ protected
80
+
81
+ def modifications
82
+ @mods = version_control.history path if @mods.nil?
83
+ @mods
84
+ end
85
+
86
+ def username
87
+ Etc.getlogin
88
+ end
89
+ end
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + '/modification'
2
+
3
+ class SvnVersionControl
4
+ attr_reader :path
5
+ @@entry_start = /^[-]{72}$/
6
+ @@valid = /\s*r\d{1,} \| \S+ \| .* | \d{1,} line[s]?\n\n.*\n/
7
+
8
+ def initialize server_path
9
+ @path = server_path
10
+ @logs = {}
11
+ end
12
+
13
+ def retrieve_log file_path
14
+ if not @logs.has_key?(file_path)
15
+ @logs[file_path] = %x[svn log #{@path + file_path}]
16
+ end
17
+ @logs[file_path]
18
+ end
19
+
20
+ def history file_path
21
+ mods = []
22
+ log = retrieve_log file_path
23
+ log.split(@@entry_start).each do |entry|
24
+ mods << Modification.parse(entry) if @@valid.match(entry)
25
+ end
26
+ mods.reverse
27
+ end
28
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../lib/header-inserter/modification'
2
+ require 'date'
3
+
4
+ describe Modification do
5
+ it "should be created with revision number, author, date and log entry" do
6
+ date = Date.today
7
+ mod = Modification.new 1, "Hugo", date, "Just a test"
8
+ mod.author.should == "Hugo"
9
+ mod.revision.should == 1
10
+ mod.date.should == date
11
+ mod.log.should == "Just a test"
12
+ end
13
+
14
+ it "should parse a complete log entry" do
15
+ date = DateTime.strptime "2009-03-17 17:21:03 -0300", "%Y-%m-%d %H:%M:%S %z"
16
+ mod = Modification.parse """r1451 | hugo.corbucci | 2009-03-17 17:21:03 -0300 (Ter, 17 Mar 2009) | 2 lines
17
+
18
+ Adding the projects for erase and tests for it. I forgot it before.
19
+ Hugo
20
+ """
21
+ mod.author.should == "hugo.corbucci"
22
+ mod.revision.should == 1451
23
+ mod.date.should == date
24
+ mod.log.should == "Adding the projects for erase and tests for it. I forgot it before.\nHugo"
25
+ end
26
+
27
+ it "should sort according to the revision" do
28
+ date = Date.today - 1
29
+ first_mod = Modification.new 1, "Hugo", date, "Just a test"
30
+ second_mod = Modification.new 2, "Hugo", Date.today, "Just a test"
31
+ equal_mod = Modification.new 1, "Hugo", date, "Just a test"
32
+ (first_mod<=>second_mod).should == -1
33
+ (second_mod<=>first_mod).should == 1
34
+ (first_mod<=>first_mod).should == 0
35
+ (first_mod<=>equal_mod).should == 0
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/../lib/header-inserter/nil_version_control'
2
+
3
+ describe NilVersionControl do
4
+ it "should have only one instance" do
5
+ instance1 = NilVersionControl.new
6
+ instance2 = NilVersionControl.new
7
+ instance1.should == instance2
8
+ end
9
+
10
+ it "should always return an empty history" do
11
+ vc = NilVersionControl.new
12
+ vc.history("a").should == []
13
+ vc.history("http://svn.archimedes.org.br/public/mainarchimedes/rcparchimedes/br.org.archimedes.izpack/ArchimedesProjectSet.psf").should == []
14
+ end
15
+ end
@@ -0,0 +1,202 @@
1
+ require File.dirname(__FILE__) + '/../lib/header-inserter/project'
2
+ require File.dirname(__FILE__) + '/../lib/header-inserter/svn_version_control'
3
+ require File.dirname(__FILE__) + '/../lib/header-inserter/modification'
4
+ require 'ftools'
5
+ require 'fileutils'
6
+
7
+ describe ProjectFile do
8
+ before(:each) do
9
+ @project = Project.new "/tmp/header-insert-test-project"
10
+ end
11
+
12
+ it "should be created with a project, a path relative to that project (assuming NilVersionControl)" do
13
+ file = ProjectFile.new @project, "non-existing-file.rb"
14
+ file.absolute_path.should == "/tmp/header-insert-test-project/non-existing-file.rb"
15
+ file.version_control.should be_kind_of(NilVersionControl)
16
+ end
17
+
18
+ it "should be created with a project, a path relative to that project and a version control system" do
19
+ file = ProjectFile.new @project, "non-existing-file.rb", SvnVersionControl.new("http://svn.archimedes.org.br/public/")
20
+ file.absolute_path.should == "/tmp/header-insert-test-project/non-existing-file.rb"
21
+ file.version_control.should be_kind_of(SvnVersionControl)
22
+ end
23
+
24
+ it "should be assume '/' as a root path if the project is nil" do
25
+ file = ProjectFile.new nil, "testfile.rb"
26
+ file.absolute_path.should == "/testfile.rb"
27
+ end
28
+
29
+ it "should compare based on the relative path" do
30
+ file = ProjectFile.new @project, "a.file"
31
+ other_file = ProjectFile.new @project, "z.file"
32
+ (file<=>other_file).should == -1
33
+ (other_file<=>file).should == 1
34
+ (file<=>file).should == 0
35
+ (other_file<=>other_file).should == 0
36
+ end
37
+
38
+ it "should equal based on the absolute and the path" do
39
+ file = ProjectFile.new @project, "a.file"
40
+ other_file = ProjectFile.new @project, "z.file"
41
+ equal_file = ProjectFile.new @project, "a.file"
42
+
43
+ file.should == file
44
+ file.should_not == other_file
45
+ file.should == equal_file
46
+
47
+ other_file.should == other_file
48
+ other_file.should_not == file
49
+ other_file.should_not == equal_file
50
+ end
51
+
52
+ it "should retrieve an empty original header from a non existing file" do
53
+ file = ProjectFile.new @project, "a.file"
54
+ file.original_header.should be_nil
55
+ end
56
+
57
+ it "should retrieve all the header until package from an existing file" do
58
+ file = ProjectFile.new @project, "my/project/A.java"
59
+ header = "/**\n * A simple test\n */\n\n"
60
+
61
+ create_file file.absolute_path, "#{header}package my.project;\n\nclass A {}"
62
+ file.original_header.should == header
63
+ end
64
+
65
+ it "should retrieve all the header until import from an existing file without package" do
66
+ file = ProjectFile.new @project, "A.java"
67
+ header = "/**\n * A simple test\n */\n\n"
68
+
69
+ create_file file.absolute_path, "#{header}import java.util.Observable;\n\nclass A extends Observable {}"
70
+ file.original_header.should == header
71
+ end
72
+
73
+ it "should retrieve all the header until class from an existing file without package or imports" do
74
+ file = ProjectFile.new @project, "A.java"
75
+ header = "/**\n * A simple test\n */\n\n"
76
+
77
+ create_file file.absolute_path, "#{header}class A {}"
78
+ file.original_header.should == header
79
+ end
80
+
81
+ it "should retrieve created on information from modifications from version control" do
82
+ mock_version_control = mock(NilVersionControl.new)
83
+ mock_version_control.should_receive(:history).with("A.java").once.and_return([])
84
+ file = ProjectFile.new @project, "A.java", mock_version_control
85
+ file.created_on.should == Date.today
86
+
87
+ date = DateTime.parse("2009-01-01T17:32:45-03:00")
88
+ mock_version_control = mock(NilVersionControl.new)
89
+ mock_version_control.should_receive(:history).with("A.java").once.and_return([Modification.new(134, "hugo", date, "Unique entry")])
90
+ file = ProjectFile.new @project, "A.java", mock_version_control
91
+ file.created_on.should == date
92
+
93
+ mock_version_control = mock(NilVersionControl.new)
94
+ date1 = DateTime.parse("2009-01-01T17:32:45-03:00")
95
+ date2 = DateTime.parse("2009-02-12T09:02:56+08:00")
96
+ date3 = DateTime.parse("2009-03-24T19:45:30+00:00")
97
+ mods = [Modification.new(134, "hugo", date1, "First entry"),
98
+ Modification.new(531, "mari", date2, "Second entry"),
99
+ Modification.new(1039, "hugo", date3, "Third and last entry")]
100
+ mock_version_control.should_receive(:history).with("A.java").once.and_return(mods)
101
+ file = ProjectFile.new @project, "A.java", mock_version_control
102
+ file.created_on.should == date1
103
+ end
104
+
105
+ it "should retrieve contributors from modifications from version control with unique names sorted by first occurrence" do
106
+ mock_version_control = mock(NilVersionControl.new)
107
+ mock_version_control.should_receive(:history).with("A.java").once.and_return([])
108
+ file = ProjectFile.new @project, "A.java", mock_version_control
109
+ file.contributors.should == ["night"]
110
+
111
+ date = DateTime.parse("2009-01-01T17:32:45-03:00")
112
+ mock_version_control = mock(NilVersionControl.new)
113
+ mock_version_control.should_receive(:history).with("A.java").once.and_return([Modification.new(134, "hugo", date, "Unique entry")])
114
+ file = ProjectFile.new @project, "A.java", mock_version_control
115
+ file.contributors.should == ["hugo"]
116
+
117
+ mock_version_control = mock(NilVersionControl.new)
118
+ date1 = DateTime.parse("2009-01-01T17:32:45-03:00")
119
+ date2 = DateTime.parse("2009-02-12T09:02:56+08:00")
120
+ date3 = DateTime.parse("2009-03-24T19:45:30+00:00")
121
+ date4 = DateTime.parse("2009-03-24T20:01:13-03:00")
122
+ date5 = DateTime.parse("2009-03-24T20:11:16-03:00")
123
+ mods = [Modification.new(134, "hugo", date1, "First entry"),
124
+ Modification.new(531, "mari", date2, "Second entry"),
125
+ Modification.new(1039, "hugo", date3, "Third entry"),
126
+ Modification.new(1045, "kung", date4, "Fourth entry"),
127
+ Modification.new(1253, "mari", date5, "Last entry")]
128
+ mock_version_control.should_receive(:history).with("A.java").once.and_return(mods)
129
+ file = ProjectFile.new @project, "A.java", mock_version_control
130
+ file.contributors.should == ["hugo", "mari", "kung"]
131
+ end
132
+
133
+ it "should have a nil version control by default" do
134
+ file = ProjectFile.new @project, "A.java"
135
+ header = "/**\n * A simple test\n */\n\n"
136
+
137
+ create_file file.absolute_path, "#{header}class A {}"
138
+ file.version_control.should == NilVersionControl.new
139
+ end
140
+
141
+ it "should retrieve a nil history if not under subversion control"
142
+ it "should retrieve a single modification history if recently under subversion control"
143
+ it "should retrieve multiple modifications history if under subversion control for a long time"
144
+
145
+ it "should generate a header according to a header format and hooks" do
146
+ header = "# My fine header.\n# It has the date (REPLACE_DATE) and the first contributor (FIRST_CONTRIBUTOR).\n # And ends like nothing\n"
147
+ hooks = { :REPLACE_DATE => lambda { |file| file.created_on.strftime "%Y-%m-%d, %H:%M:%S" },
148
+ :FIRST_CONTRIBUTOR => lambda {|file| file.contributors[0] } }
149
+
150
+ expected_header = "# My fine header.\n# It has the date (2009-03-24, 21:43:58) and the first contributor (hugo).\n # And ends like nothing\n"
151
+
152
+ date = DateTime.parse "2009-03-24T21:43:58-03:00"
153
+ mock_version_control = mock(NilVersionControl.new)
154
+ mock_version_control.should_receive(:history).with("A.java").once.and_return([Modification.new(324, "hugo", date, "Unique entry")])
155
+ file = ProjectFile.new @project, "A.java", mock_version_control
156
+ file.generate_header(header, hooks).should == expected_header
157
+ end
158
+
159
+ it "should add a header at the beginning of the file" do
160
+ new_header = "/**\n * My fine header.\n * It has the date (2009-03-24, 21:43:58) and the first contributor (hugo).\n * And ends like nothing\n */\n\n"
161
+ content = "class A {}\n"
162
+
163
+ file = ProjectFile.new @project, "A.java"
164
+ create_file file.absolute_path, content
165
+ file.add_header(new_header)
166
+
167
+ read_content(file.absolute_path).should == new_header + content
168
+ end
169
+
170
+ it "should add a header at the beginning of the file and remove the one specified" do
171
+ new_header = "/**\n * My fine header.\n * It has the date (2009-03-24, 21:43:58) and the first contributor (hugo).\n * And ends like nothing\n */\n\n"
172
+ stupid_header = "/** A stupid header */\n\n"
173
+ content = "#{stupid_header}class A {}\n"
174
+
175
+ file = ProjectFile.new @project, "A.java"
176
+ create_file file.absolute_path, content
177
+ file.add_header(new_header, stupid_header)
178
+
179
+ read_content(file.absolute_path).should == new_header + content
180
+ end
181
+
182
+ after(:each) do
183
+ FileUtils.rm_rf @project.path
184
+ end
185
+
186
+ protected
187
+
188
+
189
+ def create_file absolute_path, content
190
+ File.makedirs File.dirname(absolute_path)
191
+ file = File.new absolute_path, "w"
192
+ file.puts content
193
+ file.close
194
+ end
195
+
196
+ def read_content absolute_path
197
+ file = File.new absolute_path, "r"
198
+ content = file.inject { |result , line| result + line }
199
+ file.close
200
+ content
201
+ end
202
+ end