rast 0.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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