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
@@ -0,0 +1,66 @@
|
|
1
|
+
shared_examples "valid place" do
|
2
|
+
|
3
|
+
it "has name" do
|
4
|
+
p = place_class.new "process"
|
5
|
+
expect(p.name).to eq "process"
|
6
|
+
end
|
7
|
+
|
8
|
+
it "passes keys to marking constructor" do
|
9
|
+
expect(marking_class).to receive(:new).with(keys)
|
10
|
+
place_class.new "processes", keys
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "when created" do
|
14
|
+
subject do
|
15
|
+
place_class.new "processes", keys
|
16
|
+
end
|
17
|
+
let(:token) { "for this example just anything" }
|
18
|
+
let(:new_keys) { { node: :node_name, priority: :pri } }
|
19
|
+
|
20
|
+
let :marking do
|
21
|
+
marking = double(marking_class)
|
22
|
+
mc = class_double(marking_class).as_stubbed_const(:transfer_nested_constants => true)
|
23
|
+
allow(mc).to receive(:new).and_return(marking)
|
24
|
+
marking
|
25
|
+
end
|
26
|
+
|
27
|
+
it "passes add to marking" do
|
28
|
+
expect(marking).to receive(:add).with(token)
|
29
|
+
subject.add token
|
30
|
+
end
|
31
|
+
|
32
|
+
it "passes delete to marking" do
|
33
|
+
expect(marking).to receive(:delete).with(token)
|
34
|
+
subject.delete token
|
35
|
+
end
|
36
|
+
|
37
|
+
it "passes add_keys to marking" do
|
38
|
+
expect(marking).to receive(:add_keys).with(new_keys)
|
39
|
+
subject.add_keys new_keys
|
40
|
+
end
|
41
|
+
|
42
|
+
it "passes keys to marking" do
|
43
|
+
expect(marking).to receive(:keys)
|
44
|
+
subject.keys
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "net callback" do
|
48
|
+
let(:net) { double Object, call_callbacks: nil }
|
49
|
+
let :place do
|
50
|
+
place_class.new "process", {}, net
|
51
|
+
end
|
52
|
+
|
53
|
+
it "is called on :add" do
|
54
|
+
expect(net).to receive(:call_callbacks).with(:place, :add, anything())
|
55
|
+
place.add token
|
56
|
+
end
|
57
|
+
|
58
|
+
it "is called on :remove" do
|
59
|
+
expect(net).to receive(:call_callbacks).with(:place, :remove, anything())
|
60
|
+
allow(marking).to receive(:delete)
|
61
|
+
place.delete token
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
shared_examples 'token' do
|
2
|
+
|
3
|
+
it "stores value" do
|
4
|
+
t = token_class.new "asd"
|
5
|
+
expect(t.value).to eq "asd"
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#==" do
|
9
|
+
it "is true for self" do
|
10
|
+
t = token_class.new "asd"
|
11
|
+
expect(t == t).to be true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "is false for two different tokens, value does not matter" do
|
15
|
+
t1 = token_class.new "asd"
|
16
|
+
t2 = token_class.new "asd"
|
17
|
+
expect(t1 == t2).to be false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "it true for clones" do
|
21
|
+
t1 = token_class.new "asd"
|
22
|
+
t2 = t1.clone
|
23
|
+
expect(t1 == t2).to be true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# taken from here:
|
2
|
+
# http://gabebw.wordpress.com/2011/03/21/temp-files-in-rspec/
|
3
|
+
module UsesTempFiles
|
4
|
+
def self.included(example_group)
|
5
|
+
example_group.extend(self)
|
6
|
+
end
|
7
|
+
|
8
|
+
def in_directory_with_file(file)
|
9
|
+
before do
|
10
|
+
@pwd = Dir.pwd if @pwd.nil?
|
11
|
+
@tmp_dir = File.join(File.dirname(__FILE__), 'tmp')
|
12
|
+
FileUtils.mkdir_p(@tmp_dir)
|
13
|
+
Dir.chdir(@tmp_dir)
|
14
|
+
|
15
|
+
FileUtils.mkdir_p(File.dirname(file))
|
16
|
+
FileUtils.touch(file)
|
17
|
+
end
|
18
|
+
|
19
|
+
define_method(:content_for_file) do |fname, content|
|
20
|
+
f = File.new(File.join(@tmp_dir, fname), 'a+')
|
21
|
+
f.write(content)
|
22
|
+
f.flush # VERY IMPORTANT
|
23
|
+
f.close
|
24
|
+
end
|
25
|
+
|
26
|
+
after do
|
27
|
+
Dir.chdir(@pwd)
|
28
|
+
FileUtils.rm_rf(@tmp_dir)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FastTCPN::TCPNBinding do
|
4
|
+
|
5
|
+
let :marking_for do
|
6
|
+
process = FastTCPN::HashMarking.new
|
7
|
+
process.add "process1"
|
8
|
+
process.add "process2"
|
9
|
+
|
10
|
+
cpu = FastTCPN::HashMarking.new
|
11
|
+
cpu.add "cpu1"
|
12
|
+
cpu.add "cpu2"
|
13
|
+
|
14
|
+
{ process: process, cpu: cpu }
|
15
|
+
end
|
16
|
+
|
17
|
+
let :selected_process do
|
18
|
+
marking_for[:process].each.first
|
19
|
+
end
|
20
|
+
|
21
|
+
let :selected_cpu do
|
22
|
+
marking_for[:cpu].each.first
|
23
|
+
end
|
24
|
+
|
25
|
+
let :mapping do
|
26
|
+
{ process: selected_process, cpu: selected_cpu }
|
27
|
+
end
|
28
|
+
|
29
|
+
subject do
|
30
|
+
FastTCPN::TCPNBinding.new mapping, marking_for
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "returns new copy of token for given place" do
|
34
|
+
it "returns selected process" do
|
35
|
+
expect(subject[:process]).to eq selected_process
|
36
|
+
end
|
37
|
+
|
38
|
+
it "returns selected cpu" do
|
39
|
+
expect(subject[:cpu]).to eq selected_cpu
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
context "for array of tokens for a place" do
|
45
|
+
let :selected_process do
|
46
|
+
[ marking_for[:process].to_a[0],
|
47
|
+
marking_for[:process].to_a[1] ]
|
48
|
+
end
|
49
|
+
it "returns array of new copies of tokens for this place" do
|
50
|
+
expect(subject[:process]).to eq selected_process
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,323 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FastTCPN::TCPN do
|
4
|
+
describe "#sim" do
|
5
|
+
AppProcess = Struct.new(:name)
|
6
|
+
CPU = Struct.new(:name, :process)
|
7
|
+
|
8
|
+
let(:net) do
|
9
|
+
n = FastTCPN::TCPN.new
|
10
|
+
n
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:process_count) { 10 }
|
14
|
+
let(:cpu_count) { 10 }
|
15
|
+
|
16
|
+
shared_examples "correctly moves tokens" do
|
17
|
+
|
18
|
+
it "removes all tokens from process" do
|
19
|
+
expect(process.marking.size).to eq 0
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns all tokens back to cpu" do
|
23
|
+
expect(cpu.marking.size).to eq cpu_count * process_count
|
24
|
+
end
|
25
|
+
|
26
|
+
it "leaves no tokens in out" do
|
27
|
+
expect(out.marking.size).to eq 0
|
28
|
+
end
|
29
|
+
|
30
|
+
it "puts all tokens in finished" do
|
31
|
+
expect(finished.marking.size).to eq process_count
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "without time" do
|
36
|
+
|
37
|
+
let(:process) { net.place :process, name: :name }
|
38
|
+
let(:cpu) { net.place :cpu, name: :name, process: :process }
|
39
|
+
let(:out) { net.place :out }
|
40
|
+
let(:finished) { net.place :finished }
|
41
|
+
|
42
|
+
|
43
|
+
before do
|
44
|
+
t1 = net.transition :work
|
45
|
+
t1.sentry do |marking_for, clock, result|
|
46
|
+
marking_for[:process].each do |p|
|
47
|
+
marking_for[:cpu].each(:process, p.value.name) do |c|
|
48
|
+
result << { process: p, cpu: c }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
t1.input process
|
53
|
+
t1.input cpu
|
54
|
+
t1.output out do |binding|
|
55
|
+
binding[:process].value.name + "_done"
|
56
|
+
end
|
57
|
+
t1.output cpu do |binding|
|
58
|
+
binding[:cpu]
|
59
|
+
end
|
60
|
+
|
61
|
+
t2 = net.transition :finish
|
62
|
+
t2.input out
|
63
|
+
t2.output finished do |binding|
|
64
|
+
binding[:out]
|
65
|
+
end
|
66
|
+
|
67
|
+
process_count.times do |p|
|
68
|
+
process.add AppProcess.new(p.to_s)
|
69
|
+
cpu_count.times.map { |c| cpu.add CPU.new("CPU#{c}_#{p}", p.to_s) }
|
70
|
+
end
|
71
|
+
|
72
|
+
net.sim
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
include_examples "correctly moves tokens"
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
context "with time" do
|
81
|
+
|
82
|
+
let(:process) { net.timed_place :process, name: :name }
|
83
|
+
let(:cpu) { net.timed_place :cpu, name: :name, process: :process }
|
84
|
+
let(:out) { net.timed_place :out }
|
85
|
+
let(:finished) { net.timed_place :finished }
|
86
|
+
|
87
|
+
before do
|
88
|
+
t1 = net.transition :work
|
89
|
+
t1.sentry do |marking_for, clock, result|
|
90
|
+
marking_for[:process].each do |p|
|
91
|
+
marking_for[:cpu].each(:process, p.value.name) do |c|
|
92
|
+
result << { process: p, cpu: c }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
t1.input process
|
97
|
+
t1.input cpu
|
98
|
+
t1.output out do |binding, clock|
|
99
|
+
{ val: binding[:process].value.name + "_done", ts: clock + 10 }
|
100
|
+
end
|
101
|
+
t1.output cpu do |binding, clock|
|
102
|
+
binding[:cpu].with_timestamp clock + 100
|
103
|
+
end
|
104
|
+
|
105
|
+
t2 = net.transition :finish
|
106
|
+
t2.input out
|
107
|
+
t2.output finished do |binding, clock|
|
108
|
+
binding[:out].with_timestamp clock + 100
|
109
|
+
end
|
110
|
+
|
111
|
+
process_count.times do |p|
|
112
|
+
process.add AppProcess.new(p.to_s)
|
113
|
+
cpu_count.times.map { |c| cpu.add CPU.new("CPU#{c}_#{p}", p.to_s) }
|
114
|
+
end
|
115
|
+
|
116
|
+
net.sim
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
include_examples "correctly moves tokens"
|
121
|
+
|
122
|
+
it "sets correct timestamps of used cpus" do
|
123
|
+
used_cpus = cpu.marking.select { |token| token.timestamp == 100 }.length
|
124
|
+
expect(used_cpus).to eq process_count
|
125
|
+
end
|
126
|
+
|
127
|
+
it "sets correct timestamps on processes" do
|
128
|
+
finished.marking.each do |token|
|
129
|
+
expect(token.timestamp).to eq 110
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
it "stops at correct time" do
|
134
|
+
expect(net.clock).to eq 110
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
describe "error handling" do
|
141
|
+
let(:process) { tcpn.timed_place :process, name: :name }
|
142
|
+
let(:cpu) { tcpn.timed_place :cpu, name: :name, process: :process }
|
143
|
+
let(:out) { tcpn.timed_place :out }
|
144
|
+
|
145
|
+
let(:tcpn) do
|
146
|
+
FastTCPN::TCPN.new
|
147
|
+
end
|
148
|
+
|
149
|
+
before do
|
150
|
+
t1 = tcpn.transition :work
|
151
|
+
t1.sentry &sentry
|
152
|
+
t1.input process
|
153
|
+
t1.input cpu
|
154
|
+
t1.output out, &out_output
|
155
|
+
t1.output cpu, &cpu_output
|
156
|
+
|
157
|
+
process.add AppProcess.new('process1')
|
158
|
+
cpu.add CPU.new("CPU1_process1", 'process1')
|
159
|
+
end
|
160
|
+
|
161
|
+
let(:sentry) do
|
162
|
+
proc do |marking_for, clock, result|
|
163
|
+
marking_for[:process].each do |p|
|
164
|
+
marking_for[:cpu].each(:process, p.value.name) do |c|
|
165
|
+
result << { process: p, cpu: c }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
let(:out_output) do
|
172
|
+
proc do |binding, clock|
|
173
|
+
{ val: binding[:process].value.name + "_done", ts: clock + 10 }
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
let(:cpu_output) do
|
178
|
+
proc do |binding, clock|
|
179
|
+
binding[:cpu].with_timestamp clock + 100
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
shared_examples 'error handler' do
|
184
|
+
it "raises TCPN::SimulationError" do
|
185
|
+
expect { tcpn.sim }.to raise_error FastTCPN::TCPN::SimulationError
|
186
|
+
end
|
187
|
+
|
188
|
+
context "when FastTCPN.debug is false" do
|
189
|
+
it "does not put FastTCPN files in backtrace" do
|
190
|
+
FastTCPN.debug = false
|
191
|
+
error_raised = false
|
192
|
+
begin
|
193
|
+
tcpn.sim
|
194
|
+
rescue FastTCPN::TCPN::SimulationError => e
|
195
|
+
error_raised = true
|
196
|
+
expect(e.backtrace.map { |b| b.sub /:.*$/,'' }.select{ |b| b =~ /\/lib\/fast-tcpn\// }).to be_empty
|
197
|
+
expect(e.backtrace).not_to be_empty
|
198
|
+
end
|
199
|
+
expect(error_raised).to be true
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
context "when FastTCPN.debug is true" do
|
204
|
+
it "puts FastTCPN files in backtrace" do
|
205
|
+
FastTCPN.debug = true
|
206
|
+
error_raised = false
|
207
|
+
begin
|
208
|
+
tcpn.sim
|
209
|
+
rescue FastTCPN::TCPN::SimulationError => e
|
210
|
+
error_raised = true
|
211
|
+
expect(e.backtrace.map { |b| b.sub /:.*$/,'' }.select{ |b| b =~ /\/lib\/fast-tcpn\// }).not_to be_empty
|
212
|
+
end
|
213
|
+
expect(error_raised).to be true
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context "for invalid mapping from sentry" do
|
219
|
+
let(:sentry) do
|
220
|
+
proc do |marking_for, clock, result|
|
221
|
+
marking_for[:process].each do |p|
|
222
|
+
result << { process: p }
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
it_behaves_like 'error handler'
|
228
|
+
end
|
229
|
+
|
230
|
+
context "for NoMethodError inside simulator" do
|
231
|
+
let(:out_output) do
|
232
|
+
proc do |binding, clock|
|
233
|
+
{ val: binding[:process].value.name.no_such_method_exists , ts: clock + 10 }
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
it_behaves_like 'error handler'
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "#stop" do
|
244
|
+
AppProcess = Struct.new(:name)
|
245
|
+
CPU = Struct.new(:name, :process)
|
246
|
+
|
247
|
+
let(:net) do
|
248
|
+
n = FastTCPN::TCPN.new
|
249
|
+
n
|
250
|
+
end
|
251
|
+
|
252
|
+
let(:process_count) { 10 }
|
253
|
+
let(:cpu_count) { 10 }
|
254
|
+
|
255
|
+
let(:process) { net.place :process, name: :name }
|
256
|
+
let(:cpu) { net.place :cpu, name: :name, process: :process }
|
257
|
+
let(:out) { net.place :out }
|
258
|
+
let(:finished) { net.place :finished }
|
259
|
+
|
260
|
+
|
261
|
+
before do
|
262
|
+
t1 = net.transition :work
|
263
|
+
t1.sentry do |marking_for, clock, result|
|
264
|
+
marking_for[:process].each do |p|
|
265
|
+
marking_for[:cpu].each(:process, p.value.name) do |c|
|
266
|
+
result << { process: p, cpu: c }
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
t1.input process
|
271
|
+
t1.input cpu
|
272
|
+
t1.output out do |binding|
|
273
|
+
binding[:process].value.name + "_done"
|
274
|
+
end
|
275
|
+
t1.output cpu do |binding|
|
276
|
+
binding[:cpu]
|
277
|
+
end
|
278
|
+
|
279
|
+
t2 = net.transition :finish
|
280
|
+
t2.input out
|
281
|
+
t2.output finished do |binding|
|
282
|
+
binding[:out]
|
283
|
+
end
|
284
|
+
|
285
|
+
process_count.times do |p|
|
286
|
+
process.add AppProcess.new(p.to_s)
|
287
|
+
cpu_count.times.map { |c| cpu.add CPU.new("CPU#{c}_#{p}", p.to_s) }
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
it "stops running simulator" do
|
293
|
+
transitions = []
|
294
|
+
net.cb_for :transition, :after do |t, e|
|
295
|
+
transitions << e.transition
|
296
|
+
if transitions.size == 2
|
297
|
+
net.stop
|
298
|
+
end
|
299
|
+
end
|
300
|
+
net.sim
|
301
|
+
expect(transitions.size).to eq 2
|
302
|
+
end
|
303
|
+
|
304
|
+
describe "#stopped?" do
|
305
|
+
it "is true if simulation was stopped by #stop" do
|
306
|
+
transitions = []
|
307
|
+
net.cb_for :transition, :after do |t, e|
|
308
|
+
transitions << e.transition
|
309
|
+
if transitions.size == 2
|
310
|
+
net.stop
|
311
|
+
end
|
312
|
+
end
|
313
|
+
net.sim
|
314
|
+
expect(net.stopped?).to be true
|
315
|
+
end
|
316
|
+
|
317
|
+
it "is false if simulation finished without #stop" do
|
318
|
+
net.sim
|
319
|
+
expect(net.stopped?).to be false
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|