fast-tcpn 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +40 -0
  6. data/LICENSE.txt +674 -0
  7. data/README.md +453 -0
  8. data/fast-tcpn.gemspec +40 -0
  9. data/fast-tcpn.rb +192 -0
  10. data/lib/fast-tcpn.rb +25 -0
  11. data/lib/fast-tcpn/clone.rb +7 -0
  12. data/lib/fast-tcpn/clone/using_code_from_stack.rb +50 -0
  13. data/lib/fast-tcpn/clone/using_deep_clone.rb +10 -0
  14. data/lib/fast-tcpn/clone/using_deep_dive.rb +25 -0
  15. data/lib/fast-tcpn/clone/using_marshal.rb +8 -0
  16. data/lib/fast-tcpn/dsl.rb +177 -0
  17. data/lib/fast-tcpn/hash_marking.rb +220 -0
  18. data/lib/fast-tcpn/place.rb +70 -0
  19. data/lib/fast-tcpn/tcpn.rb +288 -0
  20. data/lib/fast-tcpn/timed_hash_marking.rb +87 -0
  21. data/lib/fast-tcpn/timed_place.rb +44 -0
  22. data/lib/fast-tcpn/timed_token.rb +27 -0
  23. data/lib/fast-tcpn/token.rb +30 -0
  24. data/lib/fast-tcpn/transition.rb +224 -0
  25. data/lib/fast-tcpn/version.rb +3 -0
  26. data/spec/callbacks_spec.rb +164 -0
  27. data/spec/dsl/page_spec.rb +195 -0
  28. data/spec/dsl/transition_spec.rb +41 -0
  29. data/spec/hash_marking_spec.rb +9 -0
  30. data/spec/place_spec.rb +10 -0
  31. data/spec/spec_helper.rb +101 -0
  32. data/spec/support/hash_marking_shared.rb +274 -0
  33. data/spec/support/place_shared.rb +66 -0
  34. data/spec/support/token_shared.rb +27 -0
  35. data/spec/support/uses_temp_files.rb +31 -0
  36. data/spec/tcpn_binding_spec.rb +54 -0
  37. data/spec/tcpn_sim_spec.rb +323 -0
  38. data/spec/tcpn_spec.rb +150 -0
  39. data/spec/timed_hash_marking_spec.rb +132 -0
  40. data/spec/timed_place_spec.rb +38 -0
  41. data/spec/timed_token_spec.rb +50 -0
  42. data/spec/token_spec.rb +13 -0
  43. data/spec/transition_spec.rb +236 -0
  44. metadata +156 -0
