shrewd 0.0.0
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 +11 -0
- data/README.md +416 -0
- data/Rakefile +0 -0
- data/lib/shrewd.rb +1 -0
- data/lib/shrewd/entities/event.rb +56 -0
- data/lib/shrewd/entities/process.rb +83 -0
- data/lib/shrewd/entities/variable.rb +58 -0
- data/lib/shrewd/simulation.rb +166 -0
- data/shrewd.gemspec +14 -0
- data/spec/integration/simple_spec.rb +145 -0
- data/spec/models/event_spec.rb +50 -0
- data/spec/models/process_spec.rb +51 -0
- data/spec/models/simulation_spec.rb +180 -0
- data/spec/models/variable_spec.rb +195 -0
- data/spec/spec_helper.rb +15 -0
- metadata +61 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Event
|
|
2
|
+
#
|
|
3
|
+
# An Event changes the state of one or more simulation resources.
|
|
4
|
+
# Each event also has one or more processes.
|
|
5
|
+
#
|
|
6
|
+
# e.g. When modeling a queue, an Event would exist that denotes the
|
|
7
|
+
# arrival of a new customer, thereby changing the state of the system
|
|
8
|
+
# by incrementing the queue by 1.
|
|
9
|
+
#
|
|
10
|
+
# Instance Variables:
|
|
11
|
+
#
|
|
12
|
+
# - @name Name of the event.
|
|
13
|
+
# e.g. @name = 'Customer Arrival'
|
|
14
|
+
#
|
|
15
|
+
# - @description Description of the event.
|
|
16
|
+
# e.g. @description = 'The arrival of a single customer
|
|
17
|
+
# to the queue.'
|
|
18
|
+
#
|
|
19
|
+
# - @processes An array of Shrewd::Process objects that are to be
|
|
20
|
+
# executed following this event. These processes will
|
|
21
|
+
# schedule future events.
|
|
22
|
+
# e.g. @processes = [ Shrewd::Process.new('Process') ]
|
|
23
|
+
#
|
|
24
|
+
# - @action A proc that is called to modify system and local state.
|
|
25
|
+
# This is the method by which events modify the system.
|
|
26
|
+
# The system state variables are injected.
|
|
27
|
+
# e.g. @action = Proc.new do |variables|
|
|
28
|
+
# variables[:queue].decrement
|
|
29
|
+
# variables[:employees].decrement
|
|
30
|
+
# end
|
|
31
|
+
|
|
32
|
+
class Shrewd::Event
|
|
33
|
+
attr_accessor :name, :description, :processes, :action
|
|
34
|
+
|
|
35
|
+
# Initialize the event
|
|
36
|
+
#
|
|
37
|
+
# Input:
|
|
38
|
+
# - name name of the event
|
|
39
|
+
# - description description of the event (optional)
|
|
40
|
+
# - processes processes to be executed after event
|
|
41
|
+
# - action proc that changes the state of system variables
|
|
42
|
+
def initialize(name, description = "", processes = [], action = nil)
|
|
43
|
+
@name = name
|
|
44
|
+
@description = description
|
|
45
|
+
@processes = processes
|
|
46
|
+
@action = action
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Trigger the event's action
|
|
50
|
+
#
|
|
51
|
+
# Input:
|
|
52
|
+
# - state_variables system state variables
|
|
53
|
+
def trigger(state_variables)
|
|
54
|
+
@action.call(state_variables) if @action
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Process
|
|
2
|
+
#
|
|
3
|
+
# A Process is triggered by an Event to schedule another Event.
|
|
4
|
+
# Each Process schedules an event in a given (or randomized) period of time.
|
|
5
|
+
# A Process can have conditions that determine if the associated Event
|
|
6
|
+
# should be queued.
|
|
7
|
+
#
|
|
8
|
+
# e.g. When modeling customer arrivals to a retail store, each customer
|
|
9
|
+
# arrival Event would create a process that schedules a service Event,
|
|
10
|
+
# in which a employee would service the customer.
|
|
11
|
+
#
|
|
12
|
+
# Instance Variables:
|
|
13
|
+
#
|
|
14
|
+
# - @name Name of the process.
|
|
15
|
+
# e.g. @name = 'Service Customer'
|
|
16
|
+
#
|
|
17
|
+
# - @description Description of the process.
|
|
18
|
+
# e.g. @description = 'An employee servicing a customer.'
|
|
19
|
+
#
|
|
20
|
+
# - @event Event that the process triggers after checking conditions.
|
|
21
|
+
# e.g. @event = Shrewd::Event.new('Target')
|
|
22
|
+
#
|
|
23
|
+
# - @conditions A proc that returns a boolean. If the result is true then
|
|
24
|
+
# the @event is added to the event queue. The proc is
|
|
25
|
+
# provided the system state variables.
|
|
26
|
+
# e.g. @conditions = Proc.new do |variables|
|
|
27
|
+
# if variables[:employees] > 0
|
|
28
|
+
# true
|
|
29
|
+
# else
|
|
30
|
+
# false
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# - @delay A proc that returns a number. This number determines how
|
|
35
|
+
# far in the future the event should be scheduled to
|
|
36
|
+
# execute (relative to current time).
|
|
37
|
+
# e.g. @delay = Proc.new do |variables|
|
|
38
|
+
# if variables[:queue] > 5
|
|
39
|
+
# 15
|
|
40
|
+
# else
|
|
41
|
+
# 10
|
|
42
|
+
# end
|
|
43
|
+
# end
|
|
44
|
+
|
|
45
|
+
class Shrewd::Process
|
|
46
|
+
attr_accessor :name, :description, :event,
|
|
47
|
+
:conditions, :delay
|
|
48
|
+
|
|
49
|
+
# Initialize the Process
|
|
50
|
+
#
|
|
51
|
+
# Input:
|
|
52
|
+
# - name name of the process
|
|
53
|
+
# - description description of the process
|
|
54
|
+
# - event event that this process will trigger
|
|
55
|
+
# - conditions proc that returns bool to validate event scheduling
|
|
56
|
+
# - delay proc that returns the time to schedule the event
|
|
57
|
+
def initialize(name, description = '', event = nil,
|
|
58
|
+
conditions = nil, delay = nil)
|
|
59
|
+
@name = name
|
|
60
|
+
@description = description
|
|
61
|
+
@event = event
|
|
62
|
+
@conditions = conditions
|
|
63
|
+
@delay = delay
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Validate the conditions to determine if event should be scheduled
|
|
67
|
+
def validated?(state_variables)
|
|
68
|
+
if @conditions
|
|
69
|
+
@conditions.call(state_variables)
|
|
70
|
+
else
|
|
71
|
+
true
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Retrieve the calculated delay time
|
|
76
|
+
def delay_time(state_variables)
|
|
77
|
+
if @delay
|
|
78
|
+
@delay.call(state_variables)
|
|
79
|
+
else
|
|
80
|
+
0
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Variable
|
|
2
|
+
#
|
|
3
|
+
# A Variable represents a state variable of the simulation system.
|
|
4
|
+
# Each Variable has a finite quantity and can be incremented, decremented, or
|
|
5
|
+
# assigned over the course of the simulation. A Variable can also have a boolean
|
|
6
|
+
# value.
|
|
7
|
+
#
|
|
8
|
+
# e.g. For a supermarket checkout simulation, a finite Variable
|
|
9
|
+
# would be the number of cashiers.
|
|
10
|
+
#
|
|
11
|
+
# Instance Variables:
|
|
12
|
+
# - @value integer value of variable
|
|
13
|
+
# e.g. @value = 5
|
|
14
|
+
# - @name name of the variable
|
|
15
|
+
# e.g. @name = 'Queue'
|
|
16
|
+
# - @description description of the variable
|
|
17
|
+
# e.g. @description = 'Number of customers in line.'
|
|
18
|
+
|
|
19
|
+
class Shrewd::Variable
|
|
20
|
+
include Comparable
|
|
21
|
+
attr_accessor :name, :description, :value
|
|
22
|
+
|
|
23
|
+
# Initialize the variable
|
|
24
|
+
#
|
|
25
|
+
# Input:
|
|
26
|
+
# - name name of the variable
|
|
27
|
+
# - description description of the variable (optional)
|
|
28
|
+
# - default default value for variable (optional)
|
|
29
|
+
def initialize(name, description = '', default = 0)
|
|
30
|
+
@value = default
|
|
31
|
+
@name = name
|
|
32
|
+
@description = description
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Increment the value of the variable
|
|
36
|
+
#
|
|
37
|
+
# Input:
|
|
38
|
+
# - amount the amount to increment the variable (optional)
|
|
39
|
+
def increment(amount = 1)
|
|
40
|
+
@value += amount
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Decrement the value of the variable
|
|
44
|
+
#
|
|
45
|
+
# Input:
|
|
46
|
+
# - amount the amount to decrement the variable (optional)
|
|
47
|
+
def decrement(amount = 1)
|
|
48
|
+
@value -= amount
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Compare variable value to number
|
|
52
|
+
#
|
|
53
|
+
# Input:
|
|
54
|
+
# - val the value to be compared with
|
|
55
|
+
def <=>(val)
|
|
56
|
+
@value <=> val
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Simulation
|
|
2
|
+
#
|
|
3
|
+
# A Simulation is a container for a simulation that holds the current
|
|
4
|
+
# state of the system via state variables as well as holds the pending
|
|
5
|
+
# and historical events of the simulation.
|
|
6
|
+
#
|
|
7
|
+
# A simulation can be started and stopped with a Simulation instance.
|
|
8
|
+
#
|
|
9
|
+
# One or multiple initial Processes must be provided to the Simulation
|
|
10
|
+
# which will started at time 0.
|
|
11
|
+
#
|
|
12
|
+
# A Simulation can also be stepped through once stopped to observe the system
|
|
13
|
+
# state changes one event at a time.
|
|
14
|
+
#
|
|
15
|
+
# Instance Variables:
|
|
16
|
+
# - @variables a hash of system variables whose key is the snake case
|
|
17
|
+
# name of the provided variable, and the value is the
|
|
18
|
+
# variable
|
|
19
|
+
#
|
|
20
|
+
# e.g. @variables = [ Shrewd::Variable.new('Queue')]
|
|
21
|
+
#
|
|
22
|
+
# - @initial_processes the initial process
|
|
23
|
+
# e.g. @initial_processes = [Shrewd::Process.new('proc')]
|
|
24
|
+
#
|
|
25
|
+
# - @queue array of hashes of events to be processed and their
|
|
26
|
+
# respective times of execution
|
|
27
|
+
# e.g. @queue = [ { time: @clock,
|
|
28
|
+
# event: Shrewd::Event.new('Event') }]
|
|
29
|
+
#
|
|
30
|
+
# - @log array of events and their time of execution
|
|
31
|
+
# e.g. @log << { event: Shrewd::Event.new, time: 15 }
|
|
32
|
+
#
|
|
33
|
+
# - @clock int value representing current clock time
|
|
34
|
+
# e.g. @clock = 5
|
|
35
|
+
module Shrewd
|
|
36
|
+
class Simulation
|
|
37
|
+
attr_accessor :initial_processes
|
|
38
|
+
|
|
39
|
+
# Initialize the simulation
|
|
40
|
+
#
|
|
41
|
+
# Input:
|
|
42
|
+
# - variables hash of the initial state of simulation state variables
|
|
43
|
+
# - initial initial process that will be triggered
|
|
44
|
+
#
|
|
45
|
+
# Return:
|
|
46
|
+
# - [Shrewd::Simulation]: new simulation instance
|
|
47
|
+
def initialize(variables = [], initial = nil)
|
|
48
|
+
@variables = {}
|
|
49
|
+
@initial_processes = initial
|
|
50
|
+
@queue = []
|
|
51
|
+
@log = []
|
|
52
|
+
@clock = 0
|
|
53
|
+
|
|
54
|
+
# assign variables
|
|
55
|
+
variables.each do |variable|
|
|
56
|
+
name = variable.name.downcase.gsub(/\s+/,"_").to_sym
|
|
57
|
+
@variables[name] = variable
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# save initial state
|
|
61
|
+
@initial_state = @variables
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Start the simulation with a default length of 1000
|
|
65
|
+
def start(length = 1000)
|
|
66
|
+
create_initial_event
|
|
67
|
+
|
|
68
|
+
while @queue.any?
|
|
69
|
+
# Set the clock
|
|
70
|
+
event = @queue.shift
|
|
71
|
+
@clock = event[:time].to_i
|
|
72
|
+
break if @clock > length
|
|
73
|
+
|
|
74
|
+
# Retrieve current event
|
|
75
|
+
current_event = event[:event]
|
|
76
|
+
current_event.trigger(@variables)
|
|
77
|
+
|
|
78
|
+
# Queue events spawned by processes
|
|
79
|
+
current_event.processes.each do |process|
|
|
80
|
+
if process.validated?(@variables)
|
|
81
|
+
process_time = process.delay_time(@variables)
|
|
82
|
+
add_to_queue({ event: process.event, time: @clock + process_time })
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Log the event
|
|
87
|
+
@log << { event: current_event, time: @clock }
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Print a log of the simulation events
|
|
92
|
+
def print_log
|
|
93
|
+
@log
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Reset the simulation
|
|
97
|
+
def reset
|
|
98
|
+
@log = []
|
|
99
|
+
@queue = []
|
|
100
|
+
@clock = 0
|
|
101
|
+
@variables = @initial_state
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
# Create the 'start' event of the simulation. The start event will call
|
|
106
|
+
# the initial process.
|
|
107
|
+
def create_initial_event
|
|
108
|
+
name = 'Start'
|
|
109
|
+
description = 'The first event of the simulation.'
|
|
110
|
+
|
|
111
|
+
initial_event = Shrewd::Event.new(name, description)
|
|
112
|
+
initial_event.processes = @initial_processes
|
|
113
|
+
add_to_queue({ event: initial_event, time: @count })
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Add event to queue according to time of event.
|
|
117
|
+
#
|
|
118
|
+
# Input:
|
|
119
|
+
# - event the event/time hash to add to the queue
|
|
120
|
+
def add_to_queue(event)
|
|
121
|
+
size = @queue.size
|
|
122
|
+
return @queue << event if size == 0
|
|
123
|
+
|
|
124
|
+
# binary search
|
|
125
|
+
index = search(event, 0, @queue.size - 1)
|
|
126
|
+
if index > @queue.size - 1
|
|
127
|
+
@queue.push(event)
|
|
128
|
+
else
|
|
129
|
+
@queue.insert(index, event)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Binary search to identify location in queue to add event, based on
|
|
134
|
+
# the time the event will be triggered.
|
|
135
|
+
#
|
|
136
|
+
# Input:
|
|
137
|
+
# - event the event/time hash to add to the queue
|
|
138
|
+
# - lo low index in the search
|
|
139
|
+
# - hi high index in the search
|
|
140
|
+
def search(event, lo, hi)
|
|
141
|
+
# Low and high values
|
|
142
|
+
low_time = @queue[lo][:time]
|
|
143
|
+
high_time = @queue[hi][:time]
|
|
144
|
+
|
|
145
|
+
# Event time is higher or lower than bounds
|
|
146
|
+
return lo if low_time > event[:time]
|
|
147
|
+
return hi + 1 if high_time <= event[:time]
|
|
148
|
+
return lo if hi == lo
|
|
149
|
+
|
|
150
|
+
# Midpoint
|
|
151
|
+
midpoint = ((lo + hi) / 2).floor
|
|
152
|
+
|
|
153
|
+
# Compare event to midpoint
|
|
154
|
+
if event[:time] < @queue[midpoint][:time]
|
|
155
|
+
search(event, 0, midpoint - 1)
|
|
156
|
+
else
|
|
157
|
+
search(event, midpoint + 1, @queue.size - 1)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# require simulation entities
|
|
164
|
+
require_relative 'entities/event'
|
|
165
|
+
require_relative 'entities/process'
|
|
166
|
+
require_relative 'entities/variable'
|
data/shrewd.gemspec
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = 'shrewd'
|
|
3
|
+
s.version = '0.0.0'
|
|
4
|
+
s.date = '2015-10-23'
|
|
5
|
+
s.summary = 'A gem for event-based discrete event simulations.'
|
|
6
|
+
s.description = 'A gem for that provides an easy interface to build ' +
|
|
7
|
+
'and run discrete event simulations. Shrewd uses an ' +
|
|
8
|
+
'event-based approach to designing simulations.'
|
|
9
|
+
s.authors = 'Sherbie'
|
|
10
|
+
s.files = `git ls-files`.split("\n")
|
|
11
|
+
s.require_path = 'lib'
|
|
12
|
+
s.homepage = 'http://rubygems.org/gems/shrewd'
|
|
13
|
+
s.license = 'MIT'
|
|
14
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# SimpleSpec
|
|
2
|
+
#
|
|
3
|
+
# Simulation integrated tests through examples.
|
|
4
|
+
|
|
5
|
+
require 'shrewd/simulation'
|
|
6
|
+
|
|
7
|
+
describe "simulation" do
|
|
8
|
+
|
|
9
|
+
# Basic manufacturing example
|
|
10
|
+
#
|
|
11
|
+
# Uses a widget manufacturing example to test a simple simulation. Widgets
|
|
12
|
+
# will arrive into the system at a uniformly randomized time. An available
|
|
13
|
+
# machine will service each incoming widget. If there are not enough available
|
|
14
|
+
# machines at the time a widget arrives, then the widget will sit in the
|
|
15
|
+
# queue. The simulation will be run for a short amount of time, and its
|
|
16
|
+
# results verified.
|
|
17
|
+
context "manufacturing" do
|
|
18
|
+
before :all do
|
|
19
|
+
# Create system variables
|
|
20
|
+
queue = Shrewd::Variable.new('Queue')
|
|
21
|
+
machines = Shrewd::Variable.new('Machines', 'Machines', 5)
|
|
22
|
+
variables = [queue, machines]
|
|
23
|
+
|
|
24
|
+
# Create events
|
|
25
|
+
widget_arrival = Shrewd::Event.new('Widget Arrival',
|
|
26
|
+
'Widget arrives to manufacturer.')
|
|
27
|
+
widget_arrival.action = Proc.new do |variables|
|
|
28
|
+
variables[:queue].increment
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
start = Shrewd::Event.new('Start Service', 'Start widget service')
|
|
32
|
+
start.action = Proc.new do |variables|
|
|
33
|
+
variables[:queue].decrement
|
|
34
|
+
variables[:machines].decrement
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
finish = Shrewd::Event.new('Finish Service', 'Finish widget service')
|
|
38
|
+
finish.action = Proc.new do |variables|
|
|
39
|
+
variables[:machines].increment
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# create processes
|
|
43
|
+
arrival = Shrewd::Process.new('Arrival', 'Arrival of a Widget')
|
|
44
|
+
arrival.delay = Proc.new { |variables| 10 }
|
|
45
|
+
arrival.event = widget_arrival
|
|
46
|
+
|
|
47
|
+
start_service = Shrewd::Process.new('Start Service',
|
|
48
|
+
'Start Widget Service.')
|
|
49
|
+
start_service.delay = Proc.new { |variables| 0 }
|
|
50
|
+
start_service.conditions = Proc.new do |variables|
|
|
51
|
+
variables[:machines] > 0 && variables[:queue] > 0
|
|
52
|
+
end
|
|
53
|
+
start_service.event = start
|
|
54
|
+
|
|
55
|
+
service_widget = Shrewd::Process.new('Service', 'Servicing a Widget')
|
|
56
|
+
service_widget.delay = Proc.new { |variables| 15 }
|
|
57
|
+
service_widget.event = finish
|
|
58
|
+
|
|
59
|
+
# Add processes to events
|
|
60
|
+
widget_arrival.processes << arrival << start_service
|
|
61
|
+
start.processes << service_widget
|
|
62
|
+
finish.processes << start_service
|
|
63
|
+
initial_processes = [ arrival ]
|
|
64
|
+
|
|
65
|
+
# Create simulation
|
|
66
|
+
@simulation = Shrewd::Simulation.new(variables, initial_processes)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "creates a simulation" do
|
|
70
|
+
expect(@simulation).to be_an_instance_of Shrewd::Simulation
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "initializes processes" do
|
|
74
|
+
expect(@simulation.initial_processes).to be_an_instance_of Array
|
|
75
|
+
@simulation.initial_processes.each do |process|
|
|
76
|
+
expect(process).to be_an_instance_of Shrewd::Process
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe "when run" do
|
|
81
|
+
before :all do
|
|
82
|
+
@simulation_length = 1000
|
|
83
|
+
@simulation.start(@simulation_length)
|
|
84
|
+
@log = @simulation.print_log
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "does not last longer than simulation length" do
|
|
88
|
+
expect(@log.last[:time]).to be <= @simulation_length
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "has more or equal widgets starts than finishes" do
|
|
92
|
+
starts = 0
|
|
93
|
+
finishes = 0
|
|
94
|
+
|
|
95
|
+
@log.each do |entry|
|
|
96
|
+
if entry[:event].name == 'Start Service'
|
|
97
|
+
starts += 1
|
|
98
|
+
elsif entry[:event].name == 'Finish Service'
|
|
99
|
+
finishes += 1
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
expect(starts).to be >= finishes
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
expect(starts).to be > 0
|
|
106
|
+
expect(finishes).to be > 0
|
|
107
|
+
expect(starts).to be >= finishes
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "has more or equal widget arrivals than starts" do
|
|
111
|
+
starts = 0
|
|
112
|
+
arrivals = 0
|
|
113
|
+
|
|
114
|
+
@log.each do |entry|
|
|
115
|
+
if entry[:event].name == 'Start Service'
|
|
116
|
+
starts += 1
|
|
117
|
+
elsif entry[:event].name == 'Widget Arrival'
|
|
118
|
+
arrivals += 1
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
expect(arrivals).to be >= starts
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
expect(arrivals).to be > 0
|
|
125
|
+
expect(starts).to be > 0
|
|
126
|
+
expect(arrivals).to be >= starts
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "executes events in order" do
|
|
130
|
+
clock = 0
|
|
131
|
+
|
|
132
|
+
@log.each do |entry|
|
|
133
|
+
expect(entry[:time]).to be >= clock
|
|
134
|
+
clock = entry[:time]
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
after :all do
|
|
139
|
+
@log.each do |entry|
|
|
140
|
+
puts "time #{entry[:time]} - #{entry[:event].name}"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|