fast-tcpn 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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