rast 0.1.0.pre

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: eb63e35c4439e5fc90b4e25ce9981739f624a114d6f8848eb07576eac93e9222
4
+ data.tar.gz: 12f0b43bd2385dada9e6272e8895054911c3bee8fbe2ff267914ceae996f5a82
5
+ SHA512:
6
+ metadata.gz: 5c39d99e9dde201939565dc47372a0cff3be2e83a77ca2162275982ac15b7e73fc8fa83270761ed7b615093e078ed0d6a4324f30883de1258bb38a14082f8fad
7
+ data.tar.gz: 103f18839c943b5c30af075fe567386d553be83a7f9f4598e5a77c3917df19362a60fbcaca65bf30873dcbc52b64470cad6318584a037d560732af0eb24921c0
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ coverage/
2
+ *.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,3 @@
1
+ ---
2
+ AllCops:
3
+ TargetRubyVersion: 2.6
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ group :test do
8
+ gem 'guard-rspec', require: false
9
+ gem 'pry'
10
+ gem 'pry-nav'
11
+ gem 'rspec'
12
+ gem 'simplecov'
13
+ gem 'simplecov-html'
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,72 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ coderay (1.1.2)
5
+ diff-lcs (1.3)
6
+ docile (1.3.2)
7
+ ffi (1.12.2)
8
+ formatador (0.2.5)
9
+ guard (2.16.2)
10
+ formatador (>= 0.2.4)
11
+ listen (>= 2.7, < 4.0)
12
+ lumberjack (>= 1.0.12, < 2.0)
13
+ nenv (~> 0.1)
14
+ notiffany (~> 0.0)
15
+ pry (>= 0.9.12)
16
+ shellany (~> 0.0)
17
+ thor (>= 0.18.1)
18
+ guard-compat (1.2.1)
19
+ guard-rspec (4.7.3)
20
+ guard (~> 2.1)
21
+ guard-compat (~> 1.1)
22
+ rspec (>= 2.99.0, < 4.0)
23
+ listen (3.2.1)
24
+ rb-fsevent (~> 0.10, >= 0.10.3)
25
+ rb-inotify (~> 0.9, >= 0.9.10)
26
+ lumberjack (1.2.4)
27
+ method_source (0.9.2)
28
+ nenv (0.3.0)
29
+ notiffany (0.1.3)
30
+ nenv (~> 0.1)
31
+ shellany (~> 0.0)
32
+ pry (0.12.2)
33
+ coderay (~> 1.1.0)
34
+ method_source (~> 0.9.0)
35
+ pry-nav (0.3.0)
36
+ pry (>= 0.9.10, < 0.13.0)
37
+ rb-fsevent (0.10.3)
38
+ rb-inotify (0.10.1)
39
+ ffi (~> 1.0)
40
+ rspec (3.9.0)
41
+ rspec-core (~> 3.9.0)
42
+ rspec-expectations (~> 3.9.0)
43
+ rspec-mocks (~> 3.9.0)
44
+ rspec-core (3.9.1)
45
+ rspec-support (~> 3.9.1)
46
+ rspec-expectations (3.9.0)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.9.0)
49
+ rspec-mocks (3.9.1)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.9.0)
52
+ rspec-support (3.9.2)
53
+ shellany (0.0.1)
54
+ simplecov (0.18.5)
55
+ docile (~> 1.1)
56
+ simplecov-html (~> 0.11)
57
+ simplecov-html (0.12.2)
58
+ thor (1.0.1)
59
+
60
+ PLATFORMS
61
+ ruby
62
+
63
+ DEPENDENCIES
64
+ guard-rspec
65
+ pry
66
+ pry-nav
67
+ rspec
68
+ simplecov
69
+ simplecov-html
70
+
71
+ BUNDLED WITH
72
+ 2.1.4
data/Guardfile ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A sample Guardfile
4
+ # More info at https://github.com/guard/guard#readme
5
+
6
+ ## Uncomment and set this to only include directories you want to watch
7
+ # directories %w(app lib config test spec features) \
8
+ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
9
+
10
+ ## Note: if you are using the `directories` clause above and you are not
11
+ ## watching the project directory ('.'), then you will want to move
12
+ ## the Guardfile to a watched dir and symlink it back, e.g.
13
+ #
14
+ # $ mkdir config
15
+ # $ mv Guardfile config/
16
+ # $ ln -s config/Guardfile .
17
+ #
18
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
19
+
20
+ # guard :rubocop do
21
+ # watch(%r{.+\.rb$})
22
+ # watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
23
+ # end
24
+
25
+ guard :rspec, cmd: 'bundle exec rspec -fd' do
26
+ require 'guard/rspec/dsl'
27
+
28
+ Guard::RSpec::Dsl.new(self)
29
+
30
+ watch(%r{^spec/.+_spec\.rb$})
31
+ # watch(%r{^spec\/.*?\/(.+_spec\.)yml$}) { |m| "#{m}.rb" }
32
+ watch(%r{^lib/(.+)\.rb$}) { |_m| 'spec/examples/prime_number_spec.rb' }
33
+ # watch(%r{^lib/(.+)\.rb$}) { |_m| 'spec/examples/logic_checker_spec.rb' }
34
+ # watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
35
+ watch('spec/spec_helper.rb') { 'spec' }
36
+ end
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # Overview
2
+ RSpec All Scenario Testing
3
+
4
+ ## Definition of terms
5
+
6
+ spec - as defined in the yaml file, the individual elements under `specs`
7
+
8
+ scenario - a specific combination of tokens from vars, it can uniquely identify a fixture.
9
+
10
+ fixture - instance of a spec, containing a scenario, reference back to the spec, and the expected result for the given scenario.
11
+
12
+ vars - raw list of variables to be combined into multiple fixtures.
13
+
14
+ rule - set of outcome paired with rule clause.
15
+ exemption/exclusions - rule defining variable combinations to be excluded from the test.
16
+
17
+ outcome - the left portion of a rule e.g. `true: true&true`
18
+
19
+ clause - the right portion of a rule
20
+
21
+
22
+ ##
23
+
24
+ When running the tests, the execution starts at the spec file, then invoking the
25
+ DSL. The DSL will then invoke the parameter generator to generate the scenarios.
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # LogicChecker ported from Java
4
+ #
5
+ # @author Royce Remulla
6
+ #
7
+ class LogicChecker
8
+ # Perform logical AND operation on two arguments.
9
+ #
10
+ # @param argument1 first argument of Boolean type.
11
+ # @param argument2 second argument of Boolean type.
12
+ def and(argument1, argument2)
13
+ argument1 && argument2
14
+ end
15
+
16
+ # Perform logical OR operation on two arguments.
17
+ #
18
+ # @param argument1 first argument of Boolean type.
19
+ # @param argument2 second argument of Boolean type.
20
+ def or(argument1, argument2)
21
+ argument1 || argument2
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example that returns true if the number passed is a prime number
4
+ class PrimeNumber
5
+ def prime?(number)
6
+ is_prime = true
7
+ raise "Invalid number: #{number}" if number <= 0
8
+
9
+ if number == 1
10
+ is_prime = false
11
+ elsif number > 2
12
+ (2...number).each do |i|
13
+ is_prime = number % i != 0
14
+ break unless is_prime
15
+ end
16
+ end
17
+ is_prime
18
+ end
19
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Rules
4
+ class Recruiter
5
+ def score; end
6
+
7
+ def position; end
8
+
9
+ def assess
10
+ case position
11
+ when 'Manager'
12
+ assess_manager(score)
13
+ when 'Senior Engineer'
14
+ assess_sr_engineer(score)
15
+ when 'Engineer'
16
+ assess_engineer(score)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ # Shortlist score of 9, accept 10, and reject the rest
23
+ def assess_manager(score)
24
+ return 'Shortlist' if score == 9
25
+
26
+ return 'Accept' if score == 10
27
+
28
+ 'Reject'
29
+ end
30
+
31
+ # Shortlist score of 8, accept 9 or 10, and reject the rest
32
+ def assess_sr_engineer(score)
33
+ return 'Shortlist' if score == 8
34
+
35
+ return 'Accept' if score > 8
36
+
37
+ 'Reject'
38
+ end
39
+
40
+ # Score of 9 and above are overqualified, shortlist 7, accept 8, and reject the rest.
41
+ def assess_engineer(score)
42
+ return 'Over Qualified' if score > 8
43
+
44
+ return 'Accept' if score == 8
45
+
46
+ return 'Shortlist' if score == 7
47
+
48
+ 'Reject'
49
+ end
50
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example
4
+ class Worker
5
+ def day_of_week; end
6
+
7
+ def holiday?; end
8
+
9
+ def goto_work?
10
+ !holiday? && day_of_week != :Sunday
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Used to provide assertion check similar to java
4
+
5
+ def assert(message)
6
+ raise message unless yield
7
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Converts string into boolean
4
+ class BoolConverter
5
+ def convert(string)
6
+ return nil if string == 'nil'
7
+
8
+ string == 'true'
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Converts string into a floating type number
4
+ class FloatConverter
5
+ def convert(string)
6
+ string.to_f
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Converts string into an integer
4
+ class IntConverter
5
+ def convert(string)
6
+ string.to_i
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Converts string into number
4
+ class StrConverter
5
+ def convert(string)
6
+ string
7
+ end
8
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pry'
4
+ require 'yaml'
5
+ require_relative 'rast_spec'
6
+ require_relative 'rules/rule'
7
+ require_relative 'rules/rule_evaluator'
8
+ require_relative 'rules/rule_validator'
9
+
10
+ require_relative 'converters/float_converter'
11
+
12
+ # Generates the test parameters.
13
+ class ParameterGenerator
14
+ def initialize(yaml_path: '')
15
+ @specs_config = YAML.load_file(yaml_path)['specs']
16
+
17
+ # p @specs_config
18
+ end
19
+
20
+ # addCase. Generate the combinations, then add the fixture to the final list
21
+ def generate_fixtures(spec_id: '')
22
+ spec_config = @specs_config[spec_id]
23
+
24
+ spec_config[:description] = spec_id
25
+ spec = instantiate_spec(spec_config)
26
+
27
+ list = []
28
+
29
+ variables = spec.variables
30
+ var_first = spec.variables.first
31
+ multipliers = []
32
+
33
+ (1...variables.size).each { |i| multipliers << variables.values[i].dup }
34
+ scenarios = var_first.last.product(*multipliers)
35
+ add_fixtures(scenarios: scenarios, spec: spec, list: list)
36
+ list
37
+ end
38
+
39
+ private
40
+
41
+ def valid_case?(scenario, spec)
42
+ return true if spec.exempt_rule.nil?
43
+
44
+ exempt_rule = fixture.exempt_rule
45
+ rule_evaluator = RuleEvaluator.new(converters: spec.converters)
46
+ rule_evaluator.parse(exempt_rule)
47
+ !ruleEvaluator.evaluate(scenario, spec.converters)
48
+ end
49
+
50
+ # add all fixtures to the list.
51
+ def add_fixtures(scenarios: [], spec: nil, list: [])
52
+ validator = RuleValidator.new
53
+
54
+ scenarios.each do |scenario|
55
+ next unless valid_case?(scenario, spec)
56
+
57
+ list << build_param(validator, scenario, spec)
58
+ end
59
+ end
60
+
61
+ def build_param(validator, scenario, spec)
62
+ param = { spec: spec, scenario: {} }
63
+ token_converter = {}
64
+
65
+ spec.variables.keys.each_with_index do |key, index|
66
+ spec.variables[key].each do |element|
67
+ unless spec.converters.nil?
68
+ token_converter[element.to_s] = spec.converters[index]
69
+ end
70
+ end
71
+ end
72
+
73
+ spec.variables.keys.zip(scenario) do |array|
74
+ var_name = array.first.to_sym
75
+ var_value = array.last
76
+ param[:scenario][var_name] = var_value
77
+ end
78
+
79
+ param[:converter_hash] = token_converter
80
+ param[:expected_outcome] = validator.validate(
81
+ scenario: scenario,
82
+ fixture: param
83
+ )
84
+
85
+ param
86
+ end
87
+
88
+ def instantiate_spec(spec_config)
89
+ spec = RastSpec.new(
90
+ description: spec_config[:description],
91
+ variables: spec_config['variables'],
92
+ rule: Rule.new(rules: spec_config['rules'])
93
+ )
94
+
95
+ pair_config = spec_config['pair']
96
+ spec.init_pair(pair_config: pair_config) unless pair_config.nil?
97
+
98
+ converters = if spec_config['converters'].nil?
99
+ str_converter = StrConverter.new
100
+ spec_config['variables'].map { |_var| str_converter }
101
+ else
102
+ spec_config['converters'].map do |converter|
103
+ Object.const_get(converter).new
104
+ end
105
+ end
106
+
107
+ spec.init_converters(converters: converters)
108
+ end
109
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # CaseFixture.java, containing an actual and specific combination of variables.
4
+ class RastSpec
5
+ attr_reader :variables, :pair, :pair_reversed, :rule, :description,
6
+ :exempt_rule, :converters
7
+
8
+ attr_accessor :exclude
9
+
10
+ def initialize(description: '', variables: [][], rule: nil)
11
+ @description = description
12
+ @variables = variables
13
+ @pair = {}
14
+ @pair_reversed = {}
15
+ @rule = rule
16
+ @exempt_rule = nil
17
+ end
18
+
19
+ def init_pair(pair_config: {})
20
+ @pair[pair_config.keys.first.to_s] = pair_config.values.first.to_s
21
+ @pair_reversed = [@pair.to_a.first.reverse].to_h
22
+ self
23
+ end
24
+
25
+ def init_converters(converters: [])
26
+ @converters = converters
27
+ self
28
+ end
29
+
30
+ def init_exempt_rule(exempt_rule)
31
+ @exempt_rule = exempt_rule
32
+ self
33
+ end
34
+
35
+ def to_s
36
+ "Class: #{self.class}
37
+ Description: #{@description}
38
+ Variables: #{@variables}
39
+ Rules: #{@rules}
40
+ Pair: #{@pair}
41
+ Converters: #{@converters}
42
+ "
43
+ end
44
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Provides logic evaluation functionalities.
4
+ module LogicHelper
5
+ # /**
6
+ # * Custom logical AND/OR evaluator.
7
+ # *
8
+ # * @author Royce
9
+ # */
10
+
11
+ TRUE = '|true'
12
+ FALSE = '|false'
13
+
14
+ # /**
15
+ # * @param scenario list of scenario tokens.
16
+ # * @param left_subscript left index.
17
+ # * @param right_subscript right index.
18
+ # * @param left left token.
19
+ # * @param right right token.
20
+ # */
21
+ def perform_logical_and(scenario: [], left_subscript: -1, right_subscript: -1,
22
+ left: nil, right: nil)
23
+ if FALSE == left || FALSE == right
24
+ FALSE
25
+ elsif TRUE == left && TRUE == right
26
+ TRUE
27
+ elsif TRUE == left
28
+ if right_subscript.negative?
29
+ scenario.include?(right).to_s
30
+ else
31
+ (scenario[right_subscript] == right).to_s
32
+ end
33
+ elsif TRUE == right
34
+ if left_subscript.negative?
35
+ scenario.include?(left).to_s
36
+ else
37
+ (scenario[left_subscript] == left).to_s
38
+ end
39
+ else
40
+ left_eval = pevaluate(
41
+ scenario: scenario,
42
+ subscript: left_subscript,
43
+ object: left
44
+ )
45
+
46
+ right_eval = pevaluate(
47
+ scenario: scenario,
48
+ subscript: right_subscript,
49
+ object: right
50
+ )
51
+
52
+ (left_eval && right_eval).to_s
53
+ end
54
+ end
55
+
56
+ # /**
57
+ # * @param scenario list of scenario tokens.
58
+ # * @param left_subscript left index.
59
+ # * @param right_subscript right index.
60
+ # * @param left left token.
61
+ # * @param right right token.
62
+ # */
63
+ def perform_logical_or(scenario: [], left_subscript: -1, right_subscript: -1,
64
+ left: nil, right: nil)
65
+ if TRUE == left || TRUE == right
66
+ TRUE
67
+ elsif FALSE == left && FALSE == right
68
+ FALSE
69
+ elsif FALSE == left
70
+ if right_subscript.negative?
71
+ scenario.include?(right).to_s
72
+ else
73
+ (scenario[right_subscript] == right).to_s
74
+ end
75
+ elsif FALSE == right
76
+ if left_subscript.negative?
77
+ scenario.include?(left).to_s
78
+ else
79
+ (scenario[left_subscript]).to_s == left
80
+ end
81
+ else
82
+ left_eval = pevaluate(
83
+ scenario: scenario,
84
+ subscript: left_subscript,
85
+ object: left
86
+ )
87
+
88
+ right_eval = pevaluate(
89
+ scenario: scenario,
90
+ subscript: right_subscript,
91
+ object: right
92
+ )
93
+
94
+ (left_eval || right_eval).to_s
95
+ end
96
+ end
97
+
98
+ # /**
99
+ # * Helper method to evaluate left or right token.
100
+ # *
101
+ # * @param scenario list of scenario tokens.
102
+ # * @param subscript scenario token subscript.
103
+ # * @param object left or right token.
104
+ # */
105
+ def pevaluate(scenario: [], subscript: -1, object: nil)
106
+ if subscript.negative?
107
+ scenario.include?(object)
108
+ else
109
+ scenario[subscript] == object
110
+ end
111
+ end
112
+
113
+ # /**
114
+ # * Check if the token is opening bracket.
115
+ # *
116
+ # * @param token Input <code>String</code> token
117
+ # * @return <code>boolean</code> output
118
+ # */
119
+ def open_bracket?(token: '')
120
+ token == '('
121
+ end
122
+
123
+ # /**
124
+ # * Check if the token is closing bracket.
125
+ # *
126
+ # * @param token Input <code>String</code> token
127
+ # * @return <code>boolean</code> output
128
+ # */
129
+ def close_bracket?(token: '')
130
+ token == ')'
131
+ end
132
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Operator Enum in Java. To be used only internally in RuleEvaluator class.
4
+ class Operator
5
+ # Not('!', Byte.MAX_VALUE), And('&', (byte) 2), Or('|', (byte) 1)
6
+
7
+ attr_reader :name, :symbol, :precedence
8
+
9
+ def initialize(name: '', symbol: '', precedence: -1)
10
+ @name = name
11
+ @symbol = symbol
12
+ @precedence = precedence
13
+ end
14
+
15
+ def to_s
16
+ @operator.to_s
17
+ end
18
+ end