Skip to content

Commit e7b857f

Browse files
authored
Merge pull request #1662 from jesicaMao/feat/multiple-invariants-reporting
feat: Add support for multiple invariants in CLI commands
2 parents 332a5e3 + 10af83f commit e7b857f

File tree

11 files changed

+243
-73
lines changed

11 files changed

+243
-73
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## UNRELEASED
99

1010
### Added
11+
12+
- `quint run` and `quint verify` can now receive multiple invariants through
13+
`--invariants` and Quint prints which ones were violated in the found
14+
violation (#1662)
15+
1116
### Changed
1217

1318
- `--out-itf` does not suppress outputs anymore. Shown output amount only depends on `--verbosity` now (#1664)

docs/pages/docs/checking-properties.mdx

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ quint run gettingStarted.qnt --witnesses=alice_more_than_bob --max-steps=5
5454

5555
Quint reports:
5656
```ansi
57-
[ok] No violation found (599ms).
58-
You may increase --max-samples and --max-steps.
59-
Use --verbosity to produce more (or less) output.
60-
Witnesses:
61-
alice_more_than_bob was witnessed in 7094 trace(s) out of 10000 explored (70.94%)
62-
Use --seed=0x1c8a2137939c73 to reproduce.
57+
[ok] No violation found (599ms).
58+
You may increase --max-samples and --max-steps.
59+
Use --verbosity to produce more (or less) output.
60+
Witnesses:
61+
alice_more_than_bob was witnessed in 7094 trace(s) out of 10000 explored (70.94%)
62+
Use --seed=0x1c8a2137939c73 to reproduce.
6363
```
6464

6565
And that informs us that, in most executions, there is a state where Alice has a larger balance than that of Bob.
@@ -78,16 +78,49 @@ quint run gettingStarted.qnt --invariant="not(alice_more_than_bob)" --max-steps=
7878

7979
Quint reports:
8080
```ansi
81-
An example execution:
82-

83-
[State 0] { balances: Map("alice" -> 0, "bob" -> 0, "charlie" -> 0) }
84-
85-
[State 1] { balances: Map("alice" -> 9, "bob" -> 0, "charlie" -> 0) }
86-
87-
[violation] Found an issue (8ms).
88-
Use --verbosity=3 to show executions.
89-
Use --seed=0xa0bbf64f2c03 to reproduce.
90-
error: Invariant violated
81+
An example execution:
82+

83+
[State 0] { balances: Map("alice" -> 0, "bob" -> 0, "charlie" -> 0) }
84+
85+
[State 1] { balances: Map("alice" -> 9, "bob" -> 0, "charlie" -> 0) }
86+
87+
[violation] Found an issue (8ms).
88+
Use --verbosity=3 to show executions.
89+
Use --seed=0xa0bbf64f2c03 to reproduce.
90+
error: Invariant violated
9191
```
9292

9393
Although it says "Invariant violated", this is exactly what we wanted: an example where Alice has more balance than Bob.
94+
95+
### Using multiple invariants with `--invariants`
96+
97+
If you want to check multiple invariants at once and determine which ones are violated, you can use the `--invariants` option:
98+
99+
```sh
100+
quint run bank.qnt --invariants no_negatives accounts_match total_positive
101+
```
102+
103+
Quint will check all three invariants and report which specific ones are violated:
104+
105+
```ansi
106+
[90mAn example execution:[39m
107+
[90m[39m
108+
[[1mState 0[22m] { balances[90m:[39m [32mMap[39m([32m"alice"[39m[90m ->[39m [33m0[39m, [32m"bob"[39m[90m ->[39m [33m0[39m, [32m"charlie"[39m[90m ->[39m [33m0[39m) }
109+
110+
[[1mState 1[22m] { balances[90m:[39m [32mMap[39m([32m"alice"[39m[90m ->[39m [33m-63[39m, [32m"bob"[39m[90m ->[39m [33m0[39m, [32m"charlie"[39m[90m ->[39m [33m0[39m) }
111+
112+
[31m[violation][39m Found an issue [90m(44ms).[39m
113+
[31m ❌ no_negatives[39m
114+
[31m ❌ total_positive[39m
115+
[90mUse --verbosity=3 to show executions.[39m
116+
[90mUse --seed=0x4e85b3a53f7ef to reproduce.[39m
117+
error: Invariant violated
118+
```
119+
120+
This makes it easier to identify which specific properties are being violated when you have multiple invariants to check.
121+
122+
The `--invariants` option also works with the `verify` command, allowing you to check multiple invariants with formal verification:
123+
124+
```sh
125+
quint verify bank.qnt --invariants no_negatives accounts_match total_positive
126+
```

docs/pages/docs/quint.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,12 @@ Options:
238238
[number] [default: 20]
239239
--init name of the initializer action [string] [default: "init"]
240240
--step name of the step action [string] [default: "step"]
241-
--invariant invariant to check: a definition name or an expression
242-
[string] [default: "true"]
241+
--invariants space separated list of invariants to check (definition names).
242+
When specified, all invariants are combined with AND and
243+
checked together, with detailed reporting of which ones were
244+
violated [array] [default: []]
245+
--invariant invariant to check: a definition name or an expression. Can be
246+
used together with --invariants [string] [default: "true"]
243247
--witnesses space separated list of witnesses to report on (counting for
244248
how many traces the witness is true) [array] [default: []]
245249
--hide space separated list of variable names to hide from the terminal
@@ -351,10 +355,13 @@ Options:
351355
filename) [string]
352356
--init name of the initializer action[string] [default: "init"]
353357
--step name of the step action [string] [default: "step"]
354-
--invariant the invariants to check, separated by commas (e.g.)
355-
[string]
358+
--invariant the invariants to check, separated by commas [string]
356359
--temporal the temporal properties to check, separated by commas
357360
[string]
361+
--invariants space separated list of invariants to check (definition
362+
names). When specified, all invariants are combined with
363+
AND and checked together, with detailed reporting of
364+
which ones were violated [array] [default: []]
358365
--out-itf output the trace in the Informal Trace Format to file
359366
(suppresses all console output) [string]
360367
--max-steps the maximum number of steps in every trace

quint/io-cli-tests.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,3 +1505,23 @@ An example execution:
15051505
15061506
[State 1]
15071507
```
1508+
1509+
### Prints violated invariants when multiple invariants are given
1510+
1511+
<!-- !test exit 1 -->
1512+
<!-- !test in multiple invariants -->
1513+
```
1514+
output=$(quint run ../examples/games/tictactoe/tictactoe.qnt --invariants="not(won(X))" "not(stalemate)" --max-samples=100 --seed=0x2b442ab439177)
1515+
exit_code=$?
1516+
echo "$output" | tail -n4 | sed -e 's/([0-9]*ms.*)/(duration)/g'
1517+
exit $exit_code
1518+
```
1519+
1520+
<!-- !test out multiple invariants -->
1521+
```
1522+
[violation] Found an issue (duration).
1523+
❌ not(won(X))
1524+
Use --verbosity=3 to show executions.
1525+
Use --seed=0x2b442ab439177 to reproduce.
1526+
```
1527+

quint/src/cli.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,13 @@ const runCmd = {
266266
type: 'string',
267267
default: 'step',
268268
})
269+
.option('invariants', {
270+
desc: 'space separated list of invariants to check (definition names). When specified, all invariants are combined with AND and checked together, with detailed reporting of which ones were violated',
271+
type: 'array',
272+
default: [],
273+
})
269274
.option('invariant', {
270-
desc: 'invariant to check: a definition name or an expression',
275+
desc: 'invariant to check: a definition name or an expression. Can be used together with --invariants',
271276
type: 'string',
272277
default: 'true',
273278
})
@@ -318,6 +323,11 @@ const verifyCmd = {
318323
desc: `Verify a Quint specification via Apalache`,
319324
builder: (yargs: any) =>
320325
compileOpts(yargs)
326+
.option('invariants', {
327+
desc: 'space separated list of invariants to check (definition names). When specified, all invariants are combined with AND and checked together, with detailed reporting of which ones were violated',
328+
type: 'array',
329+
default: [],
330+
})
321331
.option('out-itf', {
322332
desc: 'output the trace in the Informal Trace Format to file, e.g., out.itf.json (suppresses all console output)',
323333
type: 'string',

0 commit comments

Comments
 (0)