brutal 1.3.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 +40 -52
- data/bin/brutal +9 -1
- data/lib/brutal/command_line_arguments_parser.rb +102 -0
- data/lib/brutal/configuration.rb +1 -1
- data/lib/brutal/file/read.rb +2 -2
- data/lib/brutal/file/write.rb +1 -1
- data/lib/brutal/file.rb +4 -7
- data/lib/brutal/format/ruby.rb +113 -0
- data/lib/brutal/format.rb +16 -0
- data/lib/brutal/yaml.rb +5 -10
- data/lib/brutal.rb +33 -16
- metadata +7 -5
- data/lib/brutal/scaffold.rb +0 -88
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,91 +50,79 @@ 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
|
-
|
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:
|
64
78
|
|
65
79
|
* `header` - Specifies the code to execute before generating the test suite.
|
66
80
|
* `subject` - Specifies the template of the code to be declined across contexts.
|
67
81
|
* `contexts` - Specifies a list of variables to populate the subject's template.
|
68
82
|
* `actuals` - Specifies templates to challenge evaluated subjects & get results.
|
69
83
|
|
70
|
-
|
71
|
-
|
72
|
-
Currently, only the YAML format is supported.
|
73
|
-
|
74
|
-
### Getting started
|
84
|
+
When the configuration file is present, the generation of a test suite can be done with the command:
|
75
85
|
|
76
|
-
|
77
|
-
The following example `.brutal.yml` defines the shape of a Hello test suite:
|
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:
|
78
87
|
|
79
|
-
```
|
80
|
-
|
81
|
-
subject: |
|
82
|
-
"Hello " + "%{string}"
|
83
|
-
|
84
|
-
contexts:
|
85
|
-
string:
|
86
|
-
- Alice
|
87
|
-
- Bob
|
88
|
-
|
89
|
-
actuals:
|
90
|
-
- "%{subject}.to_s"
|
91
|
-
- "%{subject}.length"
|
88
|
+
```sh
|
89
|
+
brutal user_brutal.yaml
|
92
90
|
```
|
93
91
|
|
94
|
-
|
92
|
+
or:
|
95
93
|
|
96
|
-
|
97
|
-
|
98
|
-
```
|
99
|
-
# Brutal test suite
|
100
|
-
|
101
|
-
# ------------------------------------------------------------------------------
|
94
|
+
```sh
|
95
|
+
brutal .
|
96
|
+
```
|
102
97
|
|
103
|
-
|
104
|
-
"Hello " + "Alice"
|
105
|
-
end
|
98
|
+
or even:
|
106
99
|
|
107
|
-
|
108
|
-
|
100
|
+
```sh
|
101
|
+
brutal
|
102
|
+
```
|
109
103
|
|
110
|
-
|
104
|
+
This would create a `user_brutal.rb` file containing the test suite.
|
111
105
|
|
112
|
-
|
113
|
-
"Hello " + "Bob"
|
114
|
-
end
|
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:
|
115
107
|
|
116
|
-
|
117
|
-
|
108
|
+
```sh
|
109
|
+
brutal spec/ # => generate tests from each configuration file matching ./spec/**/*_brutal.yaml in to ./spec/**/*_brutal.rb
|
118
110
|
```
|
119
111
|
|
120
|
-
###
|
112
|
+
### Some examples
|
121
113
|
|
122
|
-
https://github.com/fixrb/brutal/
|
114
|
+
<https://github.com/fixrb/brutal/blob/v1.6.0.beta1/examples/>
|
123
115
|
|
124
116
|
## Rake integration example
|
125
117
|
|
126
|
-
|
118
|
+
Generated test suite files could be matched as follows:
|
127
119
|
|
128
120
|
```ruby
|
129
121
|
Rake::TestTask.new do |t|
|
130
|
-
t.pattern = "
|
122
|
+
t.pattern = "**/*_brutal.rb"
|
131
123
|
end
|
132
124
|
```
|
133
125
|
|
134
|
-
## Test suite
|
135
|
-
|
136
|
-
__Brutal__'s test set is brutally self-generated here: [./test.rb](https://github.com/fixrb/brutal/blob/main/test.rb)
|
137
|
-
|
138
126
|
## Contact
|
139
127
|
|
140
128
|
* Source code: https://github.com/fixrb/brutal
|
data/bin/brutal
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require_relative File.join("..", "lib", "brutal", "command_line_arguments_parser")
|
5
|
+
format, pathnames = Brutal::CommandLineArgumentsParser.new(*ARGV).call
|
6
|
+
|
4
7
|
require_relative File.join("..", "lib", "brutal")
|
8
|
+
generator = Brutal.new(format: format)
|
5
9
|
|
6
|
-
|
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
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
class Brutal
|
4
4
|
module File
|
5
5
|
# Brutal::File::Read
|
6
6
|
#
|
@@ -15,7 +15,7 @@ module Brutal
|
|
15
15
|
def call
|
16
16
|
::File.read(path)
|
17
17
|
rescue ::Errno::ENOENT => _e
|
18
|
-
abort
|
18
|
+
abort "File #{path} not found!"
|
19
19
|
end
|
20
20
|
|
21
21
|
protected
|
data/lib/brutal/file/write.rb
CHANGED
data/lib/brutal/file.rb
CHANGED
@@ -5,16 +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
|
-
|
12
|
-
DEFAULT_GENERATED_FILENAME = "test.rb"
|
11
|
+
RUBY_EXTENSION = ".rb"
|
13
12
|
|
14
|
-
def self.
|
15
|
-
|
16
|
-
|
17
|
-
filename.gsub(/.[^.]+\z/, ".rb")
|
13
|
+
def self.generated_pathname(pathname)
|
14
|
+
pathname.sub_ext(RUBY_EXTENSION)
|
18
15
|
end
|
19
16
|
end
|
20
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,23 +2,18 @@
|
|
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)
|
17
12
|
end
|
18
13
|
|
19
|
-
def self.parse?(
|
20
|
-
filename_extension =
|
21
|
-
|
14
|
+
def self.parse?(pathname)
|
15
|
+
filename_extension = pathname.extname
|
16
|
+
filename_extension.eql?(FILENAME_EXTENSION)
|
22
17
|
end
|
23
18
|
end
|
24
19
|
end
|
data/lib/brutal.rb
CHANGED
@@ -3,31 +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
|
-
|
13
|
-
file = File::Read.new(filename).call
|
11
|
+
class Brutal
|
12
|
+
attr_reader :format
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
raise ::ArgumentError, "Unrecognized extension. " \
|
19
|
-
"Impossible to parse #{filename.inspect}."
|
20
|
-
end
|
14
|
+
def initialize(format: Format::DEFAULT)
|
15
|
+
@format = String(format)
|
16
|
+
end
|
21
17
|
|
18
|
+
def call(pathname)
|
19
|
+
hash = parse(pathname)
|
22
20
|
conf = Configuration.load(hash)
|
21
|
+
code = scaffold(conf)
|
22
|
+
write(pathname, code)
|
23
|
+
end
|
24
|
+
|
25
|
+
def scaffold(conf)
|
26
|
+
engine = Format::SUPPORT.fetch(format) do
|
27
|
+
raise ::NotImplementedError, "#{format.inspect} format is not supported."
|
28
|
+
end
|
29
|
+
|
30
|
+
engine.new(conf.header, conf.subject, *conf.actuals, **conf.contexts)
|
31
|
+
end
|
23
32
|
|
24
|
-
|
25
|
-
conf.subject,
|
26
|
-
*conf.actuals,
|
27
|
-
**conf.contexts)
|
33
|
+
private
|
28
34
|
|
29
|
-
|
35
|
+
def parse(pathname)
|
36
|
+
return Yaml.parse(read(pathname)) if Yaml.parse?(pathname)
|
37
|
+
|
38
|
+
raise ::ArgumentError, "Unrecognized extension. " \
|
39
|
+
"Impossible to parse #{pathname.inspect}."
|
40
|
+
end
|
41
|
+
|
42
|
+
def read(pathname)
|
43
|
+
File::Read.new(pathname).call
|
44
|
+
end
|
30
45
|
|
31
|
-
|
46
|
+
def write(pathname, ruby)
|
47
|
+
new_pathname = File.generated_pathname(pathname)
|
48
|
+
File::Write.new(new_pathname).call(ruby)
|
32
49
|
end
|
33
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,88 +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
|
-
"#{header.chomp}\n#{blank_line}" + combinations_values.map do |values|
|
46
|
-
attributes = context_names.each_with_index.inject({}) do |h, (name, i)|
|
47
|
-
h.merge(name.to_sym => inspect(values.fetch(i)))
|
48
|
-
end
|
49
|
-
|
50
|
-
actual_str = format(inspect(subject), **attributes)
|
51
|
-
|
52
|
-
string = <<~CODE
|
53
|
-
actual = begin
|
54
|
-
#{actual_str.gsub(/^/, ' ')}
|
55
|
-
end
|
56
|
-
|
57
|
-
CODE
|
58
|
-
|
59
|
-
actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
|
60
|
-
|
61
|
-
actuals.each do |actual_value|
|
62
|
-
result_str = format(actual_value, subject: "actual")
|
63
|
-
string += "raise if #{result_str} != #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
|
64
|
-
end
|
65
|
-
|
66
|
-
string
|
67
|
-
end.join(blank_line)
|
68
|
-
end
|
69
|
-
|
70
|
-
def blank_line
|
71
|
-
"\n" \
|
72
|
-
"# #{'-' * 78}\n" \
|
73
|
-
"\n"
|
74
|
-
end
|
75
|
-
|
76
|
-
def context_names
|
77
|
-
contexts.keys.sort
|
78
|
-
end
|
79
|
-
|
80
|
-
def contexts_values
|
81
|
-
context_names.map { |context_name| contexts.fetch(context_name) }
|
82
|
-
end
|
83
|
-
|
84
|
-
def combinations_values
|
85
|
-
Array(contexts_values[0]).product(*Array(contexts_values[1..]))
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|