contracts 0.12.0 → 0.16.1
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 +5 -13
- data/.github/workflows/code_style_checks.yaml +36 -0
- data/.github/workflows/tests.yaml +47 -0
- data/CHANGELOG.markdown +57 -6
- data/Gemfile +2 -2
- data/LICENSE +23 -0
- data/README.md +14 -6
- data/Rakefile +3 -6
- data/TUTORIAL.md +55 -26
- data/contracts.gemspec +6 -1
- data/dependabot.yml +20 -0
- data/features/basics/pretty-print.feature +241 -0
- data/features/builtin_contracts/args.feature +80 -1
- data/features/builtin_contracts/int.feature +93 -0
- data/features/builtin_contracts/nat_pos.feature +119 -0
- data/lib/contracts.rb +48 -12
- data/lib/contracts/attrs.rb +24 -0
- data/lib/contracts/builtin_contracts.rb +60 -1
- data/lib/contracts/call_with.rb +22 -11
- data/lib/contracts/core.rb +0 -2
- data/lib/contracts/decorators.rb +5 -0
- data/lib/contracts/engine/eigenclass.rb +4 -0
- data/lib/contracts/engine/target.rb +2 -0
- data/lib/contracts/formatters.rb +4 -2
- data/lib/contracts/method_handler.rb +14 -22
- data/lib/contracts/support.rb +11 -9
- data/lib/contracts/version.rb +1 -1
- data/spec/attrs_spec.rb +119 -0
- data/spec/builtin_contracts_spec.rb +157 -95
- data/spec/contracts_spec.rb +50 -29
- data/spec/fixtures/fixtures.rb +58 -0
- data/spec/methods_spec.rb +54 -0
- data/spec/override_validators_spec.rb +1 -1
- data/spec/ruby_version_specific/contracts_spec_2.0.rb +15 -0
- data/spec/ruby_version_specific/contracts_spec_2.1.rb +1 -1
- data/spec/validators_spec.rb +1 -1
- metadata +25 -14
- data/script/cucumber +0 -5
@@ -0,0 +1,241 @@
|
|
1
|
+
Feature: Pretty printing Contract violations
|
2
|
+
|
3
|
+
Scenario: Big array argument being passed to big array method parameter
|
4
|
+
Given a file named "example.rb" with:
|
5
|
+
"""ruby
|
6
|
+
require "contracts"
|
7
|
+
C = Contracts
|
8
|
+
|
9
|
+
class Example
|
10
|
+
include Contracts::Core
|
11
|
+
|
12
|
+
class << self
|
13
|
+
Contract [
|
14
|
+
C::Or[String, Symbol],
|
15
|
+
C::Or[String, Symbol],
|
16
|
+
C::Or[String, Symbol],
|
17
|
+
C::Or[String, Symbol],
|
18
|
+
C::Or[String, Symbol],
|
19
|
+
C::Or[String, Symbol],
|
20
|
+
C::Or[String, Symbol]
|
21
|
+
] => nil
|
22
|
+
def run(data)
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
puts Example.run([
|
29
|
+
["foo", "foo"],
|
30
|
+
["foo", "foo"],
|
31
|
+
["foo", "foo"],
|
32
|
+
["foo", "foo"],
|
33
|
+
["foo", "foo"],
|
34
|
+
["foo", "foo"],
|
35
|
+
["foo", "foo"],
|
36
|
+
["foo", "foo"],
|
37
|
+
["foo", "foo"]
|
38
|
+
])
|
39
|
+
"""
|
40
|
+
When I run `ruby example.rb`
|
41
|
+
Then the output should contain:
|
42
|
+
"""
|
43
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
44
|
+
Expected: [(String or Symbol),
|
45
|
+
(String or Symbol),
|
46
|
+
(String or Symbol),
|
47
|
+
(String or Symbol),
|
48
|
+
(String or Symbol),
|
49
|
+
(String or Symbol),
|
50
|
+
(String or Symbol)],
|
51
|
+
Actual: [["foo", "foo"],
|
52
|
+
["foo", "foo"],
|
53
|
+
["foo", "foo"],
|
54
|
+
["foo", "foo"],
|
55
|
+
["foo", "foo"],
|
56
|
+
["foo", "foo"],
|
57
|
+
["foo", "foo"],
|
58
|
+
["foo", "foo"],
|
59
|
+
["foo", "foo"]]
|
60
|
+
Value guarded in: Example::run
|
61
|
+
With Contract: Array => NilClass
|
62
|
+
At: example.rb:17
|
63
|
+
"""
|
64
|
+
|
65
|
+
Scenario: Big array value being returned from method expecting different big array type
|
66
|
+
Given a file named "example.rb" with:
|
67
|
+
"""ruby
|
68
|
+
require "contracts"
|
69
|
+
C = Contracts
|
70
|
+
|
71
|
+
class Example
|
72
|
+
include Contracts::Core
|
73
|
+
|
74
|
+
class << self
|
75
|
+
Contract C::None => [
|
76
|
+
C::Or[String, Symbol],
|
77
|
+
C::Or[String, Symbol],
|
78
|
+
C::Or[String, Symbol],
|
79
|
+
C::Or[String, Symbol],
|
80
|
+
C::Or[String, Symbol],
|
81
|
+
C::Or[String, Symbol],
|
82
|
+
C::Or[String, Symbol]
|
83
|
+
]
|
84
|
+
def run
|
85
|
+
[
|
86
|
+
["foo", "foo"],
|
87
|
+
["foo", "foo"],
|
88
|
+
["foo", "foo"],
|
89
|
+
["foo", "foo"],
|
90
|
+
["foo", "foo"],
|
91
|
+
["foo", "foo"],
|
92
|
+
["foo", "foo"],
|
93
|
+
["foo", "foo"],
|
94
|
+
["foo", "foo"]
|
95
|
+
]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
puts Example.run
|
101
|
+
"""
|
102
|
+
When I run `ruby example.rb`
|
103
|
+
Then the output should contain:
|
104
|
+
"""
|
105
|
+
: Contract violation for return value: (ReturnContractError)
|
106
|
+
Expected: [(String or Symbol),
|
107
|
+
(String or Symbol),
|
108
|
+
(String or Symbol),
|
109
|
+
(String or Symbol),
|
110
|
+
(String or Symbol),
|
111
|
+
(String or Symbol),
|
112
|
+
(String or Symbol)],
|
113
|
+
Actual: [["foo", "foo"],
|
114
|
+
["foo", "foo"],
|
115
|
+
["foo", "foo"],
|
116
|
+
["foo", "foo"],
|
117
|
+
["foo", "foo"],
|
118
|
+
["foo", "foo"],
|
119
|
+
["foo", "foo"],
|
120
|
+
["foo", "foo"],
|
121
|
+
["foo", "foo"]]
|
122
|
+
Value guarded in: Example::run
|
123
|
+
With Contract: None => Array
|
124
|
+
At: example.rb:17
|
125
|
+
"""
|
126
|
+
|
127
|
+
Scenario: Big hash argument being passed to big hash method parameter
|
128
|
+
Given a file named "example.rb" with:
|
129
|
+
"""ruby
|
130
|
+
require "contracts"
|
131
|
+
C = Contracts
|
132
|
+
|
133
|
+
class Example
|
134
|
+
include Contracts::Core
|
135
|
+
|
136
|
+
class << self
|
137
|
+
Contract ({
|
138
|
+
a: C::Or[String, Symbol],
|
139
|
+
b: C::Or[String, Symbol],
|
140
|
+
c: C::Or[String, Symbol],
|
141
|
+
d: C::Or[String, Symbol],
|
142
|
+
e: C::Or[String, Symbol],
|
143
|
+
f: C::Or[String, Symbol],
|
144
|
+
g: C::Or[String, Symbol]
|
145
|
+
}) => nil
|
146
|
+
def run(data)
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
puts Example.run({
|
153
|
+
a: ["foo", "foo"],
|
154
|
+
b: ["foo", "foo"],
|
155
|
+
c: ["foo", "foo"],
|
156
|
+
d: ["foo", "foo"],
|
157
|
+
e: ["foo", "foo"],
|
158
|
+
f: ["foo", "foo"],
|
159
|
+
g: ["foo", "foo"]
|
160
|
+
})
|
161
|
+
"""
|
162
|
+
When I run `ruby example.rb`
|
163
|
+
Then the output should contain:
|
164
|
+
"""
|
165
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
166
|
+
Expected: {:a=>(String or Symbol),
|
167
|
+
:b=>(String or Symbol),
|
168
|
+
:c=>(String or Symbol),
|
169
|
+
:d=>(String or Symbol),
|
170
|
+
:e=>(String or Symbol),
|
171
|
+
:f=>(String or Symbol),
|
172
|
+
:g=>(String or Symbol)},
|
173
|
+
Actual: {:a=>["foo", "foo"],
|
174
|
+
:b=>["foo", "foo"],
|
175
|
+
:c=>["foo", "foo"],
|
176
|
+
:d=>["foo", "foo"],
|
177
|
+
:e=>["foo", "foo"],
|
178
|
+
:f=>["foo", "foo"],
|
179
|
+
:g=>["foo", "foo"]}
|
180
|
+
Value guarded in: Example::run
|
181
|
+
With Contract: Hash => NilClass
|
182
|
+
At: example.rb:17
|
183
|
+
"""
|
184
|
+
|
185
|
+
Scenario: Big hash value being returned from method expecting different big hash type
|
186
|
+
Given a file named "example.rb" with:
|
187
|
+
"""ruby
|
188
|
+
require "contracts"
|
189
|
+
C = Contracts
|
190
|
+
|
191
|
+
class Example
|
192
|
+
include Contracts::Core
|
193
|
+
|
194
|
+
class << self
|
195
|
+
Contract C::None => ({
|
196
|
+
a: C::Or[String, Symbol],
|
197
|
+
b: C::Or[String, Symbol],
|
198
|
+
c: C::Or[String, Symbol],
|
199
|
+
d: C::Or[String, Symbol],
|
200
|
+
e: C::Or[String, Symbol],
|
201
|
+
f: C::Or[String, Symbol],
|
202
|
+
g: C::Or[String, Symbol]
|
203
|
+
})
|
204
|
+
def run
|
205
|
+
{
|
206
|
+
a: ["foo", "foo"],
|
207
|
+
b: ["foo", "foo"],
|
208
|
+
c: ["foo", "foo"],
|
209
|
+
d: ["foo", "foo"],
|
210
|
+
e: ["foo", "foo"],
|
211
|
+
f: ["foo", "foo"],
|
212
|
+
g: ["foo", "foo"]
|
213
|
+
}
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
puts Example.run
|
219
|
+
"""
|
220
|
+
When I run `ruby example.rb`
|
221
|
+
Then the output should contain:
|
222
|
+
"""
|
223
|
+
: Contract violation for return value: (ReturnContractError)
|
224
|
+
Expected: {:a=>(String or Symbol),
|
225
|
+
:b=>(String or Symbol),
|
226
|
+
:c=>(String or Symbol),
|
227
|
+
:d=>(String or Symbol),
|
228
|
+
:e=>(String or Symbol),
|
229
|
+
:f=>(String or Symbol),
|
230
|
+
:g=>(String or Symbol)},
|
231
|
+
Actual: {:a=>["foo", "foo"],
|
232
|
+
:b=>["foo", "foo"],
|
233
|
+
:c=>["foo", "foo"],
|
234
|
+
:d=>["foo", "foo"],
|
235
|
+
:e=>["foo", "foo"],
|
236
|
+
:f=>["foo", "foo"],
|
237
|
+
:g=>["foo", "foo"]}
|
238
|
+
Value guarded in: Example::run
|
239
|
+
With Contract: None => Hash
|
240
|
+
At: example.rb:17
|
241
|
+
"""
|
@@ -1 +1,80 @@
|
|
1
|
-
Feature: Args
|
1
|
+
Feature: Args
|
2
|
+
|
3
|
+
Used for `*args` (variadic functions). Takes contract and uses it to validate
|
4
|
+
every element passed in through `*args`.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
Contract C::Args[C::Num] => C::Bool
|
8
|
+
def example(*args)
|
9
|
+
```
|
10
|
+
|
11
|
+
This example contract will validate all arguments passed through `*args` to
|
12
|
+
accept only numbers.
|
13
|
+
|
14
|
+
Background:
|
15
|
+
Given a file named "args_usage.rb" with:
|
16
|
+
"""ruby
|
17
|
+
require "contracts"
|
18
|
+
C = Contracts
|
19
|
+
|
20
|
+
class Example
|
21
|
+
include Contracts::Core
|
22
|
+
|
23
|
+
Contract C::Args[C::Num] => C::Bool
|
24
|
+
def only_nums(*args)
|
25
|
+
args.inspect
|
26
|
+
end
|
27
|
+
end
|
28
|
+
"""
|
29
|
+
|
30
|
+
Scenario: Accepts no arguments
|
31
|
+
Given a file named "accepts_no_arguments.rb" with:
|
32
|
+
"""ruby
|
33
|
+
require "./args_usage"
|
34
|
+
puts Example.new.only_nums
|
35
|
+
"""
|
36
|
+
When I run `ruby accepts_no_arguments.rb`
|
37
|
+
Then the output should contain:
|
38
|
+
"""
|
39
|
+
[]
|
40
|
+
"""
|
41
|
+
|
42
|
+
Scenario: Accepts one valid argument
|
43
|
+
Given a file named "accepts_one_argument.rb" with:
|
44
|
+
"""ruby
|
45
|
+
require "./args_usage"
|
46
|
+
puts Example.new.only_nums(42)
|
47
|
+
"""
|
48
|
+
When I run `ruby accepts_one_argument.rb`
|
49
|
+
Then the output should contain:
|
50
|
+
"""
|
51
|
+
[42]
|
52
|
+
"""
|
53
|
+
|
54
|
+
Scenario: Accepts many valid arguments
|
55
|
+
Given a file named "accepts_many_arguments.rb" with:
|
56
|
+
"""ruby
|
57
|
+
require "./args_usage"
|
58
|
+
puts Example.new.only_nums(42, 45, 17, 24)
|
59
|
+
"""
|
60
|
+
When I run `ruby accepts_many_arguments.rb`
|
61
|
+
Then the output should contain:
|
62
|
+
"""
|
63
|
+
[42, 45, 17, 24]
|
64
|
+
"""
|
65
|
+
|
66
|
+
Scenario: Rejects invalid argument
|
67
|
+
Given a file named "rejects_invalid_argument.rb" with:
|
68
|
+
"""ruby
|
69
|
+
require "./args_usage"
|
70
|
+
puts Example.new.only_nums(42, "foo", 17, 24)
|
71
|
+
"""
|
72
|
+
When I run `ruby rejects_invalid_argument.rb`
|
73
|
+
Then the output should contain:
|
74
|
+
"""
|
75
|
+
: Contract violation for argument 1 of 4: (ParamContractError)
|
76
|
+
Expected: (Args[Contracts::Builtin::Num]),
|
77
|
+
Actual: "foo"
|
78
|
+
Value guarded in: Example::only_nums
|
79
|
+
With Contract: Args => Bool
|
80
|
+
"""
|
@@ -0,0 +1,93 @@
|
|
1
|
+
Feature: Int
|
2
|
+
|
3
|
+
Checks that an argument is an integer.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
Contract C::Int => C::Int
|
7
|
+
```
|
8
|
+
|
9
|
+
Background:
|
10
|
+
Given a file named "int_usage.rb" with:
|
11
|
+
"""ruby
|
12
|
+
require "contracts"
|
13
|
+
C = Contracts
|
14
|
+
|
15
|
+
class Integr
|
16
|
+
include Contracts::Core
|
17
|
+
|
18
|
+
Contract C::Int => C::Int
|
19
|
+
def prev(number)
|
20
|
+
number - 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
"""
|
24
|
+
|
25
|
+
Scenario: Accepts positive integers
|
26
|
+
Given a file named "accepts_positive_integers.rb" with:
|
27
|
+
"""ruby
|
28
|
+
require "./int_usage"
|
29
|
+
puts Integr.new.prev(7)
|
30
|
+
"""
|
31
|
+
When I run `ruby accepts_positive_integers.rb`
|
32
|
+
Then output should contain:
|
33
|
+
"""
|
34
|
+
6
|
35
|
+
"""
|
36
|
+
|
37
|
+
Scenario: Accepts zero
|
38
|
+
Given a file named "accepts_zero.rb" with:
|
39
|
+
"""ruby
|
40
|
+
require "./int_usage"
|
41
|
+
puts Integr.new.prev(1)
|
42
|
+
"""
|
43
|
+
When I run `ruby accepts_zero.rb`
|
44
|
+
Then output should contain:
|
45
|
+
"""
|
46
|
+
0
|
47
|
+
"""
|
48
|
+
|
49
|
+
Scenario: Accepts negative integers
|
50
|
+
Given a file named "accepts_negative_integers.rb" with:
|
51
|
+
"""ruby
|
52
|
+
require "./int_usage"
|
53
|
+
puts Integr.new.prev(-1)
|
54
|
+
"""
|
55
|
+
When I run `ruby accepts_negative_integers.rb`
|
56
|
+
Then output should contain:
|
57
|
+
"""
|
58
|
+
-2
|
59
|
+
"""
|
60
|
+
|
61
|
+
Scenario: Rejects floats
|
62
|
+
Given a file named "rejects_floats.rb" with:
|
63
|
+
"""ruby
|
64
|
+
require "./int_usage"
|
65
|
+
puts Integr.new.prev(3.43)
|
66
|
+
"""
|
67
|
+
When I run `ruby rejects_floats.rb`
|
68
|
+
Then output should contain:
|
69
|
+
"""
|
70
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
71
|
+
Expected: Int,
|
72
|
+
Actual: 3.43
|
73
|
+
Value guarded in: Integr::prev
|
74
|
+
With Contract: Int => Int
|
75
|
+
"""
|
76
|
+
And output should contain "int_usage.rb:8"
|
77
|
+
|
78
|
+
Scenario: Rejects other values
|
79
|
+
Given a file named "rejects_others.rb" with:
|
80
|
+
"""ruby
|
81
|
+
require "./int_usage"
|
82
|
+
puts Integr.new.prev("foo")
|
83
|
+
"""
|
84
|
+
When I run `ruby rejects_others.rb`
|
85
|
+
Then output should contain:
|
86
|
+
"""
|
87
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
88
|
+
Expected: Int,
|
89
|
+
Actual: "foo"
|
90
|
+
Value guarded in: Integr::prev
|
91
|
+
With Contract: Int => Int
|
92
|
+
"""
|
93
|
+
And output should contain "int_usage.rb:8"
|
@@ -0,0 +1,119 @@
|
|
1
|
+
Feature: NatPos
|
2
|
+
|
3
|
+
Checks that an argument is a positive natural number.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
Contract C::NatPos => C::NatPos
|
7
|
+
```
|
8
|
+
|
9
|
+
Background:
|
10
|
+
Given a file named "nat_pos_usage.rb" with:
|
11
|
+
"""ruby
|
12
|
+
require "contracts"
|
13
|
+
C = Contracts
|
14
|
+
|
15
|
+
class NaturalPositive
|
16
|
+
include Contracts::Core
|
17
|
+
|
18
|
+
Contract C::NatPos => C::NatPos
|
19
|
+
def prev(number)
|
20
|
+
number - 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
"""
|
24
|
+
|
25
|
+
Scenario: Accepts positive integers
|
26
|
+
Given a file named "accepts_positive_integers.rb" with:
|
27
|
+
"""ruby
|
28
|
+
require "./nat_pos_usage"
|
29
|
+
puts NaturalPositive.new.prev(7)
|
30
|
+
"""
|
31
|
+
When I run `ruby accepts_positive_integers.rb`
|
32
|
+
Then output should contain:
|
33
|
+
"""
|
34
|
+
6
|
35
|
+
"""
|
36
|
+
|
37
|
+
Scenario: Rejects zero
|
38
|
+
Given a file named "rejects_zero.rb" with:
|
39
|
+
"""ruby
|
40
|
+
require "./nat_pos_usage"
|
41
|
+
puts NaturalPositive.new.prev(0)
|
42
|
+
"""
|
43
|
+
When I run `ruby rejects_zero.rb`
|
44
|
+
Then output should contain:
|
45
|
+
"""
|
46
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
47
|
+
Expected: NatPos,
|
48
|
+
Actual: 0
|
49
|
+
Value guarded in: NaturalPositive::prev
|
50
|
+
With Contract: NatPos => NatPos
|
51
|
+
"""
|
52
|
+
|
53
|
+
Scenario: Rejects negative integers
|
54
|
+
Given a file named "rejects_negative_integers.rb" with:
|
55
|
+
"""ruby
|
56
|
+
require "./nat_pos_usage"
|
57
|
+
puts NaturalPositive.new.prev(-1)
|
58
|
+
"""
|
59
|
+
When I run `ruby rejects_negative_integers.rb`
|
60
|
+
Then output should contain:
|
61
|
+
"""
|
62
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
63
|
+
Expected: NatPos,
|
64
|
+
Actual: -1
|
65
|
+
Value guarded in: NaturalPositive::prev
|
66
|
+
With Contract: NatPos => NatPos
|
67
|
+
"""
|
68
|
+
And output should contain "nat_pos_usage.rb:8"
|
69
|
+
|
70
|
+
Scenario: Rejects negative integers as a return value
|
71
|
+
Given a file named "rejects_negative_integers.rb" with:
|
72
|
+
"""ruby
|
73
|
+
require "./nat_pos_usage"
|
74
|
+
puts NaturalPositive.new.prev(1)
|
75
|
+
"""
|
76
|
+
When I run `ruby rejects_negative_integers.rb`
|
77
|
+
Then output should contain:
|
78
|
+
"""
|
79
|
+
: Contract violation for return value: (ReturnContractError)
|
80
|
+
Expected: NatPos,
|
81
|
+
Actual: 0
|
82
|
+
Value guarded in: NaturalPositive::prev
|
83
|
+
With Contract: NatPos => NatPos
|
84
|
+
"""
|
85
|
+
And output should contain "nat_pos_usage.rb:8"
|
86
|
+
|
87
|
+
Scenario: Rejects floats
|
88
|
+
Given a file named "rejects_floats.rb" with:
|
89
|
+
"""ruby
|
90
|
+
require "./nat_pos_usage"
|
91
|
+
puts NaturalPositive.new.prev(3.43)
|
92
|
+
"""
|
93
|
+
When I run `ruby rejects_floats.rb`
|
94
|
+
Then output should contain:
|
95
|
+
"""
|
96
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
97
|
+
Expected: NatPos,
|
98
|
+
Actual: 3.43
|
99
|
+
Value guarded in: NaturalPositive::prev
|
100
|
+
With Contract: NatPos => NatPos
|
101
|
+
"""
|
102
|
+
And output should contain "nat_pos_usage.rb:8"
|
103
|
+
|
104
|
+
Scenario: Rejects other values
|
105
|
+
Given a file named "rejects_others.rb" with:
|
106
|
+
"""ruby
|
107
|
+
require "./nat_pos_usage"
|
108
|
+
puts NaturalPositive.new.prev("foo")
|
109
|
+
"""
|
110
|
+
When I run `ruby rejects_others.rb`
|
111
|
+
Then output should contain:
|
112
|
+
"""
|
113
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
114
|
+
Expected: NatPos,
|
115
|
+
Actual: "foo"
|
116
|
+
Value guarded in: NaturalPositive::prev
|
117
|
+
With Contract: NatPos => NatPos
|
118
|
+
"""
|
119
|
+
And output should contain "nat_pos_usage.rb:8"
|