brutal 1.5.0 → 1.6.0.beta1
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 +4 -4
- data/README.md +30 -35
- data/bin/brutal +9 -7
- data/lib/brutal/command_line_arguments_parser.rb +102 -0
- data/lib/brutal/configuration.rb +1 -1
- data/lib/brutal/file/read.rb +1 -1
- data/lib/brutal/file/write.rb +1 -1
- data/lib/brutal/file.rb +1 -15
- data/lib/brutal/format/ruby.rb +113 -0
- data/lib/brutal/format.rb +16 -0
- data/lib/brutal/yaml.rb +3 -8
- data/lib/brutal.rb +22 -15
- metadata +7 -5
- data/lib/brutal/scaffold.rb +0 -113
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37fdb51264d859c50519c1bfc0f511542eb8bcca0817049e11191977d06cb7b1
|
4
|
+
data.tar.gz: 54e26e7e3ad8cc403fdb6c1db116186c61d06b0766eb8fa5e96ed7688913872b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d42f48f5e24be39950a9e5a1bfb521a84a37402b8cb3cca1eb680767ce63410ab44b836310ba7792af80a8d9c9f803ba30c9e08dff4f1094fde184409f81c95c
|
7
|
+
data.tar.gz: 40a6cf45ccdc12816d677fd5c8d19ed4c6305ec135c66fc2e56bd11f595291d9c16715b979ff920b1ed36cc90bddfe783db3653fb7f072cbc77ef8a0d760c769
|
data/README.md
CHANGED
@@ -38,7 +38,7 @@ It is therefore the responsibility of the developer to analyze the generated beh
|
|
38
38
|
Add this line to your application's Gemfile:
|
39
39
|
|
40
40
|
```ruby
|
41
|
-
gem "brutal"
|
41
|
+
gem "brutal", ">= 1.6.0.beta1", require: false
|
42
42
|
```
|
43
43
|
|
44
44
|
And then execute:
|
@@ -50,18 +50,31 @@ bundle install
|
|
50
50
|
Or install it yourself as:
|
51
51
|
|
52
52
|
```sh
|
53
|
-
gem install brutal
|
53
|
+
gem install brutal --pre
|
54
54
|
```
|
55
55
|
|
56
|
-
##
|
56
|
+
## Command line
|
57
57
|
|
58
|
-
|
58
|
+
The `brutal` command comes with several options you can use to customize Brutal's behavior.
|
59
|
+
|
60
|
+
For a full list of options, run the `brutal` command with the `--help` flag:
|
61
|
+
|
62
|
+
```sh
|
63
|
+
brutal --help
|
64
|
+
```
|
65
|
+
|
66
|
+
```txt
|
67
|
+
Usage: #{$PROGRAM_NAME} [options] [files or directories]
|
68
|
+
|
69
|
+
--format=FORMAT Choose "ruby" (default).
|
70
|
+
--help Display this help.
|
71
|
+
--version Display the version.
|
72
|
+
```
|
59
73
|
|
60
74
|
## Usage
|
61
75
|
|
62
|
-
__Brutal__ needs
|
63
|
-
|
64
|
-
This file is by default named `.brutal.yml` and is composed of 4 top-level sections:
|
76
|
+
__Brutal__ needs configuration files in YAML format to know how to write tests.
|
77
|
+
Configuration file names are suffixed by `_brutal.yaml` and composed of 4 top-level sections:
|
65
78
|
|
66
79
|
* `header` - Specifies the code to execute before generating the test suite.
|
67
80
|
* `subject` - Specifies the template of the code to be declined across contexts.
|
@@ -70,8 +83,10 @@ This file is by default named `.brutal.yml` and is composed of 4 top-level secti
|
|
70
83
|
|
71
84
|
When the configuration file is present, the generation of a test suite can be done with the command:
|
72
85
|
|
86
|
+
Assuming that in the workspace there is a configuration file named `user_brutal.yaml`, the test suite can be generated via one of these commands:
|
87
|
+
|
73
88
|
```sh
|
74
|
-
brutal .
|
89
|
+
brutal user_brutal.yaml
|
75
90
|
```
|
76
91
|
|
77
92
|
or:
|
@@ -86,48 +101,28 @@ or even:
|
|
86
101
|
brutal
|
87
102
|
```
|
88
103
|
|
89
|
-
This would create a `
|
90
|
-
|
91
|
-
Configuration files can also be named differently:
|
92
|
-
|
93
|
-
```sh
|
94
|
-
brutal path/to/test_hello_world.yml
|
95
|
-
```
|
96
|
-
|
97
|
-
This would create a `path/to/test_hello_world.rb` file containing the test suite.
|
104
|
+
This would create a `user_brutal.rb` file containing the test suite.
|
98
105
|
|
99
|
-
|
106
|
+
Assuming now that in the workspace there are a large number of configuration files named in the `spec/` folder, the complete test suite could be generated recursively via this command:
|
100
107
|
|
101
108
|
```sh
|
102
|
-
brutal
|
109
|
+
brutal spec/ # => generate tests from each configuration file matching ./spec/**/*_brutal.yaml in to ./spec/**/*_brutal.rb
|
103
110
|
```
|
104
111
|
|
105
|
-
|
106
|
-
|
107
|
-
### Getting started
|
112
|
+
### Some examples
|
108
113
|
|
109
|
-
|
110
|
-
2. Run the `brutal` command from the same directory.
|
111
|
-
3. Read the generated `test.rb` file in the same directory: <https://github.com/fixrb/brutal/blob/v1.4.0/examples/hello_world_v1/test.rb>
|
112
|
-
|
113
|
-
### More examples
|
114
|
-
|
115
|
-
<https://github.com/fixrb/brutal/blob/v1.4.0/examples/>
|
114
|
+
<https://github.com/fixrb/brutal/blob/v1.6.0.beta1/examples/>
|
116
115
|
|
117
116
|
## Rake integration example
|
118
117
|
|
119
|
-
|
118
|
+
Generated test suite files could be matched as follows:
|
120
119
|
|
121
120
|
```ruby
|
122
121
|
Rake::TestTask.new do |t|
|
123
|
-
t.pattern = "
|
122
|
+
t.pattern = "**/*_brutal.rb"
|
124
123
|
end
|
125
124
|
```
|
126
125
|
|
127
|
-
## Test suite
|
128
|
-
|
129
|
-
__Brutal__'s test set is brutally self-generated here: [./test.rb](https://github.com/fixrb/brutal/blob/main/test.rb)
|
130
|
-
|
131
126
|
## Contact
|
132
127
|
|
133
128
|
* Source code: https://github.com/fixrb/brutal
|
data/bin/brutal
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
require_relative File.join("..", "lib", "brutal", "command_line_arguments_parser")
|
5
|
+
format, pathnames = Brutal::CommandLineArgumentsParser.new(*ARGV).call
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
pathname += Brutal::File::DEFAULT_CONFIG_FILENAME if pathname.directory?
|
10
|
-
force_opt = ARGV.none?("--no-force")
|
7
|
+
require_relative File.join("..", "lib", "brutal")
|
8
|
+
generator = Brutal.new(format: format)
|
11
9
|
|
12
|
-
|
10
|
+
pathnames.each do |pathname|
|
11
|
+
Dir.chdir(pathname.dirname) do
|
12
|
+
generator.call(pathname.basename)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
|
5
|
+
class Brutal
|
6
|
+
# Accept an arbitrary number of arguments passed from the command-line.
|
7
|
+
class CommandLineArgumentsParser
|
8
|
+
DEFAULT_FORMAT = "ruby"
|
9
|
+
FILE_SUFFIX = "_brutal.yaml"
|
10
|
+
FILE_PATTERN = ::File.join("**", "*#{FILE_SUFFIX}")
|
11
|
+
CURRENT_EXECUTION_CONTEXT = "."
|
12
|
+
|
13
|
+
attr_reader :pathnames
|
14
|
+
|
15
|
+
def initialize(*args)
|
16
|
+
@any_path = false
|
17
|
+
@pathnames = []
|
18
|
+
args.each { |arg| parse!(arg) }
|
19
|
+
parse!(CURRENT_EXECUTION_CONTEXT) unless any_path?
|
20
|
+
end
|
21
|
+
|
22
|
+
def call
|
23
|
+
[format, pathnames]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def any_path?
|
29
|
+
@any_path
|
30
|
+
end
|
31
|
+
|
32
|
+
def format
|
33
|
+
@format || DEFAULT_FORMAT
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse!(arg)
|
37
|
+
case arg
|
38
|
+
when "--format=ruby"
|
39
|
+
format!("Ruby")
|
40
|
+
when "--help"
|
41
|
+
help!
|
42
|
+
when "--version"
|
43
|
+
version!
|
44
|
+
else
|
45
|
+
pathname = ::Pathname.new(arg)
|
46
|
+
load!(pathname)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def format!(name)
|
51
|
+
raise ::ArgumentError, "Format already filled in." unless format.nil?
|
52
|
+
|
53
|
+
@format = name
|
54
|
+
end
|
55
|
+
|
56
|
+
def help!
|
57
|
+
puts help_command_output
|
58
|
+
exit
|
59
|
+
end
|
60
|
+
|
61
|
+
def help_command_output
|
62
|
+
<<~TXT
|
63
|
+
Usage: #{$PROGRAM_NAME} [options] [files or directories]
|
64
|
+
|
65
|
+
--format=FORMAT Choose "ruby" (default).
|
66
|
+
--help Display this help.
|
67
|
+
--version Display the version.
|
68
|
+
TXT
|
69
|
+
end
|
70
|
+
|
71
|
+
def load!(pathname)
|
72
|
+
@any_path = true
|
73
|
+
|
74
|
+
if pathname.directory?
|
75
|
+
pathname.glob(FILE_PATTERN).each { |filename| load!(filename) }
|
76
|
+
elsif pathname.file?
|
77
|
+
if pathname.to_s.end_with?(FILE_SUFFIX)
|
78
|
+
@pathnames << pathname
|
79
|
+
else
|
80
|
+
warn "Skipping #{pathname} because not suffixed with #{FILE_SUFFIX}."
|
81
|
+
end
|
82
|
+
else
|
83
|
+
raise ::ArgumentError, "#{pathname} is neither a file nor a directory."
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def version!
|
88
|
+
abort "Gem not found on the system. Unknown version." if not_loaded_spec?
|
89
|
+
|
90
|
+
puts loaded_spec.version
|
91
|
+
exit
|
92
|
+
end
|
93
|
+
|
94
|
+
def loaded_spec
|
95
|
+
::Gem.loaded_specs["brutal"]
|
96
|
+
end
|
97
|
+
|
98
|
+
def not_loaded_spec?
|
99
|
+
loaded_spec.nil?
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/brutal/configuration.rb
CHANGED
data/lib/brutal/file/read.rb
CHANGED
data/lib/brutal/file/write.rb
CHANGED
data/lib/brutal/file.rb
CHANGED
@@ -5,27 +5,13 @@
|
|
5
5
|
write
|
6
6
|
].each { |filename| require_relative(File.join("file", filename)) }
|
7
7
|
|
8
|
-
|
8
|
+
class Brutal
|
9
9
|
# Brutal::File
|
10
10
|
module File
|
11
|
-
DEFAULT_CONFIG_FILENAME = ".brutal.yml"
|
12
|
-
DEFAULT_GENERATED_FILENAME = "test.rb"
|
13
11
|
RUBY_EXTENSION = ".rb"
|
14
12
|
|
15
13
|
def self.generated_pathname(pathname)
|
16
|
-
filename = pathname.basename
|
17
|
-
return pathname.dirname + DEFAULT_GENERATED_FILENAME if default_config_filename?(filename)
|
18
|
-
|
19
14
|
pathname.sub_ext(RUBY_EXTENSION)
|
20
15
|
end
|
21
|
-
|
22
|
-
def self.override_protection(pathname)
|
23
|
-
abort "A #{pathname} file already exists!" if pathname.exist?
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.default_config_filename?(filename)
|
27
|
-
filename.to_s == DEFAULT_CONFIG_FILENAME
|
28
|
-
end
|
29
|
-
private_class_method :default_config_filename?
|
30
16
|
end
|
31
17
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Brutal
|
4
|
+
module Format
|
5
|
+
# Ruby format class
|
6
|
+
class Ruby
|
7
|
+
# Specifies templates to challenge evaluated subjects & get results.
|
8
|
+
attr_reader :actuals
|
9
|
+
|
10
|
+
# Specifies a list of variables to populate the subject's template.
|
11
|
+
attr_reader :contexts
|
12
|
+
|
13
|
+
# Specifies the code to execute before generating the test suite.
|
14
|
+
attr_reader :header
|
15
|
+
|
16
|
+
# Specifies the template of the code to be declined across contexts.
|
17
|
+
attr_reader :subject
|
18
|
+
|
19
|
+
# Initialize a new scaffold generator.
|
20
|
+
def initialize(header, subject, *actuals, **contexts)
|
21
|
+
warn("Empty subject!") if subject.empty?
|
22
|
+
warn("Empty actual values!") if actuals.empty?
|
23
|
+
warn("Empty contexts!") if contexts.empty?
|
24
|
+
|
25
|
+
eval(header) # rubocop:disable Security/Eval
|
26
|
+
|
27
|
+
@header = header
|
28
|
+
@subject = subject
|
29
|
+
@actuals = actuals
|
30
|
+
@contexts = contexts
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return a Ruby string that can be evaluated.
|
34
|
+
def inspect(object)
|
35
|
+
return object.to_s unless object.is_a?(::String)
|
36
|
+
|
37
|
+
object.strip
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return a string representation.
|
41
|
+
#
|
42
|
+
# @return [String]
|
43
|
+
def to_s
|
44
|
+
ruby_lines.join(separator_ruby_code)
|
45
|
+
end
|
46
|
+
|
47
|
+
def attributes(*values)
|
48
|
+
context_names.each_with_index.inject({}) do |h, (name, i)|
|
49
|
+
h.merge(name.to_sym => inspect(values.fetch(i)))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def context_names
|
54
|
+
contexts.keys.sort
|
55
|
+
end
|
56
|
+
|
57
|
+
def contexts_values
|
58
|
+
context_names.map { |context_name| contexts.fetch(context_name) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def combinations_values
|
62
|
+
Array(contexts_values[0]).product(*Array(contexts_values[1..]))
|
63
|
+
end
|
64
|
+
|
65
|
+
def ruby_lines
|
66
|
+
[header_ruby_code] + actual_ruby_codes
|
67
|
+
end
|
68
|
+
|
69
|
+
def actual_ruby_codes
|
70
|
+
combinations_values.map do |values|
|
71
|
+
actual_str = format(inspect(subject), **attributes(*values))
|
72
|
+
string = actual_ruby_code(actual_str)
|
73
|
+
actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
|
74
|
+
|
75
|
+
actuals.each do |actual_value|
|
76
|
+
result_str = format(actual_value, subject: "actual")
|
77
|
+
string += "raise if #{result_str} != #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
|
78
|
+
end
|
79
|
+
|
80
|
+
string
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def actual_ruby_code(actual_str)
|
85
|
+
<<~RUBY_CODE
|
86
|
+
actual = begin
|
87
|
+
#{actual_str.gsub(/^/, ' ')}
|
88
|
+
end
|
89
|
+
|
90
|
+
RUBY_CODE
|
91
|
+
end
|
92
|
+
|
93
|
+
def header_ruby_code
|
94
|
+
<<~RUBY_CODE
|
95
|
+
#{header.chomp}
|
96
|
+
RUBY_CODE
|
97
|
+
end
|
98
|
+
|
99
|
+
def separator_ruby_code
|
100
|
+
<<~RUBY_CODE
|
101
|
+
|
102
|
+
#{thematic_break_ruby_code}
|
103
|
+
RUBY_CODE
|
104
|
+
end
|
105
|
+
|
106
|
+
def thematic_break_ruby_code
|
107
|
+
<<~RUBY_CODE
|
108
|
+
# #{'-' * 78}
|
109
|
+
RUBY_CODE
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
%w[
|
4
|
+
ruby
|
5
|
+
].each { |filename| require_relative(File.join("format", filename)) }
|
6
|
+
|
7
|
+
class Brutal
|
8
|
+
# Brutal::Format
|
9
|
+
module Format
|
10
|
+
SUPPORT = {
|
11
|
+
"ruby" => Ruby
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
DEFAULT = SUPPORT.keys.sort.fetch(-1)
|
15
|
+
end
|
16
|
+
end
|
data/lib/brutal/yaml.rb
CHANGED
@@ -2,15 +2,10 @@
|
|
2
2
|
|
3
3
|
require "yaml"
|
4
4
|
|
5
|
-
|
5
|
+
class Brutal
|
6
6
|
# Brutal::Yaml
|
7
|
-
#
|
8
|
-
# @since 1.1.0
|
9
7
|
module Yaml
|
10
|
-
|
11
|
-
.yaml
|
12
|
-
.yml
|
13
|
-
].freeze
|
8
|
+
FILENAME_EXTENSION = ".yaml"
|
14
9
|
|
15
10
|
def self.parse(yaml)
|
16
11
|
::YAML.safe_load(yaml, symbolize_names: false)
|
@@ -18,7 +13,7 @@ module Brutal
|
|
18
13
|
|
19
14
|
def self.parse?(pathname)
|
20
15
|
filename_extension = pathname.extname
|
21
|
-
|
16
|
+
filename_extension.eql?(FILENAME_EXTENSION)
|
22
17
|
end
|
23
18
|
end
|
24
19
|
end
|
data/lib/brutal.rb
CHANGED
@@ -3,41 +3,48 @@
|
|
3
3
|
%w[
|
4
4
|
configuration
|
5
5
|
file
|
6
|
-
|
6
|
+
format
|
7
7
|
yaml
|
8
8
|
].each { |filename| require_relative(File.join("brutal", filename)) }
|
9
9
|
|
10
10
|
# The Brutal namespace.
|
11
|
-
|
12
|
-
|
11
|
+
class Brutal
|
12
|
+
attr_reader :format
|
13
|
+
|
14
|
+
def initialize(format: Format::DEFAULT)
|
15
|
+
@format = String(format)
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(pathname)
|
13
19
|
hash = parse(pathname)
|
14
20
|
conf = Configuration.load(hash)
|
21
|
+
code = scaffold(conf)
|
22
|
+
write(pathname, code)
|
23
|
+
end
|
15
24
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
25
|
+
def scaffold(conf)
|
26
|
+
engine = Format::SUPPORT.fetch(format) do
|
27
|
+
raise ::NotImplementedError, "#{format.inspect} format is not supported."
|
28
|
+
end
|
20
29
|
|
21
|
-
|
30
|
+
engine.new(conf.header, conf.subject, *conf.actuals, **conf.contexts)
|
22
31
|
end
|
23
32
|
|
24
|
-
|
33
|
+
private
|
34
|
+
|
35
|
+
def parse(pathname)
|
25
36
|
return Yaml.parse(read(pathname)) if Yaml.parse?(pathname)
|
26
37
|
|
27
38
|
raise ::ArgumentError, "Unrecognized extension. " \
|
28
39
|
"Impossible to parse #{pathname.inspect}."
|
29
40
|
end
|
30
|
-
private_class_method :parse
|
31
41
|
|
32
|
-
def
|
42
|
+
def read(pathname)
|
33
43
|
File::Read.new(pathname).call
|
34
44
|
end
|
35
|
-
private_class_method :read
|
36
45
|
|
37
|
-
def
|
46
|
+
def write(pathname, ruby)
|
38
47
|
new_pathname = File.generated_pathname(pathname)
|
39
|
-
File.override_protection(new_pathname) unless force
|
40
48
|
File::Write.new(new_pathname).call(ruby)
|
41
49
|
end
|
42
|
-
private_class_method :write
|
43
50
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brutal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -147,11 +147,13 @@ files:
|
|
147
147
|
- README.md
|
148
148
|
- bin/brutal
|
149
149
|
- lib/brutal.rb
|
150
|
+
- lib/brutal/command_line_arguments_parser.rb
|
150
151
|
- lib/brutal/configuration.rb
|
151
152
|
- lib/brutal/file.rb
|
152
153
|
- lib/brutal/file/read.rb
|
153
154
|
- lib/brutal/file/write.rb
|
154
|
-
- lib/brutal/
|
155
|
+
- lib/brutal/format.rb
|
156
|
+
- lib/brutal/format/ruby.rb
|
155
157
|
- lib/brutal/yaml.rb
|
156
158
|
homepage: https://github.com/fixrb/brutal
|
157
159
|
licenses:
|
@@ -169,9 +171,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
169
171
|
version: 2.7.0
|
170
172
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
173
|
requirements:
|
172
|
-
- - "
|
174
|
+
- - ">"
|
173
175
|
- !ruby/object:Gem::Version
|
174
|
-
version:
|
176
|
+
version: 1.3.1
|
175
177
|
requirements: []
|
176
178
|
rubygems_version: 3.1.6
|
177
179
|
signing_key:
|
data/lib/brutal/scaffold.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Brutal
|
4
|
-
# Brutal::Scaffold
|
5
|
-
#
|
6
|
-
# @since 1.0.0
|
7
|
-
class Scaffold
|
8
|
-
# Specifies templates to challenge evaluated subjects & get results.
|
9
|
-
attr_reader :actuals
|
10
|
-
|
11
|
-
# Specifies a list of variables to populate the subject's template.
|
12
|
-
attr_reader :contexts
|
13
|
-
|
14
|
-
# Specifies the code to execute before generating the test suite.
|
15
|
-
attr_reader :header
|
16
|
-
|
17
|
-
# Specifies the template of the code to be declined across contexts.
|
18
|
-
attr_reader :subject
|
19
|
-
|
20
|
-
# Initialize a new scaffold generator.
|
21
|
-
def initialize(header, subject, *actuals, **contexts)
|
22
|
-
warn("Empty subject!") if subject.empty?
|
23
|
-
warn("Empty actual values!") if actuals.empty?
|
24
|
-
warn("Empty contexts!") if contexts.empty?
|
25
|
-
|
26
|
-
eval(header) # rubocop:disable Security/Eval
|
27
|
-
|
28
|
-
@header = header
|
29
|
-
@subject = subject
|
30
|
-
@actuals = actuals
|
31
|
-
@contexts = contexts
|
32
|
-
end
|
33
|
-
|
34
|
-
# Return a Ruby string that can be evaluated.
|
35
|
-
def inspect(object)
|
36
|
-
return object.to_s unless object.is_a?(::String)
|
37
|
-
|
38
|
-
object.strip
|
39
|
-
end
|
40
|
-
|
41
|
-
# Return a string representation.
|
42
|
-
#
|
43
|
-
# @return [String]
|
44
|
-
def to_s
|
45
|
-
ruby_lines.join(separator_ruby_code)
|
46
|
-
end
|
47
|
-
|
48
|
-
def attributes(*values)
|
49
|
-
context_names.each_with_index.inject({}) do |h, (name, i)|
|
50
|
-
h.merge(name.to_sym => inspect(values.fetch(i)))
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def context_names
|
55
|
-
contexts.keys.sort
|
56
|
-
end
|
57
|
-
|
58
|
-
def contexts_values
|
59
|
-
context_names.map { |context_name| contexts.fetch(context_name) }
|
60
|
-
end
|
61
|
-
|
62
|
-
def combinations_values
|
63
|
-
Array(contexts_values[0]).product(*Array(contexts_values[1..]))
|
64
|
-
end
|
65
|
-
|
66
|
-
def ruby_lines
|
67
|
-
[header_ruby_code] + actual_ruby_codes
|
68
|
-
end
|
69
|
-
|
70
|
-
def actual_ruby_codes
|
71
|
-
combinations_values.map do |values|
|
72
|
-
actual_str = format(inspect(subject), **attributes(*values))
|
73
|
-
string = actual_ruby_code(actual_str)
|
74
|
-
actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
|
75
|
-
|
76
|
-
actuals.each do |actual_value|
|
77
|
-
result_str = format(actual_value, subject: "actual")
|
78
|
-
string += "raise if #{result_str} != #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
|
79
|
-
end
|
80
|
-
|
81
|
-
string
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def actual_ruby_code(actual_str)
|
86
|
-
<<~RUBY_CODE
|
87
|
-
actual = begin
|
88
|
-
#{actual_str.gsub(/^/, ' ')}
|
89
|
-
end
|
90
|
-
|
91
|
-
RUBY_CODE
|
92
|
-
end
|
93
|
-
|
94
|
-
def header_ruby_code
|
95
|
-
<<~RUBY_CODE
|
96
|
-
#{header.chomp}
|
97
|
-
RUBY_CODE
|
98
|
-
end
|
99
|
-
|
100
|
-
def separator_ruby_code
|
101
|
-
<<~RUBY_CODE
|
102
|
-
|
103
|
-
#{thematic_break_ruby_code}
|
104
|
-
RUBY_CODE
|
105
|
-
end
|
106
|
-
|
107
|
-
def thematic_break_ruby_code
|
108
|
-
<<~RUBY_CODE
|
109
|
-
# #{'-' * 78}
|
110
|
-
RUBY_CODE
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|