precedence 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,5 @@
1
+ = 0.6: Initial Release
2
+ Basic precedence network functionality:
3
+ * Earlist/latest start/finish
4
+ * Generating float and determining critical path
5
+ * Diagram generation via dot files
data/README ADDED
@@ -0,0 +1,63 @@
1
+ = Precedence
2
+
3
+ Precedence is a library that allows for the creation, manipulation and analysis
4
+ of precedence networks.
5
+
6
+ == Download and Install
7
+
8
+ Available as a RubyGem from Rubyforge. To install
9
+ $ gem install precedence
10
+ will fetch the latest gem from Rubyforge and install it.
11
+
12
+ Source can also be downloaded from http://rubyforge.org/projects/precedence.
13
+
14
+ == Example Usage
15
+ require('precedence')
16
+
17
+ # Set up network
18
+ net = Precedence::Network.new('Begin','End')
19
+ net.new_activity('act-1-1',3,'System specification')
20
+ net.new_activity('act-1-2',2,'Review')
21
+ net.new_activity('act-1-3',2,'System re-specification')
22
+ net.new_activity('act-2-1',3,'Test tool design')
23
+ net.new_activity('act-2-2',5,'Test tool implementation')
24
+ net.new_activity('act-3-1',3,'System design')
25
+ net.new_activity('act-3-2',12,'System implementation')
26
+ net.new_activity('act-2-3',10,'System testing')
27
+ net.connect('act-1-1','act-1-2')
28
+ net.connect('act-1-2','act-1-3')
29
+ net.connect('act-1-3','act-3-1')
30
+ net.connect('act-1-2','act-2-1')
31
+ net.connect('act-2-1','act-2-2')
32
+ net.connect('act-2-2','act-3-2')
33
+ net.connect('act-3-1','act-3-2')
34
+ net.connect('act-3-2','act-2-3')
35
+ net.fix_connections!
36
+
37
+ # Generate a diagram
38
+ File.open('network.dot',File::CREAT|File::TRUNC|File::WRONLY) do|file|
39
+ file.puts(net.to_dot)
40
+ end
41
+ system("dot","-Tpng","-onetwork.png","network.dot")
42
+
43
+ # Perform some analysis of the activities
44
+ activity = net.activities['act-1-2']
45
+ activity.on_critical_path? # => true
46
+ activity.earliest_start # => 3.0
47
+ activity.latest_finish # => 5.0
48
+ activity.total_float # => 0 - activities on the critical path have no float
49
+
50
+ == Documentation
51
+
52
+ The Precedence API online documentation is available at
53
+ http://precedence.rubyforge.org.
54
+
55
+ Refer to the CHANGELOG and TODO files for past changes and upcoming plans.
56
+
57
+ == Credits
58
+
59
+ Farrel Lifson <farrel@lifson.info>
60
+
61
+ == License
62
+
63
+ This software is made available under the BSD license.
data/TODO ADDED
@@ -0,0 +1,22 @@
1
+ = To Do
2
+
3
+ I plan on adding the following leading up to a 1.0 release
4
+
5
+ == 0.7
6
+
7
+ * Adding resource usage (money, coffee, bulldozers) to each activity.
8
+ * Saving and loading precedence networks (probably using YAML).
9
+
10
+ == 0.8
11
+
12
+ * Adding an 'each_day' method to the network that will return the set of
13
+ activities used on that day.
14
+ * Calculating daily resource usage and generating some graphs from that.
15
+
16
+ == 0.9
17
+
18
+ * Generating Gantt charts
19
+
20
+ == 1.0 and further
21
+
22
+ * Some probability based reasoning using Beta/Normal distributions.
@@ -0,0 +1,251 @@
1
+ module Precedence
2
+
3
+ # A representation of an activity in a precedence network. Each activity
4
+ # has a user specified reference, description and duration. When activities
5
+ # are connected (via the add_post/pre_activities functions) then various
6
+ # other properties of the activity can be determined such as it's earliest
7
+ # finish time, float and so on.
8
+ #
9
+
10
+ class Activity
11
+
12
+ # A textual description of the activity.
13
+ attr_accessor :description
14
+ # The time the activity will take to complete.
15
+ attr_accessor :duration
16
+ # A unique reference for this activity.
17
+ attr_reader :reference
18
+ # The collection of activites that are dependent on the completion of
19
+ # this activity.
20
+ attr_reader :post_activities
21
+ # The collection of activities that this activity depends on before it
22
+ # can begin executing.
23
+ attr_reader :pre_activities
24
+
25
+ # Creates a new activity. The post_activities and pre_activities
26
+ # collections should contain the activities themselves and not their
27
+ # references.
28
+ #
29
+ # *Note*: When assigning a reference for an activity 'start' and
30
+ # 'finish' are reserved for internal usage.
31
+ def initialize(reference,duration=0,description="",post_activities=[],
32
+ pre_activities=[])
33
+ if (reference.to_s == 'start') || (reference.to_s == 'finish')
34
+ raise "The reference '#{reference.to_s}' is reserved"
35
+ end
36
+ @reference = reference.to_s
37
+ @description = description.to_s
38
+ @duration = duration.to_f
39
+ @post_activities = post_activities.to_a
40
+ @pre_activities = pre_activities.to_a
41
+ end
42
+
43
+ # Adds the activities in the parameter list to the post_activities
44
+ # collection of this activity and also adds this activity to the
45
+ # pre_activities collection of each of the activities.
46
+ def add_post_activities(*post_activities)
47
+ post_activities.each do |activity|
48
+ activity.register_pre_activity(self)
49
+ register_post_activity(activity)
50
+ end
51
+ end
52
+
53
+ # Adds the activities in the parameter list to the pre_activities
54
+ # collection of this activity and also adds this activity to the
55
+ # post_activities collection of each of the activities.
56
+ def add_pre_activities(*pre_activities)
57
+ pre_activities.each do |activity|
58
+ activity.register_post_activity(self)
59
+ register_pre_activity(activity)
60
+ end
61
+ end
62
+
63
+ # Removes the list of activities from the post_activities collection of
64
+ # the activity.
65
+ def remove_post_activities(*post_activities)
66
+ post_activities.each do |activity|
67
+ activity.deregister_pre_activity(self)
68
+ deregister_post_activity(activity)
69
+ end
70
+ end
71
+
72
+ # Removes the list of activities from the pre_activities collection of
73
+ # the activity.
74
+ def remove_pre_activities(*post_activities)
75
+ post_activities.each do |activity|
76
+ activity.deregister_post_activity(self)
77
+ deregister_pre_activity(activity)
78
+ end
79
+ end
80
+
81
+ # The earliest possible time this activity can finish.
82
+ def earliest_finish
83
+ return @duration + earliest_start
84
+ end
85
+
86
+ # The earliest possible time this activity can start.
87
+ def earliest_start
88
+ unless @pre_activities.empty?
89
+ (@pre_activities.max do |a,b|
90
+ a.earliest_finish <=> b.earliest_finish
91
+ end).earliest_finish
92
+ else
93
+ 0.0
94
+ end
95
+ end
96
+
97
+ # The latest possible time this activity can start so as not to delay
98
+ # any dependent activities.
99
+ def latest_start
100
+ return latest_finish - @duration
101
+ end
102
+
103
+ # The latest possible time this activity can finish so as not to delay
104
+ # any dependent activities.
105
+ def latest_finish
106
+ unless @post_activities.empty?
107
+ (@post_activities.min do |a,b|
108
+ a.latest_start <=> b.latest_start
109
+ end).latest_start
110
+ else
111
+ return earliest_finish
112
+ end
113
+ end
114
+
115
+ # The maximum earliest finish of this activities pre-activities.
116
+ def pre_activities_max_earliest_finish
117
+ unless @pre_activities.empty?
118
+ (@pre_activities.max do |a,b|
119
+ a.earliest_finish <=> b.earliest_finish
120
+ end).earliest_finish
121
+ else
122
+ return 0
123
+ end
124
+ end
125
+
126
+ # The minimum earliest start of this activities post-activities.
127
+ def post_activities_min_earliest_start
128
+ unless @post_activities.empty?
129
+ (@post_activities.min do |a,b|
130
+ a.earliest_start <=> b.earliest_start
131
+ end).earliest_start
132
+ else
133
+ return latest_finish
134
+ end
135
+ end
136
+
137
+ # If the activity is on the critical path returns true, returns false
138
+ # otherwise.
139
+ def on_critical_path?
140
+ if earliest_finish == latest_finish
141
+ return true
142
+ else
143
+ return false
144
+ end
145
+ end
146
+
147
+ # The amount of float this activity has such that it does not delay
148
+ # the completion of the entire precedence network.
149
+ def total_float
150
+ return latest_finish - earliest_finish
151
+ end
152
+
153
+ # The amount of float this activity has if all preceding and succeeding
154
+ # activities start as early as possible.
155
+ #
156
+ # *Note*: In almost all practical cases this is the same as if all
157
+ # preceding and successing activities start as lates as possible and so
158
+ # no late_float method is defined.
159
+ def early_float
160
+ return post_activities_min_earliest_start -
161
+ pre_activities_max_earliest_finish - @duration
162
+ end
163
+
164
+ # Register this activity as a post-activity on the parameter.
165
+ def register_post_activity(activity) #:nodoc:
166
+ unless (@post_activities.detect do |post_activity|
167
+ activity.reference == post_activity.reference
168
+ end)
169
+ @post_activities << activity
170
+ end
171
+ end
172
+
173
+ # Register this activity as a pre-activity on the parameter.
174
+ def register_pre_activity(activity) #:nodoc:
175
+ unless (@pre_activities.detect do |pre_activity|
176
+ activity.reference == pre_activity.reference
177
+ end)
178
+ @pre_activities << activity
179
+ end
180
+ end
181
+
182
+ # Deregister this activity as a post-activity on the parameter.
183
+ def deregister_post_activity(activity) #:nodoc:
184
+ if (@post_activities.detect do |post_activity|
185
+ activity.reference == post_activity.reference
186
+ end)
187
+ @post_activities.delete(activity)
188
+ end
189
+ end
190
+
191
+ # Deregister this activity as a pre-activity on the parameter.
192
+ def deregister_pre_activity(activity) #:nodoc:
193
+ if (@pre_activities.detect do |pre_activity|
194
+ activity.reference == pre_activity.reference
195
+ end)
196
+ @pre_activities.delete(activity)
197
+ end
198
+ end
199
+
200
+ # Returns this activity in an Array object.
201
+ def to_a #:nodoc:
202
+ return [self]
203
+ end
204
+
205
+ # Redefines the inspect method.
206
+ def inspect #:nodoc:
207
+ "#{@reference}(#{@duration})"
208
+ end
209
+
210
+ # Redefines the to_s method
211
+ def to_s #:nodoc:
212
+ return "Reference: #{@reference}\n"+
213
+ "Description: #{@description}\n"+
214
+ "Duration: #{@duration}"+
215
+ ("\nDepends on:\n " unless @pre_activities.empty?).to_s +
216
+ (@pre_activities.collect do |activity|
217
+ activity.reference
218
+ end).join(',')
219
+ end
220
+
221
+ # Priviliege settings
222
+ protected :register_post_activity, :register_pre_activity,
223
+ :deregister_post_activity, :deregister_pre_activity
224
+
225
+ private :pre_activities_max_earliest_finish,
226
+ :post_activities_min_earliest_start
227
+
228
+
229
+ end
230
+
231
+ # Represents the special case of a start or finish activity.
232
+ class StartFinishActivity < Precedence::Activity #:nodoc:
233
+ def initialize(reference,description=nil)
234
+ if (reference.to_s != 'start') and (reference.to_s != 'finish')
235
+ raise "StartFinishActivity reference may only be set to"+
236
+ "'start' or 'finish'. Given reference was '#{reference}'."
237
+ end
238
+
239
+ @reference = reference.to_s
240
+ if description
241
+ @description = description.to_s
242
+ else
243
+ @description = reference.to_s
244
+ end
245
+ @duration = duration.to_f
246
+ @post_activities = post_activities.to_a
247
+ @pre_activities = pre_activities.to_a
248
+ end
249
+ end
250
+ end
251
+
@@ -0,0 +1,187 @@
1
+ require('erb')
2
+
3
+ module Precedence
4
+
5
+ # An overwritten Hash that has all keys as strings
6
+ class ActivityHash < Hash #:nodoc:
7
+ def[]=(ref,value)
8
+ super(ref.to_s, value)
9
+ end
10
+
11
+ def[](ref)
12
+ super(ref.to_s)
13
+ end
14
+ end
15
+
16
+ # Represents an entire precedence network.
17
+ class Network
18
+
19
+ # A hashed collection of all the activities in the network. The index
20
+ # is the reference of the activity.
21
+ attr_reader :activities
22
+
23
+ # Initialises the precedence network. The descriptions for the start
24
+ # and finish activities can be set in the parameters. The start and
25
+ # finish activities of the network have as references 'start' and
26
+ # 'finish' respectively (and as such these references are reserved)
27
+ # and a duration of 0.
28
+ def initialize(startDescription='Start',
29
+ finishDescription='Finish')
30
+ @start = Precedence::StartFinishActivity.new('start',
31
+ startDescription)
32
+ @finish = Precedence::StartFinishActivity.new('finish',
33
+ finishDescription)
34
+ @activities = Precedence::ActivityHash.new
35
+ @activities[@start.reference] = @start
36
+ @activities[@finish.reference] = @finish
37
+ end
38
+
39
+ # Creates a new activity and adds it to the precedence network. The
40
+ # reference is the only mandatory parameter. The post_activities and
41
+ # pre_activities parameters should contain the references to activities
42
+ # already defined within the network and not the actual
43
+ # Precedence::Activity object.
44
+ #
45
+ # Exapmple usage:
46
+ # precNetwork.new_activity('a2',3,'Activity2',['a1'])
47
+ # will create a new activity in the network with referecne 'a2' and with
48
+ # activity 'a1' as a post-activity.
49
+ def new_activity(reference,duration=0,description="",
50
+ post_activities=[],pre_activities=[])
51
+
52
+ post_activities.each do |post_activity_ref|
53
+ unless @activities[post_activity_ref]
54
+ raise "Post-activity with reference #{post_activity_ref} "+
55
+ "was not found."
56
+ end
57
+ end
58
+
59
+ pre_activities.each do |pre_activity_ref|
60
+ unless @activities[pre_activity_ref]
61
+ raise "Pre-activity with reference #{pre_activity_ref} "+
62
+ "was not found."
63
+ end
64
+ end
65
+
66
+ activity =
67
+ Precedence::Activity.new(reference,duration,description)
68
+
69
+ post_activities.each do |post_activity_ref|
70
+ activity.add_post_activities(@activities[post_activity_ref])
71
+ end
72
+
73
+ pre_activities.each do |pre_activity_ref|
74
+ activity.add_pre_activities(@activities[pre_activity_ref])
75
+ end
76
+
77
+ @activities[reference]=activity
78
+ end
79
+
80
+ # Adds a Precedence::Activity object to the network. This should be a
81
+ # single activity (no pre- or post-activities should be referenced from
82
+ # it) and it's reference should not exist in the network.
83
+ #
84
+ # Example usage:
85
+ # activity = Precedence::Activity.new('a1',1,'Activity 1')
86
+ # precNetwork.add_activity(activity)
87
+ # will add the existing activity 'a1' to the network.
88
+ def add_activity(activity)
89
+ if @activities[activity.reference]
90
+ raise "Activity #{activity.reference} already exists in the "+
91
+ "network."
92
+ end
93
+
94
+ unless activity.post_activities == []
95
+ raise "Can not add an activity with post activities."
96
+ end
97
+
98
+ unless activity.pre_activities == []
99
+ raise "Can not add an activity with pre activities."
100
+ end
101
+
102
+ @activities[activity.reference] = activity
103
+
104
+ end
105
+
106
+ # Connects two or more activities together. The pre_ref activity will
107
+ # become a pre-activity to all the activities referenced in the
108
+ # post_refs parameter.
109
+ #
110
+ # Example uysage:
111
+ # precNetwork.connect(:h1,:h2,:h3)
112
+ # will add activity 'h1' as a pre-activity to activities 'h2' and 'h3'.
113
+ def connect(pre_ref,*post_refs)
114
+ unless @activities[pre_ref]
115
+ raise "Pre-activity with reference #{pre_ref} "+
116
+ "was not found."
117
+ end
118
+
119
+ post_refs.each do |post_ref|
120
+ unless @activities[post_ref]
121
+ raise "Post-activity with reference #{post_ref} "+
122
+ "was not found."
123
+ end
124
+ end
125
+
126
+ post_refs.each do |post_ref|
127
+ @activities[pre_ref].add_post_activities(@activities[post_ref])
128
+ end
129
+ end
130
+
131
+ # Ensures that the network is properly connected by connecting any
132
+ # activity without pre-activities to the start node and to the finish
133
+ # node if it has no post-activities.
134
+ def fix_connections!
135
+ @activities.each do |ref,activity|
136
+ next if (ref == 'start' or ref == 'finish')
137
+ if activity.pre_activities.size == 0
138
+ connect(:start,ref)
139
+ end
140
+
141
+ if activity.post_activities.size == 0
142
+ connect(ref,:finish)
143
+ end
144
+ end
145
+ end
146
+
147
+ # Returns the rdot (Ruby embedded in a dot file) template that is used
148
+ # by default when generating the precedence network diagrams.
149
+ def get_rdot_template
150
+ return <<EOS
151
+ /* Generated by Precedence on <%= Time.now.to_s %> */
152
+ digraph network {
153
+ rankdir=LR;
154
+ node [shape=record];
155
+
156
+ /* Activities */
157
+ <% @activities.each do |ref,activity| %>
158
+ <% case ref == 'start'
159
+ when 'start'%>
160
+ "<%= ref %>" [label="<%= activity.description %>"];
161
+ <% when 'finish' %>
162
+ "<%= ref %>" [label="{<%= activity.description%>|<%= activity.earliest_finish %>}"];
163
+ <% else %>
164
+ "<%= ref %>" [label="<%=ref%>|{{<%=activity.earliest_start%>|<%=activity.latest_start%>}|{<%=activity.description%>|{<%=activity.total_float%>|<%=activity.early_float%>}}|{<%=activity.earliest_finish%>|<%=activity.latest_finish%>}}|<%=activity.duration%>"];
165
+ <% end %>
166
+ <% end %>
167
+
168
+ /* Dependencies */
169
+ <% @activities.each do |ref,activity|%>
170
+ <% activity.post_activities.each do |post_activity| %>
171
+ "<%= activity.reference %>" -> "<%= post_activity.reference %>" <% if (activity.on_critical_path? and post_activity.on_critical_path?)%>[style=bold];<% else %>;<% end %>
172
+ <% end %>
173
+ <% end %>
174
+ }
175
+ EOS
176
+ end
177
+
178
+ # Returns a dot file capable of being rendered by the Dot graph renderer
179
+ # (available from GraphViz http://www.graphviz.org).
180
+ def to_dot(template=nil)
181
+ unless template
182
+ template = get_rdot_template
183
+ end
184
+ return ERB.new(template).result(binding)
185
+ end
186
+ end
187
+ end
data/lib/precedence.rb ADDED
@@ -0,0 +1,2 @@
1
+ require('precedence/activity')
2
+ require('precedence/network')
@@ -0,0 +1,343 @@
1
+ require('lib/precedence/activity')
2
+
3
+ class TC_Activity < Test::Unit::TestCase #:nodoc:
4
+
5
+ def setup
6
+ @activity1 = Precedence::Activity.new(:activity1,1,"Activity One")
7
+ @activity2 = Precedence::Activity.new(:activity2,2,"Activity Two")
8
+ @activity3 = Precedence::Activity.new(:activity3,3,"Activity Three")
9
+ @activity4 = Precedence::Activity.new(:activity4,4,"Activity Four")
10
+ @activity5 = Precedence::Activity.new(:activity5,5,"Activity Five")
11
+ @activity6 = Precedence::Activity.new(:activity6,6,"Activity Six")
12
+ @activity7 = Precedence::Activity.new(:activity7,7,"Activity Seven")
13
+ @activity8 = Precedence::Activity.new(:activity8,8,"Activity Eight")
14
+ end
15
+
16
+ def test_initialize
17
+
18
+ assert_raises(RuntimeError) do
19
+ Precedence::Activity.new(:start)
20
+ end
21
+ assert_raises(RuntimeError) do
22
+ Precedence::Activity.new(:finish)
23
+ end
24
+
25
+ prec = nil
26
+ assert_nothing_raised do
27
+ prec = Precedence::Activity.new(:reference)
28
+ end
29
+ assert_equal(:reference.to_s, prec.reference)
30
+ assert_equal("",prec.description)
31
+ assert_equal(0,prec.duration)
32
+ assert_equal([],prec.post_activities)
33
+ assert_equal([],prec.pre_activities)
34
+
35
+ assert_nothing_raised do
36
+ prec = Precedence::Activity.new(:reference,1)
37
+ end
38
+ assert_equal(:reference.to_s, prec.reference)
39
+ assert_equal("", prec.description)
40
+ assert_equal(1,prec.duration)
41
+ assert_equal([],prec.post_activities)
42
+ assert_equal([],prec.pre_activities)
43
+
44
+ assert_nothing_raised do
45
+ prec = Precedence::Activity.new(:reference,1,"description")
46
+ end
47
+ assert_equal(:reference.to_s, prec.reference)
48
+ assert_equal("description", prec.description)
49
+ assert_equal(1,prec.duration)
50
+ assert_equal([],prec.post_activities)
51
+ assert_equal([],prec.pre_activities)
52
+
53
+ prec2 = nil
54
+ assert_nothing_raised do
55
+ prec2 = Precedence::Activity.new(:reference,1,"description",prec)
56
+ end
57
+ assert_equal(:reference.to_s, prec2.reference)
58
+ assert_equal("description", prec2.description)
59
+ assert_equal(1,prec2.duration)
60
+ assert_equal([prec],prec2.post_activities)
61
+ assert_equal([],prec.pre_activities)
62
+
63
+ prec3 = nil
64
+ assert_nothing_raised do
65
+ prec3 = Precedence::Activity.new(:reference,1,"description")
66
+ end
67
+ assert_nothing_raised do
68
+ prec2 = Precedence::Activity.new(:reference,1,"description",prec,
69
+ prec3)
70
+ end
71
+ assert_equal(:reference.to_s, prec2.reference)
72
+ assert_equal("description", prec2.description)
73
+ assert_equal(1,prec2.duration)
74
+ assert_equal([prec],prec2.post_activities)
75
+ assert_equal([prec3],prec2.pre_activities)
76
+ end
77
+
78
+ def test_to_a
79
+ assert_equal([@activity1],@activity1.to_a)
80
+ end
81
+
82
+ def test_add_post_activities
83
+ assert_nothing_raised do
84
+ @activity1.add_post_activities(@activity2)
85
+ end
86
+ assert_equal(@activity2.to_a,@activity1.post_activities)
87
+ assert_equal(@activity1.to_a,@activity2.pre_activities)
88
+
89
+ assert_nothing_raised do
90
+ @activity1.add_post_activities(@activity2)
91
+ end
92
+ assert_equal(1,@activity1.post_activities.size)
93
+ assert_equal(1,@activity2.pre_activities.size)
94
+
95
+ assert_nothing_raised do
96
+ @activity1.add_post_activities(@activity3,@activity4)
97
+ end
98
+ assert_equal([@activity2,@activity3,@activity4],
99
+ @activity1.post_activities)
100
+ assert_equal(@activity1.to_a,@activity3.pre_activities)
101
+ assert_equal(@activity1.to_a,@activity4.pre_activities)
102
+ end
103
+
104
+ def test_add_pre_activities
105
+ assert_nothing_raised do
106
+ @activity1.add_pre_activities(@activity2)
107
+ end
108
+ assert_equal(@activity2.to_a,@activity1.pre_activities)
109
+ assert_equal(@activity1.to_a,@activity2.post_activities)
110
+
111
+ assert_nothing_raised do
112
+ @activity1.add_pre_activities(@activity2)
113
+ end
114
+ assert_equal(1,@activity1.pre_activities.size)
115
+ assert_equal(1,@activity2.post_activities.size)
116
+
117
+ assert_nothing_raised do
118
+ @activity1.add_pre_activities(@activity3,@activity4)
119
+ end
120
+ assert_equal([@activity2,@activity3,@activity4],
121
+ @activity1.pre_activities)
122
+ assert_equal(@activity1.to_a,@activity3.post_activities)
123
+ assert_equal(@activity1.to_a,@activity4.post_activities)
124
+ end
125
+
126
+ def test_remove_post_activities
127
+ @activity1.add_post_activities(@activity2)
128
+ assert_nothing_raised do
129
+ @activity1.remove_post_activities(@activity2)
130
+ end
131
+ assert_equal([],@activity1.post_activities)
132
+ assert_equal([],@activity2.pre_activities)
133
+
134
+ @activity1.add_post_activities(@activity2,@activity3)
135
+ assert_nothing_raised do
136
+ @activity1.remove_post_activities(@activity2,@activity3)
137
+ end
138
+ assert_equal([],@activity1.post_activities)
139
+ assert_equal([],@activity2.pre_activities)
140
+ assert_equal([],@activity3.pre_activities)
141
+ end
142
+
143
+ def test_remove_pre_activities
144
+ @activity1.add_pre_activities(@activity2)
145
+ assert_nothing_raised do
146
+ @activity1.remove_pre_activities(@activity2)
147
+ end
148
+ assert_equal([],@activity1.pre_activities)
149
+ assert_equal([],@activity2.post_activities)
150
+
151
+ @activity1.add_pre_activities(@activity2,@activity3)
152
+ assert_nothing_raised do
153
+ @activity1.remove_pre_activities(@activity2,@activity3)
154
+ end
155
+ assert_equal([],@activity1.pre_activities)
156
+ assert_equal([],@activity2.post_activities)
157
+ assert_equal([],@activity3.post_activities)
158
+ end
159
+
160
+ def test_earliest_finish
161
+ assert_equal(1,@activity1.earliest_finish)
162
+
163
+ # a1-a2
164
+ @activity1.add_post_activities(@activity2)
165
+ assert_equal(3,@activity2.earliest_finish)
166
+
167
+ # a1-a3-a4
168
+ # '-a2-'
169
+ @activity1.add_post_activities(@activity3)
170
+ @activity3.add_post_activities(@activity4)
171
+ @activity2.add_post_activities(@activity4)
172
+ assert_equal(8,@activity4.earliest_finish)
173
+ end
174
+
175
+ def test_latest_finish
176
+ assert_equal(1,@activity1.latest_finish)
177
+
178
+ # a1-a2
179
+ @activity1.add_post_activities(@activity2)
180
+ assert_equal(3,@activity2.latest_finish)
181
+
182
+ # a1-a3-a4
183
+ # '-a2-'
184
+ @activity1.add_post_activities(@activity3)
185
+ @activity3.add_post_activities(@activity4)
186
+ @activity2.add_post_activities(@activity4)
187
+ assert_equal(2,@activity2.latest_start)
188
+ end
189
+
190
+ def test_on_critical_path?
191
+ # a1-a2
192
+ @activity1.add_post_activities(@activity2)
193
+ assert_equal(true,@activity1.on_critical_path?)
194
+ assert_equal(true,@activity2.on_critical_path?)
195
+
196
+ # a1-a3-a4
197
+ # '-a2-'
198
+ @activity1.add_post_activities(@activity3)
199
+ @activity3.add_post_activities(@activity4)
200
+ @activity2.add_post_activities(@activity4)
201
+ assert_equal(true,@activity1.on_critical_path?)
202
+ assert_equal(false,@activity2.on_critical_path?)
203
+ assert_equal(true,@activity3.on_critical_path?)
204
+ assert_equal(true,@activity4.on_critical_path?)
205
+ end
206
+
207
+ def test_total_float
208
+ # a1-a3-a4
209
+ # '-a2-'
210
+ @activity1.add_post_activities(@activity2)
211
+ @activity1.add_post_activities(@activity3)
212
+ @activity3.add_post_activities(@activity4)
213
+ @activity2.add_post_activities(@activity4)
214
+
215
+ assert_equal(0,@activity1.total_float)
216
+ assert_equal(0,@activity3.total_float)
217
+ assert_equal(0,@activity4.total_float)
218
+ assert_equal(1,@activity2.total_float)
219
+ end
220
+
221
+ def test_early_total_floats
222
+ # a1-a3---.
223
+ # a2-a4-a6-a7
224
+ # 'a5-a7'
225
+ @activity1.duration = 1
226
+ @activity2.duration = 3
227
+ @activity3.duration = 2
228
+ @activity4.duration = 4
229
+ @activity5.duration = 2
230
+ @activity6.duration = 1
231
+ @activity7.duration = 2
232
+ @activity8.duration = 4
233
+
234
+ @activity1.add_post_activities(@activity3)
235
+ @activity2.add_post_activities(@activity4,@activity5)
236
+ @activity3.add_post_activities(@activity8)
237
+ @activity4.add_post_activities(@activity6)
238
+ @activity5.add_post_activities(@activity7)
239
+ @activity6.add_post_activities(@activity8)
240
+ @activity7.add_post_activities(@activity8)
241
+
242
+ assert_equal(true,@activity2.on_critical_path?)
243
+ assert_equal(true,@activity4.on_critical_path?)
244
+ assert_equal(true,@activity6.on_critical_path?)
245
+ assert_equal(true,@activity8.on_critical_path?)
246
+
247
+ assert_equal(false,@activity1.on_critical_path?)
248
+ assert_equal(false,@activity3.on_critical_path?)
249
+ assert_equal(false,@activity5.on_critical_path?)
250
+ assert_equal(false,@activity7.on_critical_path?)
251
+
252
+ assert_equal(0,@activity1.earliest_start)
253
+ assert_equal(5,@activity1.latest_start)
254
+ assert_equal(1,@activity1.earliest_finish)
255
+ assert_equal(6,@activity1.latest_finish)
256
+ assert_equal(5,@activity1.total_float)
257
+ assert_equal(0,@activity1.early_float)
258
+
259
+ assert_equal(0,@activity2.earliest_start)
260
+ assert_equal(0,@activity2.latest_start)
261
+ assert_equal(3,@activity2.earliest_finish)
262
+ assert_equal(3,@activity2.latest_finish)
263
+ assert_equal(0,@activity2.total_float)
264
+ assert_equal(0,@activity2.early_float)
265
+
266
+ assert_equal(1,@activity3.earliest_start)
267
+ assert_equal(6,@activity3.latest_start)
268
+ assert_equal(3,@activity3.earliest_finish)
269
+ assert_equal(8,@activity3.latest_finish)
270
+ assert_equal(5,@activity3.total_float)
271
+ assert_equal(5,@activity3.early_float)
272
+
273
+ assert_equal(3,@activity4.earliest_start)
274
+ assert_equal(3,@activity4.latest_start)
275
+ assert_equal(7,@activity4.earliest_finish)
276
+ assert_equal(7,@activity4.latest_finish)
277
+ assert_equal(0,@activity4.total_float)
278
+ assert_equal(0,@activity4.early_float)
279
+
280
+ assert_equal(3,@activity5.earliest_start)
281
+ assert_equal(4,@activity5.latest_start)
282
+ assert_equal(5,@activity5.earliest_finish)
283
+ assert_equal(6,@activity5.latest_finish)
284
+ assert_equal(1,@activity5.total_float)
285
+ assert_equal(0,@activity5.early_float)
286
+
287
+ assert_equal(7,@activity6.earliest_start)
288
+ assert_equal(7,@activity6.latest_start)
289
+ assert_equal(8,@activity6.earliest_finish)
290
+ assert_equal(8,@activity6.latest_finish)
291
+ assert_equal(0,@activity6.total_float)
292
+ assert_equal(0,@activity6.early_float)
293
+
294
+ assert_equal(5,@activity7.earliest_start)
295
+ assert_equal(6,@activity7.latest_start)
296
+ assert_equal(7,@activity7.earliest_finish)
297
+ assert_equal(8,@activity7.latest_finish)
298
+ assert_equal(1,@activity7.total_float)
299
+ assert_equal(1,@activity7.early_float)
300
+
301
+ assert_equal(8,@activity8.earliest_start)
302
+ assert_equal(8,@activity8.latest_start)
303
+ assert_equal(12,@activity8.earliest_finish)
304
+ assert_equal(12,@activity8.latest_finish)
305
+ assert_equal(0,@activity8.total_float)
306
+ assert_equal(0,@activity8.early_float)
307
+ end
308
+
309
+ def test_to_s
310
+ assert_equal("Reference: activity1\nDescription: Activity One\n"+
311
+ "Duration: 1.0",@activity1.to_s)
312
+
313
+ @activity1.add_pre_activities(@activity2,@activity3)
314
+ assert_equal("Reference: activity1\nDescription: Activity One\n"+
315
+ "Duration: 1.0\nDepends on:\n activity2,activity3",@activity1.to_s)
316
+
317
+
318
+ end
319
+ end
320
+
321
+ class TC_StartFinishActivity < Test::Unit::TestCase
322
+ def test_new
323
+ assert_raises(RuntimeError) do
324
+ Precedence::StartFinishActivity.new('bogus')
325
+ end
326
+
327
+ assert_nothing_raised do
328
+ Precedence::StartFinishActivity.new('start')
329
+ end
330
+
331
+ assert_nothing_raised do
332
+ Precedence::StartFinishActivity.new('finish')
333
+ end
334
+
335
+ assert_nothing_raised do
336
+ Precedence::StartFinishActivity.new(:finish)
337
+ end
338
+
339
+ assert_nothing_raised do
340
+ Precedence::StartFinishActivity.new(:start)
341
+ end
342
+ end
343
+ end
@@ -0,0 +1,208 @@
1
+ require('lib/precedence/network')
2
+
3
+ class TC_ActivityHash < Test::Unit::TestCase
4
+ def test_all
5
+ activityHash = nil
6
+ assert_nothing_raised do
7
+ activityHash = Precedence::ActivityHash.new
8
+ end
9
+
10
+ assert_nothing_raised do
11
+ activityHash[:test] = "test"
12
+ end
13
+ assert_equal("test",activityHash[:test])
14
+ assert_equal("test",activityHash['test'])
15
+ end
16
+ end
17
+
18
+ class TC_Network < Test::Unit::TestCase
19
+
20
+ def setup
21
+ @network = Precedence::Network.new
22
+ @activity1 = Precedence::Activity.new('a1',1,'Activity1')
23
+ @activity2 = Precedence::Activity.new('a2',1,'Activity2')
24
+ @activity3 = Precedence::Activity.new('a3',1,'Activity3')
25
+ end
26
+
27
+ def test_initialize
28
+ net = nil
29
+ assert_nothing_raised do
30
+ net = Precedence::Network.new
31
+ end
32
+ assert_equal(2,net.activities.size)
33
+ assert_equal('start',net.activities[:start].reference)
34
+ assert_equal('finish',net.activities[:finish].reference)
35
+ assert_equal('Start',net.activities[:start].description)
36
+ assert_equal('Finish',net.activities[:finish].description)
37
+
38
+ assert_nothing_raised do
39
+ net = Precedence::Network.new('Begin','End')
40
+ end
41
+ assert_equal('Begin',net.activities[:start].description)
42
+ assert_equal('End',net.activities[:finish].description)
43
+ end
44
+
45
+ def test_new_activity
46
+ assert_raises(ArgumentError) do
47
+ @network.new_activity()
48
+ end
49
+
50
+ assert_nothing_raised do
51
+ @network.new_activity('a1',1)
52
+ end
53
+ assert_equal(3,@network.activities.size)
54
+ assert_equal('a1',@network.activities['a1'].reference)
55
+ assert_equal(1,@network.activities['a1'].duration)
56
+
57
+ assert_nothing_raised do
58
+ @network.new_activity('a2',2,'Activity Two')
59
+ end
60
+ assert_equal(4,@network.activities.size)
61
+ assert_equal('a2',@network.activities['a2'].reference)
62
+ assert_equal('Activity Two',@network.activities['a2'].description)
63
+ assert_equal(2,@network.activities['a2'].duration)
64
+
65
+ assert_nothing_raised do
66
+ @network.new_activity('a3',3,'Activity Three')
67
+ end
68
+ assert_equal(5,@network.activities.size)
69
+ assert_equal('a3',@network.activities['a3'].reference)
70
+ assert_equal('Activity Three',@network.activities['a3'].description)
71
+ assert_equal(3,@network.activities['a3'].duration)
72
+
73
+ assert_nothing_raised do
74
+ @network.new_activity('a4',4,'Activity Four',['a3'])
75
+ end
76
+ assert_equal(6,@network.activities.size)
77
+ assert_equal('a4',@network.activities['a4'].reference)
78
+ assert_equal('Activity Four',@network.activities['a4'].description)
79
+ assert_equal(4,@network.activities['a4'].duration)
80
+ assert_equal(1,@network.activities['a4'].post_activities.size)
81
+ assert_equal('a3',@network.activities['a4'].post_activities[0].reference)
82
+ assert_equal('a4',@network.activities['a3'].pre_activities[0].reference)
83
+
84
+ assert_nothing_raised do
85
+ @network.new_activity('a5',5,'Activity Five',['a3'],['a4'])
86
+ end
87
+ assert_equal(7,@network.activities.size)
88
+ assert_equal('a5',@network.activities['a5'].reference)
89
+ assert_equal('Activity Five',@network.activities['a5'].description)
90
+ assert_equal(5,@network.activities['a5'].duration)
91
+ assert_equal(1,@network.activities['a5'].post_activities.size)
92
+ assert_equal(1,@network.activities['a5'].pre_activities.size)
93
+ assert_equal('a3',@network.activities['a5'].post_activities[0].reference)
94
+ assert_equal('a5',@network.activities['a3'].pre_activities[1].reference)
95
+ assert_equal('a4',@network.activities['a5'].pre_activities[0].reference)
96
+ assert_equal('a5',@network.activities['a4'].post_activities[1].reference)
97
+
98
+ assert_raises(RuntimeError) do
99
+ @network.new_activity('a6',6,'Activity Six',['a7'])
100
+ end
101
+
102
+ assert_raises(RuntimeError) do
103
+ @network.new_activity('a6',6,'Activity Six',['a5','a7'])
104
+ end
105
+ assert_equal(nil,
106
+ @network.activities['a5'].pre_activities.detect do |activity|
107
+ activity.reference == 'a6'
108
+ end)
109
+
110
+ assert_raises(RuntimeError) do
111
+ @network.new_activity('a6',6,'Activity Six',[],['a5','a7'])
112
+ end
113
+ assert_equal(nil,
114
+ @network.activities['a5'].post_activities.detect do |activity|
115
+ activity.reference == 'a6'
116
+ end)
117
+ end
118
+
119
+ def test_add_activity
120
+ assert_nothing_raised do
121
+ @network.add_activity(@activity1)
122
+ end
123
+ assert_equal(3,@network.activities.size)
124
+ assert_equal(@activity1,@network.activities[@activity1.reference])
125
+
126
+ assert_raises(RuntimeError) do
127
+ @network.add_activity(@activity1)
128
+ end
129
+
130
+ @network = Precedence::Network.new
131
+ @activity1.add_post_activities(@activity2)
132
+ assert_raises(RuntimeError) do
133
+ @network.add_activity(@activity1)
134
+ end
135
+ assert_equal(2,@network.activities.size)
136
+ end
137
+
138
+ def test_connect
139
+ @network.add_activity(@activity1)
140
+ @network.add_activity(@activity2)
141
+
142
+ assert_nothing_raised do
143
+ @network.connect(@activity1.reference,@activity2.reference)
144
+ end
145
+ assert_equal(1,@activity1.post_activities.size)
146
+ assert_equal(1,@activity2.pre_activities.size)
147
+ assert_equal('a2',@activity1.post_activities[0].reference)
148
+ assert_equal('a1',@activity2.pre_activities[0].reference)
149
+
150
+ assert_raises(RuntimeError) do
151
+ @network.connect(@activity1.reference,'bogus reference')
152
+ end
153
+ assert_equal(1,@activity1.post_activities.size)
154
+ assert_equal('a2',@activity1.post_activities[0].reference)
155
+
156
+ assert_raises(RuntimeError) do
157
+ @network.connect('bogus reference','a2')
158
+ end
159
+ assert_equal(1,@activity2.pre_activities.size)
160
+ assert_equal('a1',@activity2.pre_activities[0].reference)
161
+ end
162
+
163
+ def test_fix_connections!
164
+ @network.add_activity(@activity1)
165
+ @network.add_activity(@activity2)
166
+ @network.add_activity(@activity3)
167
+ @network.connect('start',@activity1.reference)
168
+ @network.connect(@activity1.reference,@activity2.reference)
169
+ @network.connect(@activity3.reference,'finish')
170
+ @network.fix_connections!
171
+ assert_equal(1,@activity2.post_activities.size)
172
+ assert_equal('finish',@activity2.post_activities[0].reference)
173
+ assert_equal(1,@activity3.pre_activities.size)
174
+ assert_equal('start',@activity3.pre_activities[0].reference)
175
+ end
176
+
177
+ def test_to_dot
178
+ net = Precedence::Network.new('Begin','End')
179
+ net.new_activity('act-1-1',3,'System specification')
180
+ net.new_activity('act-1-2',2,'Review')
181
+ net.new_activity('act-1-3',2,'System re-specification')
182
+ net.new_activity('act-2-1',3,'Test tool design')
183
+ net.new_activity('act-2-2',5,'Test tool implementation')
184
+ net.new_activity('act-3-1',3,'System design')
185
+ net.new_activity('act-3-2',12,'System implementation')
186
+ net.new_activity('act-2-3',10,'System testing')
187
+ net.connect('act-1-1','act-1-2')
188
+ net.connect('act-1-2','act-1-3')
189
+ net.connect('act-1-3','act-3-1')
190
+ net.connect('act-1-2','act-2-1')
191
+ net.connect('act-2-1','act-2-2')
192
+ net.connect('act-2-2','act-3-2')
193
+ net.connect('act-3-1','act-3-2')
194
+ net.connect('act-3-2','act-2-3')
195
+ net.fix_connections!
196
+
197
+ assert_nothing_raised do
198
+ File.open('test_to_dot.dot',File::CREAT|File::TRUNC|File::WRONLY) do|f|
199
+ f.puts(net.to_dot)
200
+ end
201
+ end
202
+ $stdout.puts("\nTest dot file test_to_dot.dot file generated.")
203
+
204
+ if system("dot","-Tpng","-otest_to_dot.png","test_to_dot.dot")
205
+ $stdout.puts("Test png file test_to_dot.png files generated.")
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,14 @@
1
+ require('test/unit')
2
+
3
+ require('tests/tc_activity')
4
+ require('tests/tc_network')
5
+
6
+ class TS_Precedence < Test::Unit::TestSuite
7
+ def initialize(name="Precedence Test Suite")
8
+ super(name)
9
+ @tests << TC_Activity.suite
10
+ @tests << TC_StartFinishActivity.suite
11
+ @tests << TC_ActivityHash.suite
12
+ @tests << TC_Network.suite
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.10
3
+ specification_version: 1
4
+ name: precedence
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.6.0
7
+ date: 2005-05-15
8
+ summary: A library for the creation manipulation and analysis of precedence networks.
9
+ require_paths:
10
+ - lib
11
+ email: farrel@lifson.info
12
+ homepage: http://precedence.rubyforge.org
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: precedence
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ -
22
+ - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
25
+ version:
26
+ platform: ruby
27
+ authors:
28
+ - Farrel Lifson
29
+ files:
30
+ - lib/precedence
31
+ - lib/precedence.rb
32
+ - lib/precedence/activity.rb
33
+ - lib/precedence/network.rb
34
+ - tests/tc_activity.rb
35
+ - tests/tc_network.rb
36
+ - tests/ts_precedence.rb
37
+ - README
38
+ - CHANGELOG
39
+ - TODO
40
+ test_files:
41
+ - tests/ts_precedence.rb
42
+ rdoc_options: []
43
+ extra_rdoc_files:
44
+ - README
45
+ - CHANGELOG
46
+ - TODO
47
+ executables: []
48
+ extensions: []
49
+ requirements: []
50
+ dependencies: []