fast-tcpn 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +40 -0
- data/LICENSE.txt +674 -0
- data/README.md +453 -0
- data/fast-tcpn.gemspec +40 -0
- data/fast-tcpn.rb +192 -0
- data/lib/fast-tcpn.rb +25 -0
- data/lib/fast-tcpn/clone.rb +7 -0
- data/lib/fast-tcpn/clone/using_code_from_stack.rb +50 -0
- data/lib/fast-tcpn/clone/using_deep_clone.rb +10 -0
- data/lib/fast-tcpn/clone/using_deep_dive.rb +25 -0
- data/lib/fast-tcpn/clone/using_marshal.rb +8 -0
- data/lib/fast-tcpn/dsl.rb +177 -0
- data/lib/fast-tcpn/hash_marking.rb +220 -0
- data/lib/fast-tcpn/place.rb +70 -0
- data/lib/fast-tcpn/tcpn.rb +288 -0
- data/lib/fast-tcpn/timed_hash_marking.rb +87 -0
- data/lib/fast-tcpn/timed_place.rb +44 -0
- data/lib/fast-tcpn/timed_token.rb +27 -0
- data/lib/fast-tcpn/token.rb +30 -0
- data/lib/fast-tcpn/transition.rb +224 -0
- data/lib/fast-tcpn/version.rb +3 -0
- data/spec/callbacks_spec.rb +164 -0
- data/spec/dsl/page_spec.rb +195 -0
- data/spec/dsl/transition_spec.rb +41 -0
- data/spec/hash_marking_spec.rb +9 -0
- data/spec/place_spec.rb +10 -0
- data/spec/spec_helper.rb +101 -0
- data/spec/support/hash_marking_shared.rb +274 -0
- data/spec/support/place_shared.rb +66 -0
- data/spec/support/token_shared.rb +27 -0
- data/spec/support/uses_temp_files.rb +31 -0
- data/spec/tcpn_binding_spec.rb +54 -0
- data/spec/tcpn_sim_spec.rb +323 -0
- data/spec/tcpn_spec.rb +150 -0
- data/spec/timed_hash_marking_spec.rb +132 -0
- data/spec/timed_place_spec.rb +38 -0
- data/spec/timed_token_spec.rb +50 -0
- data/spec/token_spec.rb +13 -0
- data/spec/transition_spec.rb +236 -0
- 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
|
data/spec/token_spec.rb
ADDED
@@ -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
|