data/spec/tcpn_spec.rb ADDED
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+
3
+ describe FastTCPN::TCPN do
4
+ shared_examples "place handler" do
5
+ context "if place does not exist" do
6
+
7
+ it "creates new place" do
8
+ expect {
9
+ subject.place "cpus", name: :name, node: :node
10
+ }.to change(subject, :places_count).by(1)
11
+ end
12
+
13
+ it "returns created place" do
14
+ p = subject.place "cpus", name: :name, node: :node
15
+ expect(p.name).to eq 'cpus'
16
+ expect(p.keys).to eq({ name: :name, node: :node })
17
+ end
18
+
19
+ end
20
+
21
+ context "if place exists" do
22
+
23
+ subject do
24
+ n = FastTCPN::TCPN.new
25
+ n.place "cpus", name: :name, node: :node
26
+ n
27
+ end
28
+
29
+ it "does not create place" do
30
+ expect {
31
+ subject.place "cpus", name: :name, node: :node
32
+ }.not_to change(subject, :places_count)
33
+ end
34
+
35
+ it "returns existing place" do
36
+ p = subject.place("cpus", valid: :valid?)
37
+ expect(p.kind_of? FastTCPN::Place).to be true
38
+ expect(p.name).to eq("cpus")
39
+ end
40
+
41
+ it "merges new keys of place with old ones" do
42
+ expect(subject.place("cpus", valid: :valid?).keys).to eq({ name: :name, node: :node, valid: :valid? })
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+
49
+ describe "#place" do
50
+ it_behaves_like 'place handler'
51
+
52
+ it "creates Place" do
53
+ expect(subject.place("new one").class).to be FastTCPN::Place
54
+ end
55
+ end
56
+
57
+ describe "#timed_place" do
58
+ it_behaves_like 'place handler'
59
+
60
+ it "creates TimedPlace" do
61
+ expect(subject.timed_place("new one").class).to be FastTCPN::TimedPlace
62
+ end
63
+ end
64
+
65
+ describe "#find_place finds place by name" do
66
+
67
+ subject do
68
+ net = FastTCPN::TCPN.new
69
+ net.place "cpus", name: :name, node: :node
70
+ net.place "nodes", name: :name
71
+ net
72
+ end
73
+
74
+ it { expect(subject.find_place("cpus").name).to eq "cpus" }
75
+ it { expect(subject.find_place("nodes").name).to eq "nodes" }
76
+
77
+ end
78
+
79
+ describe "#transition" do
80
+ context "if transition does not exist" do
81
+ it "creates new transition" do
82
+ expect {
83
+ subject.transition(:cpu_working)
84
+ }.to change(subject, :transitions_count).by(1)
85
+ end
86
+
87
+ it "returns created transition" do
88
+ t = subject.transition(:cpu_working)
89
+ expect(t.kind_of? FastTCPN::Transition).to be true
90
+ expect(t.name).to eq :cpu_working
91
+ end
92
+ end
93
+
94
+ context "if transition exists" do
95
+ subject do
96
+ n = FastTCPN::TCPN.new
97
+ n.transition :cpu_working
98
+ n
99
+ end
100
+
101
+ it "does not create new transition" do
102
+ expect {
103
+ subject.transition(:cpu_working)
104
+ }.not_to change(subject, :transitions_count)
105
+ end
106
+
107
+ it "returns existing transition" do
108
+ t = subject.transition(:cpu_working)
109
+ expect(t.kind_of? FastTCPN::Transition).to be true
110
+ expect(t.name).to eq :cpu_working
111
+ end
112
+ end
113
+ end
114
+
115
+ describe "old API from tcpn gem" do
116
+ let :tcpn do
117
+ n = FastTCPN::TCPN.new
118
+ n.timed_place "cpus"
119
+ n
120
+ end
121
+
122
+ subject { tcpn }
123
+
124
+ describe "#add_marking_for" do
125
+ it "adds token for specified place" do
126
+ subject.add_marking_for 'cpus', 'cpu1'
127
+ expect(subject.find_place('cpus').marking.map {|t| t.value }).to eq ['cpu1']
128
+ end
129
+
130
+ it "adds timed token for specified place" do
131
+ subject.add_marking_for 'cpus', { val: 'cpu1', ts: 0 }
132
+ expect(subject.find_place('cpus').marking.map {|t| t.value }).to eq ['cpu1']
133
+ expect(subject.find_place('cpus').marking.map {|t| t.timestamp }).to eq [0]
134
+ end
135
+ end
136
+
137
+ describe "#marking_for" do
138
+ subject do
139
+ tcpn.find_place('cpus').add 'cpu1'
140
+ tcpn
141
+ end
142
+
143
+ it "returns correct token value for specified place" do
144
+ expect(subject.marking_for('cpus')).to eq [{ val: 'cpu1', ts: 0}]
145
+ end
146
+
147
+ end
148
+ end
149
+
150
+ end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+
3
+ describe FastTCPN::TimedHashMarking do
4
+
5
+ let(:marking_class) { FastTCPN::TimedHashMarking }
6
+
7
+ let(:time) { 100 }
8
+
9
+ let(:active1) { Worker.new("active1", true) }
10
+ let(:active2) { Worker.new("active2", false) }
11
+ let(:waiting1) { Worker.new("waiting1", true) }
12
+ let(:waiting1_timestamp) { time + 100 }
13
+ let(:waiting2) { Worker.new("waiting2", false) }
14
+ let(:waiting2_timestamp) { time + 200 }
15
+
16
+ let :marking do
17
+ m = marking_class.new name: :name, finished: :finished?
18
+ m.add active1
19
+ m.add active2, time
20
+ m.add waiting1, waiting1_timestamp
21
+ m.add waiting1, waiting2_timestamp
22
+ m.time = time
23
+ m
24
+ end
25
+
26
+ subject { marking }
27
+
28
+ it_behaves_like 'hash marking'
29
+
30
+ it "stores current time" do
31
+ expect(subject.time).to eq time
32
+ end
33
+
34
+ it "does not allow to put back clock" do
35
+ expect { subject.time = time - 10 }.to raise_error FastTCPN::TimedHashMarking::InvalidTime
36
+ end
37
+
38
+ it "returns only active tokens" do
39
+ expect(subject.each.map { |t| t.value.name }).to match_array [ active1.name, active2.name ]
40
+ end
41
+
42
+ describe "returns waiting tokens when time comes" do
43
+ subject do
44
+ marking.time = waiting1_timestamp
45
+ marking
46
+ end
47
+
48
+ it "without filter" do
49
+ expect(subject.each.map { |t| t.value.name }).to match_array [ active1.name, active2.name, waiting1.name ]
50
+ end
51
+
52
+ it "with name filter" do
53
+ expect(subject.each(:name, waiting1.name).first.value.name).to eq waiting1.name
54
+ end
55
+
56
+ it "with finished filter" do
57
+ expect(subject.each(:finished, true).map{ |t| t.value.name }).to match_array [ waiting1.name, active1.name ]
58
+ end
59
+ end
60
+
61
+ describe "#next_time" do
62
+ it "returns next time when a token may be available" do
63
+ expect(marking.next_time).to eq waiting1_timestamp
64
+ marking.time = waiting1_timestamp
65
+ expect(marking.next_time).to eq waiting2_timestamp
66
+ end
67
+
68
+ it "returns 0 if no token will be available in the future" do
69
+ marking.time = waiting2_timestamp
70
+ expect(marking.next_time).to eq 0
71
+ end
72
+
73
+ it "returns 0 for empty marking" do
74
+ marking = marking_class.new name: :name, finished: :finished?
75
+ expect(marking.next_time).to eq 0
76
+ end
77
+ end
78
+
79
+ describe "#add" do
80
+ it "uses token's timestamp for token" do
81
+ token = FastTCPN::TimedToken.new(Worker.new(:asd, true), 200)
82
+ expect {
83
+ marking.add token
84
+ }.not_to change(marking, :size)
85
+ end
86
+
87
+ it "uses given timestamp for object" do
88
+ expect {
89
+ marking.add Worker.new(:asd, true), time
90
+ }.to change(marking, :size).by(1)
91
+ end
92
+
93
+ describe "with Hash" do
94
+ let(:value) { Worker.new("new intel123", true, 'intel') }
95
+ subject do
96
+ marking = marking_class.new name: :name, finished: :finished?
97
+ marking.add val: value, ts: 100
98
+ marking.time = 100
99
+ marking
100
+ end
101
+
102
+ it "adds token with value from :val key" do
103
+ expect(subject.each.first.value.name).to eq value.name
104
+ end
105
+
106
+ it "adds token with timestamp from :ts key" do
107
+ expect(subject.each.first.timestamp).to eq 100
108
+ end
109
+
110
+ end
111
+
112
+ describe "with Hash in Array" do
113
+ let(:value1) { Worker.new("new intel123", true, 'intel') }
114
+ let(:value2) { Worker.new("new intel223", true, 'intel') }
115
+
116
+ subject do
117
+ marking = marking_class.new name: :name, finished: :finished?
118
+ marking.add [{ val: value1, ts: 100}, { val: value2, ts: 100}]
119
+ marking.time = 100
120
+ marking
121
+ end
122
+
123
+ it "adds all tokens from Array" do
124
+ expect(subject.map { |t| t.value.name }).to match_array [value1.name, value2.name]
125
+ end
126
+
127
+ it "added tokens have correct timestamp" do
128
+ expect(subject.map { |t| t.timestamp }).to match_array [100, 100]
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe FastTCPN::TimedPlace do
4
+
5
+ let(:place_class) { FastTCPN::TimedPlace }
6
+ let(:marking_class) { FastTCPN::TimedHashMarking }
7
+ let(:keys) { { name: :name, valid: :valid? } }
8
+
9
+ it_behaves_like 'valid place'
10
+
11
+ subject do
12
+ place_class.new "cpu"
13
+ end
14
+
15
+ let :marking do
16
+ marking = double(marking_class)
17
+ mc = class_double(marking_class).as_stubbed_const(:transfer_nested_constants => true)
18
+ allow(mc).to receive(:new).and_return(marking)
19
+ marking
20
+ end
21
+
22
+ it "passes next_time to marking" do
23
+ expect(marking).to receive(:next_time).and_return(997)
24
+ expect(subject.next_time).to eq 997
25
+ end
26
+
27
+ it "passes time= to marking" do
28
+ expect(marking).to receive(:time=)
29
+ subject.time = 123
30
+ end
31
+
32
+ it "passes add with timestamp to marking" do
33
+ token = Worker.new(:asd, true)
34
+ expect(marking).to receive(:add).with(token, 100)
35
+ subject.add token, 100
36
+ end
37
+
38
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe FastTCPN::TimedToken do
4
+
5
+ let(:token_class) { FastTCPN::TimedToken }
6
+ it_behaves_like 'token'
7
+
8
+ it "stores timestamp" do
9
+ t = token_class.new "asd", 100
10
+ expect(t.timestamp).to eq 100
11
+ end
12
+
13
+ it "has default timestamp seto to 0" do
14
+ t = token_class.new "asd"
15
+ expect(t.timestamp).to eq 0
16
+ end
17
+
18
+ describe "#==" do
19
+ it "does not care for timestamp" do
20
+ t1 = token_class.new "asd", 100
21
+ t2 = t1.clone
22
+ t2.timestamp = 200
23
+ expect(t1 == t2).to be true
24
+ end
25
+ end
26
+
27
+ describe "#with_timestamp" do
28
+ let(:t1) { token_class.new "asd", 100 }
29
+ let(:t2) { t1.with_timestamp 1000 }
30
+
31
+ it "returns token with the same value" do
32
+ expect(t2.value).to eq t1.value
33
+ end
34
+
35
+ it "returns token with new timestamp" do
36
+ expect(t2.timestamp).to eq 1000
37
+ end
38
+
39
+ it "returns token that is not equal to self" do
40
+ expect(t1 == t2).not_to be true
41
+ end
42
+ end
43
+
44
+ describe "#to_hash" do
45
+ it "returns valid reprsentation of token" do
46
+ expect(token_class.new(:asd, 200).to_hash).to eq({ val: :asd, ts: 200 })
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe FastTCPN::Token do
4
+
5
+ let(:token_class) { FastTCPN::Token }
6
+ it_behaves_like 'token'
7
+
8
+ describe "#to_hash" do
9
+ it "returns valid reprsentation of token" do
10
+ expect(token_class.new(:asd).to_hash).to eq({ val: :asd })
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,236 @@
1
+ require 'spec_helper'
2
+
3
+ describe FastTCPN::Transition do
4
+ let(:process) { :a_process }
5
+ let(:process2) { :a_process2 }
6
+ let(:process3) { :a_process3 }
7
+ let(:cpu) { :a_cpu }
8
+
9
+ let(:in1) do
10
+ p = FastTCPN::Place.new "process"
11
+ p.add process
12
+ p
13
+ end
14
+
15
+ let(:in2) do
16
+ p = FastTCPN::Place.new "cpu"
17
+ p.add cpu
18
+ p
19
+ end
20
+
21
+ let(:empty) do
22
+ p = FastTCPN::Place.new "empty"
23
+ p
24
+ end
25
+
26
+ let(:out) { FastTCPN::Place.new "output" }
27
+
28
+ let :transition do
29
+ t = FastTCPN::Transition.new :working
30
+ t.input in1
31
+ t.input in2
32
+ t
33
+ end
34
+
35
+ it "has name" do
36
+ expect(transition.name).to eq :working
37
+ end
38
+
39
+ describe "#fire" do
40
+ it "passes input tokens to sentry" do
41
+ transition.sentry do |marking_for, clock, result|
42
+ expect(marking_for[in1.name].each.first.value).to eq process
43
+ expect(marking_for[in2.name].each.first.value).to eq cpu
44
+ end
45
+ transition.fire
46
+ end
47
+
48
+ it "passes valid binding to output arc expression" do
49
+ transition.sentry do |marking_for, clock, result|
50
+ result << { in1.name => marking_for[in1.name].first, in2.name => marking_for[in2.name].first }
51
+ end
52
+ transition.output out do |binding|
53
+ expect(binding[in1.name].value).to eq process
54
+ expect(binding[in2.name].value).to eq cpu
55
+ binding[in1.name]
56
+ end
57
+ transition.fire
58
+ end
59
+
60
+ it "passes clock to sentry" do
61
+ transition.sentry do |marking_for, clock, result|
62
+ expect(clock).to eq 1000
63
+ end
64
+ transition.fire(1000)
65
+ end
66
+
67
+ it "passes clock to output arc expression" do
68
+ transition.sentry do |marking_for, clock, result|
69
+ result << { in1.name => marking_for[in1.name].first, in2.name => marking_for[in2.name].first }
70
+ end
71
+ transition.output out do |binding, clock|
72
+ expect(clock).to eq 1000
73
+ binding[in1.name]
74
+ end
75
+ transition.fire 1000
76
+ end
77
+
78
+ it "removes single tokens from input places and puts in output places" do
79
+ transition.sentry do |marking_for, clock, result|
80
+ result << { in1.name => marking_for[in1.name].first, in2.name => marking_for[in2.name].first }
81
+ end
82
+ transition.output out do |binding|
83
+ binding[in1.name]
84
+ end
85
+ transition.output in2 do |binding|
86
+ binding[in2.name]
87
+ end
88
+ transition.fire
89
+ expect(out.marking.each.map { |t| t.value }).to match_array [ process ]
90
+ expect(in2.marking.each.map { |t| t.value }).to match_array [ cpu ]
91
+ end
92
+
93
+ it "removes array of tokens from input places and puts in output places" do
94
+ in1.add process2
95
+ in1.add process3
96
+ transition.sentry do |marking_for, clock, result|
97
+ p1 = marking_for[in1.name].select { |t| t.value == process2 }.first
98
+ p2 = marking_for[in1.name].select { |t| t.value == process3 }.first
99
+ result << { in1.name => [p1, p2], in2.name => marking_for[in2.name].first }
100
+ end
101
+ transition.output out do |binding|
102
+ expect(binding[in1.name].map{ |t| t.value }).to eq [process2, process3]
103
+ binding[in1.name].first
104
+ end
105
+ transition.output in2 do |binding|
106
+ binding[in2.name]
107
+ end
108
+ transition.fire
109
+ expect(out.marking.each.map { |t| t.value }).to match_array [ process2 ]
110
+ expect(in2.marking.each.map { |t| t.value }).to match_array [ cpu ]
111
+ end
112
+
113
+ it "accepts nil as result of output arc expression" do
114
+ transition.sentry do |marking_for, clock, result|
115
+ result << { in1.name => marking_for[in1.name].first, in2.name => marking_for[in2.name].first }
116
+ end
117
+ transition.output out do |binding|
118
+ binding[in1.name]
119
+ end
120
+ transition.output in2 do |binding|
121
+ nil
122
+ end
123
+ transition.fire
124
+ expect(out.marking.each.map { |t| t.value }).to match_array [ process ]
125
+ expect(in2.marking).to be_empty
126
+ end
127
+
128
+
129
+ describe "default sentry" do
130
+ it "works correctly for empty input markings" do
131
+ transition = FastTCPN::Transition.new "disabled"
132
+ transition.input empty
133
+ transition.output out do |binding|
134
+ binding[empty.name]
135
+ end
136
+ expect(transition.fire).to be false
137
+ end
138
+
139
+ it "lets it fire for any tokens" do
140
+ transition.output out do |binding|
141
+ binding[in1.name]
142
+ end
143
+ transition.output in2 do |binding|
144
+ binding[in2.name]
145
+ end
146
+ expect(transition.fire).to be true
147
+ expect(in1.marking).to be_empty
148
+ expect(in2.marking).not_to be_empty
149
+ expect(out.marking).not_to be_empty
150
+ end
151
+
152
+ it "prevents firing transition if one of many input places is not marked" do
153
+ transition = FastTCPN::Transition.new "disabled"
154
+ transition.input in1
155
+ transition.input empty
156
+ transition.output out do |binding|
157
+ binding[empty.name]
158
+ end
159
+ expect(transition.fire).to be false
160
+
161
+ end
162
+ end
163
+
164
+ describe "#default_sentry?" do
165
+ it "is true if transition has no custom sentry" do
166
+ transition = FastTCPN::Transition.new "work"
167
+ expect(transition.default_sentry?).to be true
168
+ end
169
+ it "is false if transition has a custom sentry" do
170
+ transition = FastTCPN::Transition.new "work"
171
+ transition.sentry {}
172
+ expect(transition.default_sentry?).to be false
173
+ end
174
+ end
175
+
176
+ describe "#output" do
177
+ it "requires Place object as first parameter" do
178
+ expect { transition.output(:asd) {} }.to raise_error
179
+ end
180
+ it "requires block" do
181
+ expect { transition.output in1 }.to raise_error
182
+ end
183
+ end
184
+
185
+ describe "#inputs_size" do
186
+ it "counts input arcs" do
187
+ expect{
188
+ transition.input out
189
+ }.to change(transition, :inputs_size).from(2).to(3)
190
+ end
191
+ end
192
+
193
+ describe "#outputs_size" do
194
+ it "counts output arcs" do
195
+ expect{
196
+ transition.output(out) {}
197
+ }.to change(transition, :outputs_size).from(0).to(1)
198
+ end
199
+ end
200
+
201
+ describe "callbacks" do
202
+ let(:net) { double Object, call_callbacks: nil }
203
+ let :transition do
204
+ t = FastTCPN::Transition.new :working, net
205
+ t.input in1
206
+ t.input in2
207
+ t
208
+ end
209
+
210
+ it "calls before callbacks on its net" do
211
+ expect(net).to receive(:call_callbacks).with(:transition, :before, anything()).once
212
+ transition.fire
213
+ end
214
+
215
+ it "calls after callbacks on its net" do
216
+ expect(net).to receive(:call_callbacks).with(:transition, :after, anything()).once
217
+ transition.fire
218
+ end
219
+ end
220
+
221
+ it "raises error if sentry puts in binding invalid object in place of token" do
222
+ transition.sentry do |marking_for, clock, result|
223
+ result << { in1.name => Object.new }
224
+ end
225
+ expect { transition.fire }.to raise_error FastTCPN::Transition::InvalidToken
226
+ end
227
+
228
+ it "raises error if sentry puts in binding token that not exists in place" do
229
+ transition.sentry do |marking_for, clock, result|
230
+ result << { in1.name => marking_for[in2.name].first }
231
+ end
232
+ expect { transition.fire }.to raise_error FastTCPN::Transition::InvalidToken
233
+ end
234
+ end
235
+
236
+ end