pg-verify 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -3
- data/README.md +1 -1
- data/data/project-template/README.md +81 -0
- data/doc/examples/vending_machine/checkpoint_1.rb +29 -0
- data/doc/examples/vending_machine/checkpoint_2.rb +47 -0
- data/doc/examples/vending_machine/checkpoint_3.rb +68 -0
- data/doc/examples/vending_machine/checkpoint_4.rb +202 -0
- data/integration_tests/ruby_dsl/001_states.rb +2 -1
- data/integration_tests/ruby_dsl/002_transitions.rb +2 -1
- data/integration_tests/ruby_dsl/017_ctl_specifications.rb +19 -0
- data/integration_tests/ruby_dsl/018_non_atomic_hazard.rb +17 -0
- data/integration_tests/ruby_dsl/019_multiple_actions.rb +21 -0
- data/integration_tests/ruby_dsl/020_vending_machine.rb +188 -0
- data/lib/pg-verify/cli/cli.rb +80 -24
- data/lib/pg-verify/cli/cli_utils.rb +61 -0
- data/lib/pg-verify/interpret/component_context.rb +1 -1
- data/lib/pg-verify/interpret/interpret.rb +1 -1
- data/lib/pg-verify/interpret/pg_script.rb +1 -0
- data/lib/pg-verify/model/parsed_expression.rb +10 -5
- data/lib/pg-verify/model/simulation/trace.rb +15 -4
- data/lib/pg-verify/model/validation/errors.rb +21 -2
- data/lib/pg-verify/nusmv/runner.rb +69 -17
- data/lib/pg-verify/puml/puml.rb +1 -0
- data/lib/pg-verify/puml/runner.rb +0 -0
- data/lib/pg-verify/transform/hash_transformation.rb +46 -14
- data/lib/pg-verify/transform/nusmv_transformation.rb +4 -3
- data/lib/pg-verify/transform/puml_transformation.rb +27 -8
- data/lib/pg-verify/version.rb +1 -1
- data/vscript.rb +64 -0
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61be58b97dd5e5beb6546f87ef6b2f19b2d372e983fb3b93332c88a0f0b489ad
|
4
|
+
data.tar.gz: 6fc1e14c40748353219ca58d69b034b2970c57da5d19f2ad27ee842c66847035
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbf7170ad7ebff0f939a1ed3309d82ae45246cfbc670475be37f7c0f72f17e516d5e9a73427ebb23dc5403de48693f4dc26db194ac299523af0c119063c75cc4
|
7
|
+
data.tar.gz: c0b0b0c88b8e4a54465c857f5daaaa06826e69c2d9c2149e248863b2d8e627ab6348b59e842352ea24a3c453a0319d9d6054205337ab6de6c5bd88fa6f395047
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pg-verify (0.1.
|
4
|
+
pg-verify (0.1.2)
|
5
5
|
config (~> 4.2.1)
|
6
6
|
plantuml_builder (~> 0.3.0)
|
7
7
|
rainbow (~> 3.0.0)
|
@@ -10,7 +10,7 @@ PATH
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
-
concurrent-ruby (1.2.
|
13
|
+
concurrent-ruby (1.2.3)
|
14
14
|
config (4.2.1)
|
15
15
|
deep_merge (~> 1.2, >= 1.2.1)
|
16
16
|
dry-validation (~> 1.0, >= 1.0.0)
|
@@ -66,7 +66,7 @@ GEM
|
|
66
66
|
diff-lcs (>= 1.2.0, < 2.0)
|
67
67
|
rspec-support (~> 3.12.0)
|
68
68
|
rspec-support (3.12.0)
|
69
|
-
thor (1.2.
|
69
|
+
thor (1.2.2)
|
70
70
|
|
71
71
|
PLATFORMS
|
72
72
|
x86_64-darwin-19
|
data/README.md
CHANGED
@@ -26,4 +26,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
26
26
|
|
27
27
|
## Contributing
|
28
28
|
|
29
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
29
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/aweber/pg-verify.
|
@@ -7,6 +7,7 @@ To get started you can run `pg-verify doctor` to verify your installation and gu
|
|
7
7
|
you through the steps needed to install external addons like the NuSMV model checker.
|
8
8
|
|
9
9
|
You can always run `pg-verify help` to get a list of available commands.
|
10
|
+
Run `pg-verify help <command>` to get more information about a specific command and its options.
|
10
11
|
For example try running `pg-verify show png` to render a PNG image of your program graph
|
11
12
|
and save that to your working directory.
|
12
13
|
|
@@ -15,4 +16,84 @@ and save that to your working directory.
|
|
15
16
|
There are a couple of prelude files and directories in your project to get you started:
|
16
17
|
|
17
18
|
- `program-graph.rb`: This file defines the default program graph you will be working on
|
19
|
+
- `.pg-verify.yml`: This file can be used to configure pg-verify
|
18
20
|
- `addon/`: This is the directory where you will place addon resources like the NuSMV executable.
|
21
|
+
|
22
|
+
## Writing specifications
|
23
|
+
|
24
|
+
The specification framework for the Ruby DSL is inspired by the popular
|
25
|
+
testing library [rspec](https://rspec.info/). Here is an example of the syntax:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
# Wrap two specifications which are related to some "Car" model.
|
29
|
+
specify "The car" do
|
30
|
+
it "won't crash" => :"G distance_to_tree > 0" # Becomes: The car won't crash
|
31
|
+
it "will drive" => :"F velocity > 0" # Becomes: The car will drive
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
You wrap your specification using one or more `specify` blocks.
|
36
|
+
Within those blocks you can declare a *specification* using the `it` keyword.
|
37
|
+
Each specification consists of two parts:
|
38
|
+
- A **text** which is used to describe the spec in natural language
|
39
|
+
- An **expression** given in LTL or CTL. This will actually be checked
|
40
|
+
|
41
|
+
The outer block serves as a wrapper for specifications which are related.
|
42
|
+
In the example above we declare two specifications which are both concerning
|
43
|
+
some "Car" model.
|
44
|
+
|
45
|
+
You can read those specifications from outside to inside.
|
46
|
+
The `it` semantically refers to the outer block (the car in our case).
|
47
|
+
So when reading `it "won't crash"`, **it** refers to **the car**.
|
48
|
+
Thus this specification becomes "The car won't crash" when it is expanded.
|
49
|
+
|
50
|
+
### Assumption blocks
|
51
|
+
|
52
|
+
You can consolidate multiple preconditions for your specifications into
|
53
|
+
an `assuming` block. Here's the syntax:
|
54
|
+
|
55
|
+
```Ruby
|
56
|
+
specify "The car" do
|
57
|
+
assuming "the breaks don't fail" => :"G BreakFailure == No" do
|
58
|
+
it "won't crash" => :"G distance_to_tree > 0" # Becomes: The car (assuming the breaks don't fail) won't crash
|
59
|
+
it "will drive" => :"F velocity > 0" # Becomes: The car (assuming the breaks don't fail) will drive
|
60
|
+
end
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
Much like the specifications themselves, the assuming block expects a `text` and an `expression`.
|
65
|
+
For each contained specification, the assumption expression will be prepended to the spec
|
66
|
+
while expanding. Thus for the example above we get two expanded specifications:
|
67
|
+
|
68
|
+
```
|
69
|
+
1) The car (assuming the breaks don't fail) won't crash
|
70
|
+
( G BreakFailure == No ) => G distance_to_tree > 0
|
71
|
+
|
72
|
+
2) The car (assuming the breaks don't fail) will drive
|
73
|
+
( G BreakFailure == No ) => F velocity > 0
|
74
|
+
```
|
75
|
+
|
76
|
+
### Assuming no errors
|
77
|
+
|
78
|
+
The imagined use case for the `assume` block is to specify model properties under exclusion of errors.
|
79
|
+
|
80
|
+
```Ruby
|
81
|
+
specify "The car" do
|
82
|
+
assuming no_errors do
|
83
|
+
it "won't crash" => :"G distance_to_tree > 0" # Becomes: The car (assuming the breaks don't fail) won't crash
|
84
|
+
it "will drive" => :"F velocity > 0" # Becomes: The car (assuming the breaks don't fail) will drive
|
85
|
+
end
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
The thing that's new here is the `no_errors` keyword.
|
90
|
+
All this does is generate a *text* and *expression* to be used by the `assuming` block.
|
91
|
+
Say you had three error graphs: `BreakFailure`, `UsageFailure` and `MotorFailure`.
|
92
|
+
Then `no_errors` would produce this expression:
|
93
|
+
|
94
|
+
```
|
95
|
+
G ( BreakFailure == No && UsageFailure == No && MotorFailure == No )
|
96
|
+
```
|
97
|
+
|
98
|
+
Instead of `no_errors` you can also use `only(...)` to limit the possibility of errors to the
|
99
|
+
ones you provide. e.g: `assuming only(:BreakFailure, :UsageFailure) do ...`.
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Create graphs with default states
|
2
|
+
|
3
|
+
model :VendingMachine do
|
4
|
+
|
5
|
+
graph :User do
|
6
|
+
states :todo
|
7
|
+
end
|
8
|
+
|
9
|
+
graph :CoinReader do
|
10
|
+
states :todo
|
11
|
+
end
|
12
|
+
|
13
|
+
graph :Controller do
|
14
|
+
states :todo
|
15
|
+
end
|
16
|
+
|
17
|
+
graph :LED do
|
18
|
+
states :todo
|
19
|
+
end
|
20
|
+
|
21
|
+
graph :Dispenser do
|
22
|
+
states :todo
|
23
|
+
end
|
24
|
+
|
25
|
+
graph :CoinDispenser do
|
26
|
+
states :todo
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Add products and coins
|
2
|
+
|
3
|
+
model :VendingMachine do
|
4
|
+
|
5
|
+
products = {
|
6
|
+
cola: 1.2,
|
7
|
+
chips: 2.5
|
8
|
+
}
|
9
|
+
products = products.map { |c, v| [c, (v * 10).to_i] }.to_h
|
10
|
+
|
11
|
+
coins = {
|
12
|
+
ten_cents: 10,
|
13
|
+
twenty_cents: 20,
|
14
|
+
fifty_cents: 50,
|
15
|
+
one_euro: 100,
|
16
|
+
two_euro: 200
|
17
|
+
}
|
18
|
+
coins = coins.map { |c, v| [c, (v / 10).to_i] }.to_h
|
19
|
+
|
20
|
+
MAX_MONEY = 100
|
21
|
+
START_MONEY = 20
|
22
|
+
|
23
|
+
graph :User do
|
24
|
+
states :todo
|
25
|
+
end
|
26
|
+
|
27
|
+
graph :CoinReader do
|
28
|
+
states :todo
|
29
|
+
end
|
30
|
+
|
31
|
+
graph :Controller do
|
32
|
+
states :todo
|
33
|
+
end
|
34
|
+
|
35
|
+
graph :LED do
|
36
|
+
states :todo
|
37
|
+
end
|
38
|
+
|
39
|
+
graph :Dispenser do
|
40
|
+
states :todo
|
41
|
+
end
|
42
|
+
|
43
|
+
graph :CoinDispenser do
|
44
|
+
states :todo
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Add states and variables
|
2
|
+
|
3
|
+
model :VendingMachine do
|
4
|
+
|
5
|
+
products = {
|
6
|
+
cola: 1.2,
|
7
|
+
chips: 2.5
|
8
|
+
}
|
9
|
+
products = products.map { |c, v| [c, (v * 10).to_i] }.to_h
|
10
|
+
|
11
|
+
coins = {
|
12
|
+
ten_cents: 10,
|
13
|
+
twenty_cents: 20,
|
14
|
+
fifty_cents: 50,
|
15
|
+
one_euro: 100,
|
16
|
+
two_euro: 200
|
17
|
+
}
|
18
|
+
coins = coins.map { |c, v| [c, (v / 10).to_i] }.to_h
|
19
|
+
|
20
|
+
|
21
|
+
MAX_MONEY = 100
|
22
|
+
START_MONEY = 20
|
23
|
+
|
24
|
+
graph :User do
|
25
|
+
press_states = products.keys.map { |product| :"press_#{product}" }
|
26
|
+
grab_states = products.keys.map { |product| :"grab_#{product}" }
|
27
|
+
insert_states = coins.keys.map { |coin| :"insert_#{coin}" }
|
28
|
+
|
29
|
+
var pocket_money: (0..MAX_MONEY), init: START_MONEY
|
30
|
+
var value_in_products: (0..MAX_MONEY), init: 0
|
31
|
+
|
32
|
+
states :inserting, *insert_states, \
|
33
|
+
:pressing, *press_states, \
|
34
|
+
:waiting, :grab_product, \
|
35
|
+
:grab_change, \
|
36
|
+
:done, \
|
37
|
+
init: :inserting
|
38
|
+
end
|
39
|
+
|
40
|
+
graph :CoinReader do
|
41
|
+
states :reading
|
42
|
+
|
43
|
+
var read_value: (0..coins.values.max), init: 0
|
44
|
+
end
|
45
|
+
|
46
|
+
graph :Controller do
|
47
|
+
var budget: (0..MAX_MONEY), init: 0
|
48
|
+
|
49
|
+
dispense_states = products.keys.map { |product| :"dispense_#{product}" }
|
50
|
+
states :accepting, *dispense_states, :rejected, :done, init: :accepting
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
graph :LED do
|
55
|
+
states :idle, :red, :green, init: :idle
|
56
|
+
end
|
57
|
+
|
58
|
+
graph :Dispenser do
|
59
|
+
dispensed_states = products.keys.map { |product| :"dispensed_#{product}" }
|
60
|
+
states :empty, *dispensed_states, init: :empty
|
61
|
+
end
|
62
|
+
|
63
|
+
graph :CoinDispenser do
|
64
|
+
states :idle, :dispensed_change, init: :idle
|
65
|
+
var change: (0..MAX_MONEY), init: 0
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
# Finish it all up
|
2
|
+
|
3
|
+
# CONFIG
|
4
|
+
# trace:
|
5
|
+
# colors:
|
6
|
+
# insert_*: green
|
7
|
+
# press_*: yellow
|
8
|
+
# dispense_*: yellow
|
9
|
+
# accepting: sidenote
|
10
|
+
# idle: sidenote
|
11
|
+
# empty: sidenote
|
12
|
+
# reading: sidenote
|
13
|
+
|
14
|
+
model :VendingMachine do
|
15
|
+
|
16
|
+
products = {
|
17
|
+
cola: 1.2,
|
18
|
+
chips: 2.5
|
19
|
+
}.map { |c, v| [c, (v * 10).to_i] }.to_h
|
20
|
+
|
21
|
+
coins = {
|
22
|
+
ten_cents: 10,
|
23
|
+
twenty_cents: 20,
|
24
|
+
fifty_cents: 50,
|
25
|
+
one_euro: 100,
|
26
|
+
two_euro: 200
|
27
|
+
}.map { |c, v| [c, (v / 10).to_i] }.to_h
|
28
|
+
|
29
|
+
MAX_MONEY = 100
|
30
|
+
START_MONEY = 20
|
31
|
+
|
32
|
+
transient error :CoinReadFails
|
33
|
+
|
34
|
+
graph :User do
|
35
|
+
press_states = products.keys.map { |product| :"press_#{product}" }
|
36
|
+
grab_states = products.keys.map { |product| :"grab_#{product}" }
|
37
|
+
insert_states = coins.keys.map { |coin| :"insert_#{coin}" }
|
38
|
+
|
39
|
+
var pocket_money: (0..MAX_MONEY), init: START_MONEY
|
40
|
+
var value_in_products: (0..MAX_MONEY), init: 0
|
41
|
+
|
42
|
+
states :inserting, *insert_states, \
|
43
|
+
:pressing, *press_states, \
|
44
|
+
:waiting, :grab_product, \
|
45
|
+
:grab_change, \
|
46
|
+
:done, \
|
47
|
+
init: :inserting
|
48
|
+
|
49
|
+
# The user can insert coins until the money runs out
|
50
|
+
coins.each { |coin, value|
|
51
|
+
transition :inserting => :"insert_#{coin}" do
|
52
|
+
guard "pocket_money - #{value} >= 0"
|
53
|
+
action "pocket_money := pocket_money - #{value}"
|
54
|
+
end
|
55
|
+
transition :"insert_#{coin}" => :inserting
|
56
|
+
}
|
57
|
+
# The user can decide at any point to start pressing buttons
|
58
|
+
transition :inserting => :pressing
|
59
|
+
|
60
|
+
# Press one button and wait for the result
|
61
|
+
products.each { |product, value|
|
62
|
+
transition :pressing => :"press_#{product}"
|
63
|
+
transition :"press_#{product}" => :waiting
|
64
|
+
}
|
65
|
+
|
66
|
+
# Grab a product and take note of the value
|
67
|
+
products.each { |product, value|
|
68
|
+
transition :waiting => :grab_product do
|
69
|
+
precon "value_in_products + #{value} < #{MAX_MONEY}"
|
70
|
+
guard "LED == green && Dispenser == dispensed_#{product}"
|
71
|
+
action "value_in_products := value_in_products + #{value}"
|
72
|
+
end
|
73
|
+
}
|
74
|
+
transition :grab_product => :grab_change
|
75
|
+
|
76
|
+
# Grab change directly on fail
|
77
|
+
transition :waiting => :grab_change do
|
78
|
+
guard "LED == red"
|
79
|
+
end
|
80
|
+
|
81
|
+
# Grab the change and be done
|
82
|
+
transition :grab_change => :done do
|
83
|
+
precon "pocket_money + change <= #{MAX_MONEY}"
|
84
|
+
action "pocket_money := pocket_money + change"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
graph :CoinReader do
|
89
|
+
states :reading
|
90
|
+
|
91
|
+
var read_value: (0..coins.values.max), init: 0
|
92
|
+
|
93
|
+
coins.each { |coin, value|
|
94
|
+
transition :reading => :reading do
|
95
|
+
guard "User == insert_#{coin} && CoinReadFails == No"
|
96
|
+
action "read_value := #{value}"
|
97
|
+
end
|
98
|
+
}
|
99
|
+
|
100
|
+
transition :reading => :reading do
|
101
|
+
guard coins.keys.map { |coin| "User != insert_#{coin}" }.join(" && ")
|
102
|
+
action "read_value := 0"
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
graph :Controller do
|
108
|
+
var budget: (0..MAX_MONEY), init: 0
|
109
|
+
|
110
|
+
dispense_states = products.keys.map { |product| :"dispense_#{product}" }
|
111
|
+
|
112
|
+
states :accepting, *dispense_states, :rejected, :done, init: :accepting
|
113
|
+
|
114
|
+
# Accept coin reads from the reader
|
115
|
+
transition :accepting => :accepting do
|
116
|
+
precon "budget + read_value <= #{MAX_MONEY}"
|
117
|
+
guard "read_value > 0"
|
118
|
+
action "budget := budget + read_value"
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
products.each { |product, value|
|
123
|
+
|
124
|
+
# Accept button presses when enough money
|
125
|
+
transition :accepting => :"dispense_#{product}" do
|
126
|
+
guard "User == press_#{product} && budget >= #{value}"
|
127
|
+
action "budget := budget - #{value}"
|
128
|
+
end
|
129
|
+
transition :"dispense_#{product}" => :done
|
130
|
+
|
131
|
+
# Reject button presses when not enough money
|
132
|
+
transition :accepting => :rejected do
|
133
|
+
guard "User == press_#{product} && budget < #{value}"
|
134
|
+
end
|
135
|
+
}
|
136
|
+
|
137
|
+
# Reset when the coin dispenser has dispensed
|
138
|
+
transition :accepting => :accepting do
|
139
|
+
guard "CoinDispenser == dispensed_change"
|
140
|
+
action "budget := 0"
|
141
|
+
end
|
142
|
+
transition :done => :accepting do
|
143
|
+
guard "CoinDispenser == dispensed_change"
|
144
|
+
action "budget := 0"
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
graph :LED do
|
151
|
+
states :idle, :red, :green, init: :idle
|
152
|
+
|
153
|
+
transition :idle => :green do
|
154
|
+
guard "Controller == done"
|
155
|
+
end
|
156
|
+
transition :idle => :red do
|
157
|
+
guard "Controller == rejected"
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
graph :Dispenser do
|
163
|
+
dispensed_states = products.keys.map { |product| :"dispensed_#{product}" }
|
164
|
+
states :empty, *dispensed_states, init: :empty
|
165
|
+
|
166
|
+
# Dispense the product the Controller tells us to
|
167
|
+
products.each { |product, value|
|
168
|
+
transition :empty => :"dispensed_#{product}" do
|
169
|
+
guard "Controller == dispense_#{product}"
|
170
|
+
end
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
graph :CoinDispenser do
|
175
|
+
states :idle, :dispensed_change, init: :idle
|
176
|
+
var change: (0..MAX_MONEY), init: 0
|
177
|
+
|
178
|
+
# Eject the change money
|
179
|
+
transition :idle => :dispensed_change do
|
180
|
+
guard "Controller == rejected || Controller == done"
|
181
|
+
action "change := budget"
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
specify "The vending machine" do
|
187
|
+
|
188
|
+
it "allows the user to buy something" => :"EF value_in_products > 0"
|
189
|
+
it "always completes the transaction" => :"F User == done"
|
190
|
+
|
191
|
+
products.each { |product, value|
|
192
|
+
assuming "the product can be bough" => :"pocket_money >= #{value}" do
|
193
|
+
it "allows the user to buy #{product}" => :"EF Dispenser == dispensed_#{product}"
|
194
|
+
end
|
195
|
+
}
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
hazard "The user looses money" => :"User == done && pocket_money + value_in_products < #{START_MONEY}"
|
200
|
+
hazard "The machine looses money" => :"User == done && pocket_money + value_in_products > #{START_MONEY}"
|
201
|
+
|
202
|
+
end
|
@@ -4,7 +4,8 @@ model :TestTransitions do
|
|
4
4
|
end
|
5
5
|
specify "The state" do
|
6
6
|
it "starts in initial" => :"Test == initial"
|
7
|
-
it "stays in initial" => :"G Test == initial"
|
7
|
+
it "stays in initial (LTL)" => :"G Test == initial"
|
8
|
+
it "stays in initial (CTL)" => :"AG Test == initial"
|
8
9
|
it "never leaves initial" => :"! F Test != initial"
|
9
10
|
end
|
10
11
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
model :CtlSpecifications do
|
2
|
+
|
3
|
+
graph :Example do
|
4
|
+
states :one, :two, :three, init: :one
|
5
|
+
|
6
|
+
transition :one => :two
|
7
|
+
transition :two => :one
|
8
|
+
transition :one => :three
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "The model" do
|
13
|
+
it "starts in state one" => :"Example == one"
|
14
|
+
it "cannot stay in one" => :"! G Example == one"
|
15
|
+
it "can stay in three" => :"EX AG Example == three"
|
16
|
+
it "cannot go back to one from three" => :"AG ( (Example == three) => ! EF Example == one)"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
model :NonAtomicHazard do
|
2
|
+
|
3
|
+
persistent error :KeysForgotten
|
4
|
+
|
5
|
+
graph :Person do
|
6
|
+
states :inside, :outside, init: :inside
|
7
|
+
transition :inside => :outside
|
8
|
+
transition :outside => :inside do
|
9
|
+
guard "KeysForgotten == No"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# This does not work as at any point the fault could not have occurred
|
14
|
+
# (KeysForgotten == No) already but then it happens at a later point
|
15
|
+
# Expected Cut Sets: { :"" }
|
16
|
+
# hazard "The person can't get in" => :"G Person == outside"
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
model :MultipleActions do
|
2
|
+
|
3
|
+
graph :GraphWithMultipleActions do
|
4
|
+
states :one, :two, init: :one
|
5
|
+
|
6
|
+
var a: (0..10), init: 0
|
7
|
+
var b: (0..10), init: 0
|
8
|
+
var c: (0..10), init: 0
|
9
|
+
|
10
|
+
transition :one => :two do
|
11
|
+
action "a := 1 + 1 | b := 0 | c := 25 - (2 * 10)"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "The graph" do
|
16
|
+
it "set a = 2" => :"X a == 2"
|
17
|
+
it "set b = 0" => :"X b == 0"
|
18
|
+
it "set c = 5" => :"X c == 5"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|