rast 0.19.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/.ruby-version +1 -0
- data/CHANGELOG.md +9 -4
- data/Documentation.md +8 -5
- data/Gemfile +2 -2
- data/Gemfile.lock +41 -42
- data/Getting-Started-Detailed.md +4 -3
- data/README.md +26 -17
- data/examples/{hotel_finder.rb → diplomat_hotel_finder.rb} +1 -1
- data/lib/rast/parameter_generator.rb +12 -8
- data/lib/rast/rast_spec.rb +1 -1
- data/lib/rast/{rules/rule_validator.rb → rule_validator.rb} +16 -5
- data/lib/rast/rules/logic_helper.rb +4 -5
- data/lib/rast/rules/rule.rb +0 -13
- data/lib/rast/rules/rule_evaluator.rb +10 -10
- data/lib/rast/rules/rule_processor.rb +15 -19
- data/lib/rast/spec_dsl.rb +8 -5
- data/rast.gemspec +3 -3
- metadata +13 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db0077928a7df4dcd98e98737660be491b8eb5aaad2ed6f9e3015ed26d2b703e
|
4
|
+
data.tar.gz: 75c49f92fed7ac16e1490a531c3bf87fec92e6c26e20f466c92cde3c2f0c3d66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74101d1f6227851cab7d263e6750df4aae5a9f70dbf868c9a3925646f1781406edca38f35b214ad54e4917035ba6d779bd80a18f30fd8c2ffee903b02a4c4889
|
7
|
+
data.tar.gz: 1a41f4db833451dffe2ff219d2acd009426f1ebe6685db335ff44371a22acbb6c6bd76c53edefe19e52a1f884bb19a0587b9041d38539f9431b8b88bb0530fc2
|
data/.rubocop.yml
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.2
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,17 @@
|
|
1
|
-
# Change
|
1
|
+
# Change logs
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
5
|
|
6
6
|
## Released
|
7
7
|
|
8
|
-
-
|
9
|
-
|
8
|
+
- 1.00.0
|
9
|
+
- [maint] Updated for Ruby 3.0, updated Gemfiles.
|
10
|
+
|
11
|
+
- 0.19.1
|
12
|
+
|
13
|
+
- [bug] nil as configured outcome
|
14
|
+
- [bug] nil showing in the report as empty string.
|
10
15
|
|
11
16
|
- 0.19.0 - [feature] boolean in variables definition to automatically infer
|
12
17
|
false and true
|
@@ -56,4 +61,4 @@ the prepare block now runs under the context of RSpec.
|
|
56
61
|
- 0.3.0.pre - Support factory methods in execute block.
|
57
62
|
- 0.2.0.pre - Support module subjects.
|
58
63
|
- 0.1.2.pre - Fixed exclusions.
|
59
|
-
- 0.1.1.pre - Pre release version compatible with Ruby 2.0.0-p247
|
64
|
+
- 0.1.1.pre - Pre release version compatible with Ruby 2.0.0-p247
|
data/Documentation.md
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
YAML is the preferred because it simplifies the content of the spec file. YAML contains the variables that affect the outcome, and the expected outcome based on rules that involve the said variables.
|
6
6
|
|
7
7
|
```yaml
|
8
|
+
---
|
8
9
|
specs:
|
9
10
|
# spec key uniquely identifies a spec. It has to match the ID when the spec
|
10
11
|
# block is invoked in the ruby spec file. Usually the method name.
|
@@ -28,6 +29,7 @@ In the example below, the left variable has the subscript of `0`, and the right
|
|
28
29
|
[logic_checker_spec.yml](./spec/examples/rast/logic_checker_spec.yml)
|
29
30
|
|
30
31
|
```yaml
|
32
|
+
---
|
31
33
|
specs:
|
32
34
|
Logical AND:
|
33
35
|
variables:
|
@@ -75,8 +77,8 @@ Finished in 0.00292 seconds (files took 0.26024 seconds to load)
|
|
75
77
|
### Filtering Out Invalid Cases
|
76
78
|
|
77
79
|
In a prior example `HotelFinder`, some cases are invalid. For example, if an
|
78
|
-
aircon is not available, then it makes
|
79
|
-
not. In such case, we
|
80
|
+
aircon is not available, then it makes no sense to check if it is operational
|
81
|
+
not. In such case, we can limit the scenarios with `exclude` clause.
|
80
82
|
|
81
83
|
```yaml
|
82
84
|
# double_example_spec.yml
|
@@ -191,12 +193,12 @@ specs:
|
|
191
193
|
- "Will this work?"
|
192
194
|
- "Let's make a statement"
|
193
195
|
...
|
194
|
-
|
195
196
|
```
|
196
197
|
|
197
198
|
The rules must then be written in a different way, as arrays.
|
198
199
|
|
199
200
|
```yaml
|
201
|
+
---
|
200
202
|
...
|
201
203
|
outcomes:
|
202
204
|
exclamation: ["Let's do it!"]
|
@@ -207,6 +209,7 @@ The rules must then be written in a different way, as arrays.
|
|
207
209
|
If an operation is involved:
|
208
210
|
|
209
211
|
```yaml
|
212
|
+
---
|
210
213
|
outcomes:
|
211
214
|
non-question: ["Let's do it!", '|', "Let's make a statement"]
|
212
215
|
```
|
@@ -257,7 +260,7 @@ Suppose we have a HotelFinder class that has a dependency to air conditioning
|
|
257
260
|
and security
|
258
261
|
|
259
262
|
```ruby
|
260
|
-
rast
|
263
|
+
rast DiplomatHotelFinder do
|
261
264
|
spec '#applicable?' do
|
262
265
|
prepare do |with_ac, is_opererational, with_security, security_grade|
|
263
266
|
if with_ac
|
@@ -277,7 +280,7 @@ Suppose we have a HotelFinder class that has a dependency to air conditioning
|
|
277
280
|
|
278
281
|
```
|
279
282
|
|
280
|
-
### Spec file without
|
283
|
+
### Spec file without YAML
|
281
284
|
|
282
285
|
If a single spec is preferred, like in cases where it's much simplier,
|
283
286
|
the required configuration can be written with similar name except for the
|
data/Gemfile
CHANGED
@@ -5,7 +5,7 @@ source 'https://rubygems.org'
|
|
5
5
|
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
6
6
|
|
7
7
|
group :development, :test do
|
8
|
-
gem '
|
8
|
+
gem 'factory_bot'
|
9
9
|
end
|
10
10
|
|
11
11
|
group :test do
|
@@ -14,5 +14,5 @@ group :test do
|
|
14
14
|
gem 'rb-inotify', '~> 0.9.10'
|
15
15
|
|
16
16
|
gem 'rspec'
|
17
|
-
gem 'simplecov'
|
17
|
+
gem 'simplecov'
|
18
18
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,60 +1,59 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
-
activesupport (
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
tzinfo (~>
|
9
|
-
concurrent-ruby (1.
|
10
|
-
diff-lcs (1.
|
11
|
-
docile (1.
|
12
|
-
|
13
|
-
activesupport (>=
|
14
|
-
ffi (1.
|
15
|
-
i18n (
|
4
|
+
activesupport (7.0.7)
|
5
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
6
|
+
i18n (>= 1.6, < 2)
|
7
|
+
minitest (>= 5.1)
|
8
|
+
tzinfo (~> 2.0)
|
9
|
+
concurrent-ruby (1.2.2)
|
10
|
+
diff-lcs (1.5.0)
|
11
|
+
docile (1.4.0)
|
12
|
+
factory_bot (6.2.1)
|
13
|
+
activesupport (>= 5.0.0)
|
14
|
+
ffi (1.15.5)
|
15
|
+
i18n (1.14.1)
|
16
16
|
concurrent-ruby (~> 1.0)
|
17
|
-
listen (3.0
|
18
|
-
rb-fsevent (~> 0.
|
19
|
-
rb-inotify (~> 0.9, >= 0.9.
|
20
|
-
lumberjack (1.
|
21
|
-
minitest (5.
|
22
|
-
|
23
|
-
rb-fsevent (0.10.3)
|
17
|
+
listen (3.8.0)
|
18
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
19
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
20
|
+
lumberjack (1.2.9)
|
21
|
+
minitest (5.19.0)
|
22
|
+
rb-fsevent (0.11.2)
|
24
23
|
rb-inotify (0.9.10)
|
25
24
|
ffi (>= 0.5.0, < 2)
|
26
|
-
rspec (3.
|
27
|
-
rspec-core (~> 3.
|
28
|
-
rspec-expectations (~> 3.
|
29
|
-
rspec-mocks (~> 3.
|
30
|
-
rspec-core (3.
|
31
|
-
rspec-support (~> 3.
|
32
|
-
rspec-expectations (3.
|
25
|
+
rspec (3.12.0)
|
26
|
+
rspec-core (~> 3.12.0)
|
27
|
+
rspec-expectations (~> 3.12.0)
|
28
|
+
rspec-mocks (~> 3.12.0)
|
29
|
+
rspec-core (3.12.2)
|
30
|
+
rspec-support (~> 3.12.0)
|
31
|
+
rspec-expectations (3.12.3)
|
33
32
|
diff-lcs (>= 1.2.0, < 2.0)
|
34
|
-
rspec-support (~> 3.
|
35
|
-
rspec-mocks (3.
|
33
|
+
rspec-support (~> 3.12.0)
|
34
|
+
rspec-mocks (3.12.6)
|
36
35
|
diff-lcs (>= 1.2.0, < 2.0)
|
37
|
-
rspec-support (~> 3.
|
38
|
-
rspec-support (3.
|
39
|
-
simplecov (0.
|
40
|
-
docile (~> 1.1
|
41
|
-
|
42
|
-
|
43
|
-
simplecov-html (0.
|
44
|
-
|
45
|
-
tzinfo (
|
46
|
-
|
36
|
+
rspec-support (~> 3.12.0)
|
37
|
+
rspec-support (3.12.1)
|
38
|
+
simplecov (0.22.0)
|
39
|
+
docile (~> 1.1)
|
40
|
+
simplecov-html (~> 0.11)
|
41
|
+
simplecov_json_formatter (~> 0.1)
|
42
|
+
simplecov-html (0.12.3)
|
43
|
+
simplecov_json_formatter (0.1.4)
|
44
|
+
tzinfo (2.0.6)
|
45
|
+
concurrent-ruby (~> 1.0)
|
47
46
|
|
48
47
|
PLATFORMS
|
49
|
-
|
48
|
+
arm64-darwin-21
|
50
49
|
|
51
50
|
DEPENDENCIES
|
52
|
-
|
51
|
+
factory_bot
|
53
52
|
listen (>= 3.0.8)
|
54
53
|
lumberjack (~> 1.0, >= 1.0.13)
|
55
54
|
rb-inotify (~> 0.9.10)
|
56
55
|
rspec
|
57
|
-
simplecov
|
56
|
+
simplecov
|
58
57
|
|
59
58
|
BUNDLED WITH
|
60
|
-
|
59
|
+
2.4.18
|
data/Getting-Started-Detailed.md
CHANGED
@@ -57,8 +57,8 @@ accepting a parameter.
|
|
57
57
|
|
58
58
|
#### Step 2: Defining outcomes and the variables that affects the test.
|
59
59
|
|
60
|
-
For the sake of simplicity, we will define the variables and outcomes in a separate
|
61
|
-
steps are purely configurations. Using
|
60
|
+
For the sake of simplicity, we will define the variables and outcomes in a separate YAML file, mainly because the next
|
61
|
+
steps are purely configurations. Using YAML is completely optional.
|
62
62
|
|
63
63
|
Create a folder `rast` in the same level as the spec file:
|
64
64
|
|
@@ -70,8 +70,9 @@ Create a yaml file with the same name as the spec, but with `.yml` extension.
|
|
70
70
|
`spec/rast/positive_spec.yml` would contain:
|
71
71
|
|
72
72
|
```yaml
|
73
|
+
---
|
73
74
|
specs:
|
74
|
-
Is Positive
|
75
|
+
Is Positive Example:
|
75
76
|
variables: {number: [-1, 0, 1]}
|
76
77
|
outcomes: {true: 1}
|
77
78
|
```
|
data/README.md
CHANGED
@@ -9,11 +9,11 @@ RSpec All Scenario Testing
|
|
9
9
|
|
10
10
|
This library runs on top of RSpec to provide basically a parameterized unit testing pattern. It follows a specific pattern of writing unit tests, enabling a predictable, complete and outputs a result that is simple to analyze.
|
11
11
|
|
12
|
-
|
12
|
+
## A Basic Example
|
13
13
|
|
14
|
-
Suppose we want to create a class that checks if a number is a positive
|
14
|
+
Suppose we want to create a class that checks if a number is a positive or a negative number.
|
15
15
|
|
16
|
-
|
16
|
+
### Create a spec file `spec/positive_spec.rb`
|
17
17
|
|
18
18
|
```ruby
|
19
19
|
require 'rast'
|
@@ -25,11 +25,12 @@ rast Positive do
|
|
25
25
|
end
|
26
26
|
```
|
27
27
|
|
28
|
-
|
28
|
+
### Create a spec configuration `spec/rast/positive_spec.yml`
|
29
29
|
|
30
30
|
```yaml
|
31
|
+
---
|
31
32
|
specs:
|
32
|
-
Is Positive
|
33
|
+
Is Positive Example:
|
33
34
|
variables: {number: [-1, 0, 1]}
|
34
35
|
outcomes: {true: 1}
|
35
36
|
```
|
@@ -61,38 +62,46 @@ Finished in 0.00471 seconds (files took 0.47065 seconds to load)
|
|
61
62
|
3 examples, 0 failures
|
62
63
|
```
|
63
64
|
|
64
|
-
|
65
|
+
See the [documentation](./Documentation.md) for more examples.
|
66
|
+
|
67
|
+
## How to use this project
|
68
|
+
1. Run `$ bundle install`
|
69
|
+
2. Run `$ bundle exec rspec`
|
70
|
+
|
71
|
+
### Troubleshooting
|
72
|
+
* Delete the Gemfile.lock and reinstall.
|
73
|
+
|
65
74
|
|
66
75
|
## Contributing
|
67
76
|
|
68
77
|
### Definition of terms
|
69
78
|
|
70
|
-
- `spec` - as defined in the
|
79
|
+
- `spec` - as defined in the YAML file, the individual elements under `specs`.
|
71
80
|
- `scenario` - a specific combination of tokens from vars, it can uniquely identify a fixture.
|
72
81
|
- `token` - used loosely to denote the individual variable in a rule. e.g. `true: you & me`, `you` and `me` are tokens.
|
73
|
-
- `fixture` - a hash containing a scenario,
|
82
|
+
- `fixture` - a hash containing a scenario, references back to the spec, and the expected result for the given scenario.
|
74
83
|
- `variables` - raw list of variables to be combined into multiple fixtures.
|
75
|
-
- `rule` - set of outcomes, each paired with rule clause.
|
76
|
-
- `exclusions` - rule defining variable combinations to be excluded from the test.
|
84
|
+
- `rule` - set of outcomes, each paired with a rule clause.
|
85
|
+
- `exclusions` - rule defining the variable combinations to be excluded from the test.
|
77
86
|
- `inclusions` - rule that limits the scenarios to be included. Useful for isolating test cases.
|
78
|
-
- `outcome` - the left portion `us` of a rule e.g. `us: you&me`
|
79
|
-
- `clause` - the right portion `you&me` of a rule e.g. `us: you&me`
|
87
|
+
- `outcome` - the left portion `us` of a rule, e.g. `us: you&me`
|
88
|
+
- `clause` - the right portion `you&me` of a rule, e.g. `us: you&me`
|
80
89
|
|
81
90
|
## Notes to author
|
82
91
|
|
83
|
-
When running the tests, the execution starts at the spec file, then
|
92
|
+
When running the tests, the execution starts at the spec file, then invokes the
|
84
93
|
DSL. The DSL will then invoke the parameter generator to generate the scenarios.
|
85
94
|
|
86
95
|
### Releasing new features/bugfix
|
87
96
|
|
88
|
-
- Increment the .gemspec
|
89
|
-
- Modify the CHANGELOG.md
|
97
|
+
- Increment the [.gemspec](./rast.gemspec)
|
98
|
+
- Modify the [CHANGELOG.md](./CHANGELOG.md)
|
90
99
|
|
91
|
-
### Releasing GEM
|
100
|
+
### Releasing the GEM
|
92
101
|
|
93
102
|
- Build gem with `gem build rast.gemspec`
|
94
103
|
- Publish with `gem push <gem-filename>`
|
95
104
|
|
96
105
|
## References
|
97
106
|
|
98
|
-
[Semantic Versioning](https://semver.org)
|
107
|
+
* [Semantic Versioning](https://semver.org)
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'yaml'
|
4
|
-
|
4
|
+
|
5
5
|
require 'rast/rules/rule'
|
6
6
|
require 'rast/rules/rule_evaluator'
|
7
|
-
|
7
|
+
|
8
|
+
require 'rast/rast_spec'
|
9
|
+
require 'rast/rule_validator'
|
8
10
|
|
9
11
|
# Generates the test parameters.
|
10
12
|
class ParameterGenerator
|
@@ -36,6 +38,9 @@ class ParameterGenerator
|
|
36
38
|
list = []
|
37
39
|
|
38
40
|
variables = spec.variables
|
41
|
+
# dict.first method returns the first key and value as an array, so invoking
|
42
|
+
# .first, then . last would return the
|
43
|
+
# first value.
|
39
44
|
var_first = variables.first
|
40
45
|
multipliers = []
|
41
46
|
|
@@ -67,12 +72,12 @@ class ParameterGenerator
|
|
67
72
|
|
68
73
|
include_result = true
|
69
74
|
unless spec.exclude_clause.nil?
|
70
|
-
include_result =
|
75
|
+
include_result = qualify_scenario?(spec, scenario, false)
|
71
76
|
end
|
72
77
|
|
73
78
|
return include_result if no_include_or_dont_include?(spec, include_result)
|
74
79
|
|
75
|
-
|
80
|
+
qualify_scenario?(spec, scenario, true)
|
76
81
|
end
|
77
82
|
|
78
83
|
# blech!
|
@@ -80,14 +85,13 @@ class ParameterGenerator
|
|
80
85
|
spec.include_clause.nil? || !include_result
|
81
86
|
end
|
82
87
|
|
83
|
-
def
|
88
|
+
def qualify_scenario?(spec, scenario, is_included)
|
84
89
|
action = is_included ? 'include' : 'exclude'
|
85
|
-
rule_evaluator = RuleEvaluator.new(
|
90
|
+
rule_evaluator = RuleEvaluator.new(token_converters: spec.token_converter)
|
86
91
|
clause = Rule.sanitize(clause: spec.send("#{action}_clause"))
|
87
92
|
rule_evaluator.parse(expression: clause)
|
88
93
|
rule_evaluator.evaluate(
|
89
|
-
scenario: scenario
|
90
|
-
rule_token_convert: spec.token_converter
|
94
|
+
scenario: scenario
|
91
95
|
) == is_included.to_s
|
92
96
|
end
|
93
97
|
|
data/lib/rast/rast_spec.rb
CHANGED
@@ -5,17 +5,21 @@ require 'rast/rules/rule_processor'
|
|
5
5
|
# Validates rules
|
6
6
|
class RuleValidator
|
7
7
|
def validate(scenario: [], fixture: {})
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
spec = fixture[:spec]
|
9
|
+
rule_processor = RuleProcessor.new(
|
10
|
+
rule: spec.rule,
|
11
|
+
token_converters: spec.token_converter
|
11
12
|
)
|
12
13
|
|
14
|
+
rule_result = rule_processor.evaluate(scenario: scenario)
|
15
|
+
|
13
16
|
spec = fixture[:spec]
|
14
17
|
validate_results(scenario, rule_result, spec)
|
15
18
|
end
|
16
19
|
|
17
20
|
private
|
18
21
|
|
22
|
+
# @returns string
|
19
23
|
def validate_results(scenario, rule_result, spec)
|
20
24
|
rule = spec.rule
|
21
25
|
single_result = rule.size == 1
|
@@ -32,7 +36,7 @@ class RuleValidator
|
|
32
36
|
matched_outputs = []
|
33
37
|
match_count = 0
|
34
38
|
|
35
|
-
rule_result.
|
39
|
+
to_boolean(string_list: rule_result).each_with_index do |result, i|
|
36
40
|
next unless result
|
37
41
|
|
38
42
|
match_count += 1
|
@@ -44,6 +48,13 @@ class RuleValidator
|
|
44
48
|
matched_outputs.first || spec.default_outcome
|
45
49
|
end
|
46
50
|
|
51
|
+
# @returns array of boolean from array of strings. 'true' becomes true.
|
52
|
+
def to_boolean(string_list: [])
|
53
|
+
string_list.map do |result|
|
54
|
+
result.to_s == 'true'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
47
58
|
def verify_results(spec, scenario, matched_outputs, match_count)
|
48
59
|
Rast.assert("#{spec.description} #{scenario} must fall into a unique rule" \
|
49
60
|
" outcome/clause, matched: #{matched_outputs}") do
|
@@ -51,7 +62,7 @@ class RuleValidator
|
|
51
62
|
end
|
52
63
|
end
|
53
64
|
|
54
|
-
def binary_outcome(outcome: '', spec: nil, expected: false)
|
65
|
+
def binary_outcome(outcome: '', spec: nil, expected: 'false')
|
55
66
|
if expected == 'true'
|
56
67
|
outcome
|
57
68
|
else
|
@@ -26,9 +26,10 @@ module LogicHelper
|
|
26
26
|
# * @left left left token object.
|
27
27
|
# * @right right right token object.
|
28
28
|
# * @operation :and or :or.
|
29
|
+
# * @returns String boolean value, can be internal, thus it is string.
|
29
30
|
# */
|
30
31
|
def perform_logical(scenario: [], left: {}, right: {}, operation: :nil)
|
31
|
-
evaluated =
|
32
|
+
evaluated = both_internal?(left, right, operation)
|
32
33
|
return evaluated if evaluated
|
33
34
|
|
34
35
|
default = operation == :and ? TRUE : FALSE
|
@@ -83,8 +84,7 @@ module LogicHelper
|
|
83
84
|
|
84
85
|
return false unless left_eval
|
85
86
|
|
86
|
-
|
87
|
-
left_eval && right_eval
|
87
|
+
present?(scenario, right)
|
88
88
|
end
|
89
89
|
|
90
90
|
def evaluate_or(scenario, left, right)
|
@@ -92,8 +92,7 @@ module LogicHelper
|
|
92
92
|
|
93
93
|
return true if left_eval
|
94
94
|
|
95
|
-
|
96
|
-
left_eval || right_eval
|
95
|
+
present?(scenario, right)
|
97
96
|
end
|
98
97
|
|
99
98
|
# /**
|
data/lib/rast/rules/rule.rb
CHANGED
@@ -10,25 +10,12 @@ class Rule
|
|
10
10
|
# * <b>Parameter Example:</b><br/>
|
11
11
|
# * Visible:Proposed|Approved<br/>
|
12
12
|
# * <br/>
|
13
|
-
# * Peace:Friendly|Indifferent\<br/>
|
14
|
-
# * ~War:Angry\<br/>
|
15
|
-
# * ~Neutral:Play safe
|
16
13
|
# */
|
17
14
|
def initialize(rules: {})
|
18
15
|
raise 'Must not have empty rules' if rules.empty?
|
19
16
|
|
20
17
|
@outcome_clause_hash = {}
|
21
|
-
# should have array of the rule pairs
|
22
|
-
# rules = pActRuleSrc.split("~");
|
23
|
-
duplicates = []
|
24
|
-
|
25
18
|
rules.each do |outcome, clause|
|
26
|
-
# if duplicates.include?(outcome)
|
27
|
-
# raise "#{outcome} matched multiple clauses"
|
28
|
-
# end
|
29
|
-
|
30
|
-
# duplicates << outcome
|
31
|
-
|
32
19
|
@outcome_clause_hash[outcome.to_s] = Rule.sanitize(clause: clause)
|
33
20
|
end
|
34
21
|
end
|
@@ -31,7 +31,6 @@ class RuleEvaluator
|
|
31
31
|
DEFAULT_CONVERT_HASH = {
|
32
32
|
Integer => IntConverter.new,
|
33
33
|
Float => FloatConverter.new,
|
34
|
-
Fixnum => IntConverter.new,
|
35
34
|
Array => DefaultConverter.new,
|
36
35
|
TrueClass => BoolConverter.new,
|
37
36
|
FalseClass => BoolConverter.new,
|
@@ -39,9 +38,9 @@ class RuleEvaluator
|
|
39
38
|
NilClass => DefaultConverter.new
|
40
39
|
}.freeze
|
41
40
|
|
42
|
-
# /** @param
|
43
|
-
def initialize(
|
44
|
-
@
|
41
|
+
# /** @param token_converters token to converter mapping */
|
42
|
+
def initialize(token_converters: {})
|
43
|
+
@token_converters = token_converters
|
45
44
|
|
46
45
|
@stack_operations = []
|
47
46
|
@stack_rpn = []
|
@@ -53,6 +52,7 @@ class RuleEvaluator
|
|
53
52
|
# *
|
54
53
|
# * @param pExpression <code>String</code> input expression (logical
|
55
54
|
# * expression formula)
|
55
|
+
# * @return void.
|
56
56
|
# * @since 0.3.0
|
57
57
|
# */
|
58
58
|
def parse(expression: '')
|
@@ -86,13 +86,13 @@ class RuleEvaluator
|
|
86
86
|
# * @param rule_token_convert mapping of rule tokens to converter.
|
87
87
|
# * @return <code>String</code> representation of the result
|
88
88
|
# */
|
89
|
-
def evaluate(scenario: []
|
89
|
+
def evaluate(scenario: [])
|
90
90
|
if @stack_rpn.size == 1
|
91
91
|
evaluate_one_rpn(scenario: scenario).to_s
|
92
92
|
else
|
93
93
|
evaluate_multi_rpn(
|
94
94
|
scenario: scenario,
|
95
|
-
rule_token_convert:
|
95
|
+
rule_token_convert: @token_converters
|
96
96
|
)
|
97
97
|
end
|
98
98
|
end
|
@@ -110,19 +110,19 @@ class RuleEvaluator
|
|
110
110
|
|
111
111
|
return default if token.is_a?(Array) || [TRUE, FALSE].include?(token)
|
112
112
|
|
113
|
-
next_value_default(
|
113
|
+
next_value_default(token)
|
114
114
|
end
|
115
115
|
|
116
116
|
# private
|
117
|
-
def next_value_default(
|
117
|
+
def next_value_default(token)
|
118
118
|
token_cleaned = token.to_s.strip
|
119
119
|
subscript = TokenUtil.extract_subscript(token: token_cleaned)
|
120
120
|
token_body = subscript > -1 ? token_cleaned[/^.+(?=\[)/] : token_cleaned
|
121
121
|
|
122
|
-
raise "Config Error: Outcome clause token: '#{token}' not found in variables" if
|
122
|
+
raise "Config Error: Outcome clause token: '#{token}' not found in variables" if @token_converters[token_body].nil?
|
123
123
|
|
124
124
|
{
|
125
|
-
value:
|
125
|
+
value: @token_converters[token_body].convert(token_body),
|
126
126
|
subscript: subscript
|
127
127
|
}
|
128
128
|
end
|
@@ -4,20 +4,21 @@ require 'rast/rules/rule_evaluator'
|
|
4
4
|
|
5
5
|
# undoc
|
6
6
|
class RuleProcessor
|
7
|
+
def initialize(rule: nil, token_converters: {})
|
8
|
+
@rule = rule
|
9
|
+
@token_converters = token_converters
|
10
|
+
end
|
11
|
+
|
7
12
|
# /**
|
8
13
|
# * @param scenario current scenario.
|
9
|
-
# *
|
14
|
+
# *
|
15
|
+
# * @return the outcome results against the given scenario.
|
10
16
|
# */
|
11
|
-
def evaluate(scenario: []
|
12
|
-
|
13
|
-
# raise 'Scenario or fixture cannot be nil.'
|
14
|
-
# end
|
15
|
-
|
16
|
-
fixture[:spec].rule.outcomes.inject([]) do |retval, outcome|
|
17
|
+
def evaluate(scenario: [])
|
18
|
+
@rule.outcomes.inject([]) do |retval, outcome|
|
17
19
|
process_outcome(
|
18
|
-
scenario: scenario,
|
19
|
-
fixture: fixture,
|
20
20
|
list: retval,
|
21
|
+
scenario: scenario,
|
21
22
|
outcome: outcome
|
22
23
|
)
|
23
24
|
end
|
@@ -25,17 +26,12 @@ class RuleProcessor
|
|
25
26
|
|
26
27
|
private
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
rule_evaluator = RuleEvaluator.new(converters: spec.converters)
|
33
|
-
|
29
|
+
# @returns the list argument with the new result.
|
30
|
+
def process_outcome(list: [], scenario: [], outcome: '')
|
31
|
+
clause = @rule.clause(outcome: outcome)
|
32
|
+
rule_evaluator = RuleEvaluator.new(token_converters: @token_converters)
|
34
33
|
rule_evaluator.parse(expression: clause)
|
35
34
|
|
36
|
-
list << rule_evaluator.evaluate(
|
37
|
-
scenario: scenario,
|
38
|
-
rule_token_convert: spec.token_converter
|
39
|
-
)
|
35
|
+
list << rule_evaluator.evaluate(scenario: scenario)
|
40
36
|
end
|
41
37
|
end
|
data/lib/rast/spec_dsl.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'factory_bot'
|
4
4
|
require 'rast/parameter_generator'
|
5
5
|
|
6
6
|
# Main DSL. This is the entry point of the test when running a spec.
|
7
7
|
class SpecDSL
|
8
|
-
include
|
8
|
+
include FactoryBot::Syntax::Methods
|
9
9
|
|
10
10
|
attr_accessor :subject, :execute_block,
|
11
11
|
:prepare_block, :outcomes, :fixtures, :spec_id
|
@@ -144,13 +144,16 @@ def generate_rspec(scope: nil, scenario: {}, expected: '')
|
|
144
144
|
instance_exec(*block_params, &scope.prepare_block)
|
145
145
|
end
|
146
146
|
|
147
|
-
actual = scope
|
148
|
-
actual = actual.nil? ? 'nil' : actual.to_s
|
149
|
-
|
147
|
+
actual = execute_and_format_result(scope, block_params)
|
150
148
|
expect(actual).to eq(expected)
|
151
149
|
end
|
152
150
|
end
|
153
151
|
|
152
|
+
def execute_and_format_result(scope, block_params)
|
153
|
+
actual = scope.execute_block.call(*block_params)
|
154
|
+
actual.nil? ? 'nil' : actual.to_s
|
155
|
+
end
|
156
|
+
|
154
157
|
def _it_title(expected, scenario)
|
155
158
|
spec_params = scenario.keys.inject('') do |output, key|
|
156
159
|
build_it(scenario, output, key)
|
data/rast.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = 'rast'
|
5
|
-
spec.version = '0.
|
5
|
+
spec.version = '1.0.0'
|
6
6
|
spec.authors = ['Royce Remulla']
|
7
7
|
spec.email = ['royce.com@gmail.com']
|
8
8
|
|
@@ -10,9 +10,9 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.description = 'Extends RSpec functionality by using the catch-all-scenario testing (CAST) principle.'
|
11
11
|
spec.homepage = 'https://github.com/roycetech/rast'
|
12
12
|
spec.license = 'MIT'
|
13
|
-
spec.required_ruby_version = Gem::Requirement.new('~> 2
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new('~> 3.2')
|
14
14
|
|
15
|
-
spec.add_runtime_dependency '
|
15
|
+
spec.add_runtime_dependency 'factory_bot', '~> 6.2'
|
16
16
|
|
17
17
|
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
18
18
|
spec.metadata['homepage_uri'] = spec.homepage
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Royce Remulla
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: factory_bot
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '6.2'
|
27
27
|
description: Extends RSpec functionality by using the catch-all-scenario testing (CAST)
|
28
28
|
principle.
|
29
29
|
email:
|
@@ -35,6 +35,7 @@ files:
|
|
35
35
|
- ".gitignore"
|
36
36
|
- ".rspec"
|
37
37
|
- ".rubocop.yml"
|
38
|
+
- ".ruby-version"
|
38
39
|
- ".travis.yml"
|
39
40
|
- CHANGELOG.md
|
40
41
|
- Documentation.md
|
@@ -43,9 +44,9 @@ files:
|
|
43
44
|
- Getting-Started-Detailed.md
|
44
45
|
- Getting-Started.md
|
45
46
|
- README.md
|
47
|
+
- examples/diplomat_hotel_finder.rb
|
46
48
|
- examples/enum_module.rb
|
47
49
|
- examples/factory_example.rb
|
48
|
-
- examples/hotel_finder.rb
|
49
50
|
- examples/logic_checker.rb
|
50
51
|
- examples/person.rb
|
51
52
|
- examples/positive.rb
|
@@ -62,12 +63,12 @@ files:
|
|
62
63
|
- lib/rast/converters/str_converter.rb
|
63
64
|
- lib/rast/parameter_generator.rb
|
64
65
|
- lib/rast/rast_spec.rb
|
66
|
+
- lib/rast/rule_validator.rb
|
65
67
|
- lib/rast/rules/logic_helper.rb
|
66
68
|
- lib/rast/rules/operator.rb
|
67
69
|
- lib/rast/rules/rule.rb
|
68
70
|
- lib/rast/rules/rule_evaluator.rb
|
69
71
|
- lib/rast/rules/rule_processor.rb
|
70
|
-
- lib/rast/rules/rule_validator.rb
|
71
72
|
- lib/rast/rules/token_util.rb
|
72
73
|
- lib/rast/spec_dsl.rb
|
73
74
|
- lib/template_spec.yml
|
@@ -79,7 +80,7 @@ metadata:
|
|
79
80
|
homepage_uri: https://github.com/roycetech/rast
|
80
81
|
source_code_uri: https://github.com/roycetech/rast
|
81
82
|
changelog_uri: https://github.com/roycetech/rast/blob/feature/ruby-2.0.0-support/CHANGELOG.md
|
82
|
-
post_install_message:
|
83
|
+
post_install_message:
|
83
84
|
rdoc_options: []
|
84
85
|
require_paths:
|
85
86
|
- lib
|
@@ -87,15 +88,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
87
88
|
requirements:
|
88
89
|
- - "~>"
|
89
90
|
- !ruby/object:Gem::Version
|
90
|
-
version: '2
|
91
|
+
version: '3.2'
|
91
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
93
|
requirements:
|
93
94
|
- - ">="
|
94
95
|
- !ruby/object:Gem::Version
|
95
96
|
version: '0'
|
96
97
|
requirements: []
|
97
|
-
rubygems_version: 3.
|
98
|
-
signing_key:
|
98
|
+
rubygems_version: 3.4.18
|
99
|
+
signing_key:
|
99
100
|
specification_version: 4
|
100
101
|
summary: RSpec AST - All Scenario Testing
|
101
102
|
test_files: []
|