place 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +11 -0
- data/LICENSE +207 -0
- data/README.markdown +96 -0
- data/Rakefile +9 -0
- data/Technical.markdown +29 -0
- data/examples/sample-1.rb +34 -0
- data/lib/place/comment.rb +76 -0
- data/lib/place/link.rb +20 -0
- data/lib/place/participation.rb +12 -0
- data/lib/place/project.rb +159 -0
- data/lib/place/time_point.rb +55 -0
- data/lib/place/user.rb +79 -0
- data/lib/place/work_item.rb +179 -0
- data/lib/place/work_record.rb +51 -0
- data/lib/place.rb +112 -0
- data/place.gemspec +19 -0
- data/test/helper.rb +9 -0
- data/test/test_comment.rb +35 -0
- data/test/test_place.rb +28 -0
- data/test/test_project.rb +100 -0
- data/test/test_time_point.rb +50 -0
- data/test/test_user.rb +51 -0
- data/test/test_workitem.rb +172 -0
- metadata +133 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
module Place
|
2
|
+
|
3
|
+
# A TimePoint is a "named milestone"
|
4
|
+
class TimePoint < Ohm::Model
|
5
|
+
attribute :tid
|
6
|
+
index :tid
|
7
|
+
attribute :url
|
8
|
+
|
9
|
+
attribute :name
|
10
|
+
attribute :time
|
11
|
+
attribute :description
|
12
|
+
|
13
|
+
reference :project, Project
|
14
|
+
|
15
|
+
collection :workitems, WorkItem, :timepoint
|
16
|
+
|
17
|
+
class << self; attr_accessor :rules end
|
18
|
+
@rules = {
|
19
|
+
'name' => '//time-point/field[@id="name"]',
|
20
|
+
'description' => '//time-point/field[@id="description"]',
|
21
|
+
'time' => '//time-point/field[@id="time"]'
|
22
|
+
}
|
23
|
+
|
24
|
+
# Collect all timepoints (url) under a given project url
|
25
|
+
def self.collect_all(base_url)
|
26
|
+
Dir.glob(base_url + '/tracker/timepoints/*.xml').each do |filename|
|
27
|
+
TimePoint.create(
|
28
|
+
:tid => File.basename(filename, '.xml'),
|
29
|
+
:url => filename,
|
30
|
+
:project => Project.find_by_url(base_url)
|
31
|
+
)
|
32
|
+
Place.logger.info("collected timepoint at #{filename}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Retrieve a specific timepoint data (name, time, description)
|
37
|
+
def retrieve
|
38
|
+
content = IO.readlines(url).join('')
|
39
|
+
doc = Nokogiri::XML(content)
|
40
|
+
self.class.rules.each_pair do |k,v|
|
41
|
+
tmp = doc.xpath(v)
|
42
|
+
self.send("#{k}=", tmp[0].content) unless tmp[0].nil?
|
43
|
+
end
|
44
|
+
Place.logger.info("retrieved timepoint #{self.url}")
|
45
|
+
self.save
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.find_by_tid(tid)
|
49
|
+
return nil if tid.nil?
|
50
|
+
tps = self.find(:tid => tid.to_s)
|
51
|
+
tps.nil? ? nil : tps[0]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/lib/place/user.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
module Place
|
2
|
+
|
3
|
+
# A User is an account under Polarion
|
4
|
+
class User < Ohm::Model
|
5
|
+
attribute :name
|
6
|
+
index :name
|
7
|
+
attribute :url
|
8
|
+
|
9
|
+
attribute :full_name
|
10
|
+
attribute :description
|
11
|
+
attribute :email
|
12
|
+
|
13
|
+
collection :workrecords, WorkRecord, :user
|
14
|
+
collection :comments, Comment, :author
|
15
|
+
|
16
|
+
set :watches, WorkItem
|
17
|
+
collection :author_of, WorkItem, :author
|
18
|
+
set :assignments, WorkItem
|
19
|
+
|
20
|
+
collection :participations, Participation, :user
|
21
|
+
|
22
|
+
|
23
|
+
# Collect all users (name, url) under a given repository url
|
24
|
+
def self.collect_all(base_url)
|
25
|
+
Dir.glob(base_url + '/.polarion/user-management/users/**/user.xml').each do |filename|
|
26
|
+
User.create(
|
27
|
+
:name => File.dirname(filename).split(/\//).last,
|
28
|
+
:url => filename
|
29
|
+
)
|
30
|
+
Place.logger.info("collected user at #{filename}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self; attr_accessor :rules end
|
35
|
+
@rules = {
|
36
|
+
'full_name' => '//user/field[@id="name"]',
|
37
|
+
'description' => '//user/field[@id="description"]',
|
38
|
+
'email' => '//user/field[@id="email"]'
|
39
|
+
}
|
40
|
+
|
41
|
+
# Retrieve a specific user data (based on name and url only)
|
42
|
+
def retrieve
|
43
|
+
content = IO.readlines(url).join('')
|
44
|
+
doc = Nokogiri::XML(content)
|
45
|
+
self.class.rules.each_pair do |k,v|
|
46
|
+
tmp = doc.xpath(v)
|
47
|
+
self.send("#{k}=", tmp[0].content) unless tmp[0].nil?
|
48
|
+
end
|
49
|
+
Place.logger.info("retrieved user at #{self.name}")
|
50
|
+
self.save
|
51
|
+
end
|
52
|
+
|
53
|
+
def retrieve_watches
|
54
|
+
content = IO.readlines(url).join('')
|
55
|
+
doc = Nokogiri::XML(content)
|
56
|
+
doc.xpath('//user/field[@id="watches"]/list//item').each do |wa|
|
57
|
+
prj, wid = wa.text.split('$')
|
58
|
+
if Project.all.map{|p| p.name}.include?(prj)
|
59
|
+
wi = WorkItem.find_by_wid(wid)
|
60
|
+
unless wi.nil?
|
61
|
+
wi.watches << User.find_by_name(name)
|
62
|
+
wi.save
|
63
|
+
self.watches << wi
|
64
|
+
self.save
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
Place.logger.info("retrieved watches for user #{self.name}")
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
# Find a user by the given name, returns +nil+ if not present
|
73
|
+
def self.find_by_name(name)
|
74
|
+
users = self.find(:name => name.to_s)
|
75
|
+
users.nil? ? nil : users[0]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Place
|
5
|
+
|
6
|
+
# Each WorkItem has a work-item id (wid) and an url that refers to its main xml file (workitem.xml) located under its own directory (that terminates with its Polarion ID = our wid).
|
7
|
+
class WorkItem < Ohm::Model
|
8
|
+
attribute :wid
|
9
|
+
index :wid
|
10
|
+
|
11
|
+
attribute :url
|
12
|
+
attribute :type
|
13
|
+
index :type
|
14
|
+
attribute :fields
|
15
|
+
|
16
|
+
reference :author, User
|
17
|
+
set :assignees, User
|
18
|
+
|
19
|
+
reference :timepoint, TimePoint
|
20
|
+
reference :project, Project
|
21
|
+
|
22
|
+
set :watches, User
|
23
|
+
collection :workrecords, WorkRecord, :workitem
|
24
|
+
collection :comments, Comment, :workitem
|
25
|
+
|
26
|
+
# Collect all workitems (wid and url) under a given project url
|
27
|
+
def self.collect_all(base_url)
|
28
|
+
Dir.glob(base_url + '/tracker/workitems/**/workitem.xml').each do |filename|
|
29
|
+
wi = WorkItem.create(
|
30
|
+
:wid => File.dirname(filename).split(/\//).last,
|
31
|
+
:url => filename,
|
32
|
+
:fields => "{}"
|
33
|
+
)
|
34
|
+
project_prefix = wi.wid.split(/\-/)[0]
|
35
|
+
wi.project = Project.find_by_prefix(project_prefix)
|
36
|
+
Place.logger.info("collected workitem at #{wi.url}")
|
37
|
+
wi.save
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Retrieve a specific workitem (based on wid and url only)
|
42
|
+
def retrieve
|
43
|
+
content = IO.readlines(url).join('')
|
44
|
+
doc = Nokogiri::XML(content)
|
45
|
+
|
46
|
+
doc.xpath('//work-item/field').each do |field|
|
47
|
+
self[field.attributes['id'].to_s] = field.content
|
48
|
+
end
|
49
|
+
|
50
|
+
self.type = self[:type]
|
51
|
+
|
52
|
+
self.author = User.find_by_name(self[:author])
|
53
|
+
|
54
|
+
unless self[:assignee].nil?
|
55
|
+
self[:assignee].split.each do |name|
|
56
|
+
u = User.find_by_name(name)
|
57
|
+
unless u.nil?
|
58
|
+
self.assignees << u
|
59
|
+
u.assignments << self
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
self.timepoint = TimePoint.find_by_tid(self['timePoint'])
|
65
|
+
|
66
|
+
%w(initialEstimate timeSpent remainingEstimate).each do |t|
|
67
|
+
self[t] = to_hours(self[t])
|
68
|
+
end
|
69
|
+
|
70
|
+
# links
|
71
|
+
doc.xpath('/work-item/field[@id="linkedWorkItems"]/list/struct').each do |struct|
|
72
|
+
l = Link.create(
|
73
|
+
:role => struct.xpath('item[@id="role"]').text,
|
74
|
+
:suspect => struct.xpath('item[@id="suspect"]').text == 'true' ? true : false,
|
75
|
+
:revision => struct.xpath('item[@id="revision"]').empty? ? false : true,
|
76
|
+
:from => self,
|
77
|
+
:to => WorkItem.find_by_wid(struct.xpath('item[@id="workItem"]').text),
|
78
|
+
:from_wid => self.wid,
|
79
|
+
:to_wid => struct.xpath('item[@id="workItem"]').text
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
# comments
|
84
|
+
Dir.glob(File.dirname(url) + '/comment-*.xml').each do |filename|
|
85
|
+
c = Comment.create(:url => filename)
|
86
|
+
c.retrieve
|
87
|
+
end
|
88
|
+
|
89
|
+
# workrecords
|
90
|
+
Dir.glob(File.dirname(url) + '/workrecord-*.xml').each do |filename|
|
91
|
+
wr = WorkRecord.create(:url => filename)
|
92
|
+
wr.retrieve
|
93
|
+
end
|
94
|
+
|
95
|
+
Place.logger.info("retrieved workitem #{self.wid}")
|
96
|
+
|
97
|
+
self.save
|
98
|
+
end
|
99
|
+
|
100
|
+
# Get value from specified field, +nil+ if not present
|
101
|
+
def [](fname)
|
102
|
+
JSON.parse(fields)[fname.to_s]
|
103
|
+
end
|
104
|
+
|
105
|
+
# Set value for specified field
|
106
|
+
def []=(fname, fvalue)
|
107
|
+
h = JSON.parse(self.fields)
|
108
|
+
h[fname.to_s] = fvalue
|
109
|
+
self.fields = h.to_json
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns a workitem by its wid, +nil+ if not present
|
113
|
+
def self.find_by_wid(wid)
|
114
|
+
wis = self.find(:wid => wid)
|
115
|
+
wis.nil? ? nil : wis[0]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns all workitems of the given +type+
|
119
|
+
def self.find_by_type(type)
|
120
|
+
#self.all.select{|wi| wi.type == type.to_s}
|
121
|
+
self.find(:type => type.to_s)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns all links from the current workitems to others, eventually only with a specified role
|
125
|
+
def links_out(role=nil)
|
126
|
+
if role.nil?
|
127
|
+
Link.find(:from_wid => self.wid)
|
128
|
+
else
|
129
|
+
Link.find(:from_wid => self.wid, :role => role.to_s)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns all links to the current workitems from others, eventually only with a specified role
|
134
|
+
def links_in(role=nil)
|
135
|
+
if role.nil?
|
136
|
+
Link.find(:to_wid => self.wid)
|
137
|
+
else
|
138
|
+
Link.find(:to_wid => self.wid, :role => role.to_s)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns all the fields name for the workitem
|
143
|
+
def field_names
|
144
|
+
JSON.parse(self.fields).keys.sort
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns the total sum, for a given +field+, from descendant and workitem itself
|
148
|
+
# Note: descendants = children with parent role
|
149
|
+
def total_on(field)
|
150
|
+
# return a past computed value if present
|
151
|
+
return self["_total_on_#{field}"] unless self["_total_on_#{field}"].nil?
|
152
|
+
|
153
|
+
# computing the total
|
154
|
+
total = self[field].nil? ? 0 : self[field]
|
155
|
+
children.each{|c| total += c.total_on(field) }
|
156
|
+
|
157
|
+
# saving (momoizing) for future invocations
|
158
|
+
self["_total_on_#{field}"] = total.to_f
|
159
|
+
self.save
|
160
|
+
|
161
|
+
total.to_f
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
|
168
|
+
# Returns workitem effective children (linked in with a parent role)
|
169
|
+
def children
|
170
|
+
tmp = [].to_set
|
171
|
+
project.parent_roles.each do |role|
|
172
|
+
tmp.merge(links_in(role).map{|lnk| lnk.from}) unless links_in(role).empty?
|
173
|
+
end
|
174
|
+
tmp.to_a
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Place
|
2
|
+
|
3
|
+
# Each WorkRecord ties a User to a WorkItem, with a +time_spent+, +date+ plus a +type+ and a +comment+
|
4
|
+
class WorkRecord < Ohm::Model
|
5
|
+
attribute :url
|
6
|
+
index :url
|
7
|
+
|
8
|
+
attribute :time_spent
|
9
|
+
attribute :date
|
10
|
+
attribute :type
|
11
|
+
attribute :comment
|
12
|
+
|
13
|
+
reference :user, User
|
14
|
+
reference :workitem, WorkItem
|
15
|
+
|
16
|
+
class << self; attr_accessor :rules end
|
17
|
+
@rules = {
|
18
|
+
'time_spent' => '//work-record/field[@id="timeSpent"]',
|
19
|
+
'date' => '//work-record/field[@id="date"]',
|
20
|
+
'type' => '//work-record/field[@id="type"]',
|
21
|
+
'comment' => '//work-record/field[@id="comment"]',
|
22
|
+
'user' => '//work-record/field[@id="user"]'
|
23
|
+
}
|
24
|
+
|
25
|
+
# Retrieve a specific workrecord data (time_spent, date, user, workitem) + (type, comment)
|
26
|
+
def retrieve
|
27
|
+
content = IO.readlines(url).join('')
|
28
|
+
doc = Nokogiri::XML(content)
|
29
|
+
self.class.rules.each_pair do |k,v|
|
30
|
+
tmp = doc.xpath(v)
|
31
|
+
|
32
|
+
case k
|
33
|
+
when 'time_spent'
|
34
|
+
self.send("#{k}=", to_hours(tmp[0].content))
|
35
|
+
when 'user'
|
36
|
+
self.send("#{k}=", User.find_by_name(tmp[0].content))
|
37
|
+
else
|
38
|
+
self.send("#{k}=", tmp[0].content) unless tmp[0].nil?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
self.workitem = WorkItem.find_by_wid(File.dirname(self.url).split(/\//).last)
|
43
|
+
|
44
|
+
Place.logger.info("saved workrecord #{self.url}")
|
45
|
+
|
46
|
+
self.save
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
end
|
data/lib/place.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'ohm'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'json'
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
|
9
|
+
require File.join(File.dirname(__FILE__), 'place', 'project')
|
10
|
+
require File.join(File.dirname(__FILE__), 'place', 'time_point')
|
11
|
+
require File.join(File.dirname(__FILE__), 'place', 'user')
|
12
|
+
require File.join(File.dirname(__FILE__), 'place', 'work_item')
|
13
|
+
require File.join(File.dirname(__FILE__), 'place', 'work_record')
|
14
|
+
require File.join(File.dirname(__FILE__), 'place', 'comment')
|
15
|
+
require File.join(File.dirname(__FILE__), 'place', 'link')
|
16
|
+
require File.join(File.dirname(__FILE__), 'place', 'participation')
|
17
|
+
|
18
|
+
|
19
|
+
module Place
|
20
|
+
VERSION = '0.1.0'
|
21
|
+
|
22
|
+
@conf = {}
|
23
|
+
|
24
|
+
def self.setup(repository_path)
|
25
|
+
@conf['base'] = repository_path
|
26
|
+
Ohm.connect
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.conf
|
30
|
+
@conf
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.logger
|
34
|
+
@log
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.logger_setup(filename=nil)
|
38
|
+
unless filename.nil?
|
39
|
+
@log = Logger.new(filename)
|
40
|
+
else
|
41
|
+
@log = Logger.new(STDERR)
|
42
|
+
end
|
43
|
+
@log.level = Logger::INFO
|
44
|
+
end
|
45
|
+
|
46
|
+
# Collect and retrieve all repo information, for each contained project
|
47
|
+
# If a project url is specified, it retrieves only info on this project
|
48
|
+
# BEWARE: each gather flush the whole database!
|
49
|
+
def gather!(project_url=nil)
|
50
|
+
start_time = Time.now.to_i
|
51
|
+
|
52
|
+
path = @conf['base'] # without ending '.polarion'
|
53
|
+
Ohm.flush
|
54
|
+
|
55
|
+
Project.collect_all(path)
|
56
|
+
User.collect_all(path)
|
57
|
+
|
58
|
+
unless project_url.nil?
|
59
|
+
prj = Project.find_by_url(project_url)
|
60
|
+
prj.retrieve
|
61
|
+
else
|
62
|
+
Project.all.each{|prj| prj.retrieve}
|
63
|
+
end
|
64
|
+
|
65
|
+
Ohm.redis.set 'updated_duration', Time.now.to_i - start_time
|
66
|
+
end
|
67
|
+
|
68
|
+
# Write last update timespent for the whole database
|
69
|
+
def updated!
|
70
|
+
Ohm.redis.set 'updated', Time.now.strftime("%d.%m.%Y %H:%M:%S")
|
71
|
+
end
|
72
|
+
|
73
|
+
def updated_at
|
74
|
+
Ohm.redis.get 'updated'
|
75
|
+
end
|
76
|
+
|
77
|
+
def updated?
|
78
|
+
not updated_at.nil?
|
79
|
+
end
|
80
|
+
|
81
|
+
def updated_duration
|
82
|
+
Ohm.redis.get 'updated_duration'
|
83
|
+
end
|
84
|
+
|
85
|
+
def reset!
|
86
|
+
Ohm.flush
|
87
|
+
end
|
88
|
+
|
89
|
+
# from duration to number of hours (1d == 8h)
|
90
|
+
def to_hours(duration)
|
91
|
+
return nil if duration.nil?
|
92
|
+
# TODO with a case statement or another regexp?
|
93
|
+
if duration =~ /(\d+)d\s(\d+)h/
|
94
|
+
days, hours = $1.to_f, $2.to_f
|
95
|
+
elsif duration =~ /(\d+)\/(\d+)d/
|
96
|
+
days = $1.to_f / $2.to_f
|
97
|
+
hours = 0
|
98
|
+
elsif duration =~ /(\d+)\/(\d+)h/
|
99
|
+
days = 0
|
100
|
+
hours = $1.to_f / $2.to_f
|
101
|
+
elsif duration =~ /(\d+)d/
|
102
|
+
days, hours = $1.to_f, 0
|
103
|
+
elsif duration =~ /(\d+)h/
|
104
|
+
days, hours = 0, $1.to_f
|
105
|
+
else
|
106
|
+
days, hours = 0, 0
|
107
|
+
end
|
108
|
+
|
109
|
+
(8*days + hours)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
data/place.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = "place"
|
4
|
+
s.version = "0.1.0"
|
5
|
+
s.summary = "Polarion LACE: Polarion items access in Ruby"
|
6
|
+
s.description = <<-EOF
|
7
|
+
Extract information from Polarion and turns it into easy manageable ruby objects.
|
8
|
+
|
9
|
+
Those objects are stored via Redis.
|
10
|
+
EOF
|
11
|
+
s.authors = ["Carlo Pecchia"]
|
12
|
+
s.email = ["info@carlopecchia.eu"]
|
13
|
+
s.homepage = "http://github.com/carlopecchia/place"
|
14
|
+
s.add_dependency('json')
|
15
|
+
s.add_dependency('ohm')
|
16
|
+
s.add_dependency('nokogiri')
|
17
|
+
s.files = ["Changelog", "LICENSE", "README.markdown", "Technical.markdown", "Rakefile", "lib/place.rb", "lib/place/project.rb", "lib/place/link.rb", "lib/place/participation.rb", "lib/place/user.rb", "lib/place/time_point.rb", "lib/place/work_item.rb", "lib/place/work_record.rb", "lib/place/comment.rb", "place.gemspec", "test/test_project.rb", "test/helper.rb", "test/test_workitem.rb", "test/test_time_point.rb", "test/test_place.rb", "test/test_comment.rb", "test/test_user.rb", "examples/sample-1.rb"]
|
18
|
+
end
|
19
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
$: << File.join(File.dirname(__FILE__), '.')
|
3
|
+
require 'helper.rb'
|
4
|
+
|
5
|
+
class CommentTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
Place.setup(File.expand_path(File.join(File.dirname(__FILE__), 'repo')))
|
9
|
+
Place.gather!
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
Ohm.flush
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_environment
|
17
|
+
Comment.all.each do |c|
|
18
|
+
assert_not_nil c.author
|
19
|
+
assert_not_nil c.workitem
|
20
|
+
assert_not_nil c.text
|
21
|
+
assert_not_nil c.created
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_find_by_cid
|
26
|
+
assert_not_nil Comment.find_by_cid('P-6#1')
|
27
|
+
assert_nil Comment.find_by_cid('P-6#z00')
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_parent
|
31
|
+
c = Comment.find_by_cid('P-6#2')
|
32
|
+
assert_not_nil c.parent
|
33
|
+
assert_equal Comment.find_by_cid('P-6#1'), c.parent
|
34
|
+
end
|
35
|
+
end
|
data/test/test_place.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
$: << File.join(File.dirname(__FILE__), '.')
|
3
|
+
require 'helper.rb'
|
4
|
+
|
5
|
+
class PlaceTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def teardown
|
8
|
+
Ohm.flush
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_gather
|
12
|
+
base = File.expand_path(File.join(File.dirname(__FILE__), 'repo'))
|
13
|
+
Place.setup(base)
|
14
|
+
Place.gather!
|
15
|
+
|
16
|
+
assert_equal 2, Project.all.size
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_duration
|
20
|
+
assert_equal 11, to_hours('1d 3h')
|
21
|
+
assert_equal 16, to_hours('2d')
|
22
|
+
assert_equal 3, to_hours('3h')
|
23
|
+
assert_equal 0.5, to_hours('1/2h')
|
24
|
+
assert_equal 4, to_hours('1/2d')
|
25
|
+
assert_equal 0, to_hours('')
|
26
|
+
assert_equal nil, to_hours(nil)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
$: << File.join(File.dirname(__FILE__), '.')
|
3
|
+
require 'helper.rb'
|
4
|
+
|
5
|
+
class ProjectTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@base = File.expand_path(File.join(File.dirname(__FILE__), 'repo'))
|
9
|
+
Place.setup(@base)
|
10
|
+
Place.gather!
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
Ohm.flush
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_collecting
|
18
|
+
assert_not_nil @base
|
19
|
+
assert_equal 2, Project.all.size
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_find_by_name
|
23
|
+
p = Project.find_by_name('P')
|
24
|
+
assert_equal 'P', p.name
|
25
|
+
assert_equal 'P', p.prefix
|
26
|
+
assert_not_nil p.description
|
27
|
+
|
28
|
+
p = Project.find_by_name 'NONE'
|
29
|
+
assert_nil p
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_find_by_url
|
33
|
+
p = Project.find_by_url(@base + '/P/.polarion')
|
34
|
+
assert_not_nil p
|
35
|
+
|
36
|
+
b = Project.find_by_url(@base + '/A/B/.polarion')
|
37
|
+
assert_not_nil b
|
38
|
+
|
39
|
+
n = Project.find_by_url(@base + '/none/.polarion')
|
40
|
+
assert_nil n
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_find_by_prefix
|
44
|
+
p = Project.find_by_prefix('P')
|
45
|
+
assert_not_nil p
|
46
|
+
|
47
|
+
p = Project.find_by_prefix('NONE')
|
48
|
+
assert_nil p
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_workitems
|
52
|
+
p = Project.find_by_name('P')
|
53
|
+
assert_not_nil p
|
54
|
+
|
55
|
+
assert p.workitems.all.size > 0
|
56
|
+
|
57
|
+
wi = p.workitems.all.first
|
58
|
+
assert_not_nil wi
|
59
|
+
assert_equal p, wi.project
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_roles
|
63
|
+
p = Project.find_by_name('P')
|
64
|
+
assert_not_nil p
|
65
|
+
|
66
|
+
assert p.roles.size > 0
|
67
|
+
assert p.roles.include?('project_admin')
|
68
|
+
assert !p.roles.include?('foo servant role')
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_users
|
72
|
+
p = Project.find_by_name('P')
|
73
|
+
assert_not_nil p
|
74
|
+
|
75
|
+
assert_equal 2, p.users.size
|
76
|
+
assert_equal 1, p.users('project_admin').size
|
77
|
+
|
78
|
+
u = User.find_by_name 'alice'
|
79
|
+
assert_not_nil u
|
80
|
+
assert_equal u, p.users('project_admin').first
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_parent_roles
|
84
|
+
p = Project.find_by_name('P')
|
85
|
+
assert_not_nil p
|
86
|
+
|
87
|
+
assert ! p.parent_roles.empty?
|
88
|
+
assert p.parent_roles.include?('parent')
|
89
|
+
assert p.parent_roles.include?('refines')
|
90
|
+
assert ! p.parent_roles.include?('depends')
|
91
|
+
|
92
|
+
# A/B doesn't have a local file for workitem-link-roles
|
93
|
+
b = Project.find_by_url(@base + '/A/B/.polarion')
|
94
|
+
assert_not_nil b
|
95
|
+
assert ! b.parent_roles.empty?
|
96
|
+
assert b.parent_roles.include?('parent')
|
97
|
+
assert_equal 1, b.parent_roles.size
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|