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.
- 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
|