brutal 1.6.0.beta1 → 1.6.0.beta2
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 +24 -19
- data/bin/brutal +9 -5
- data/lib/brutal/command_line_arguments_parser.rb +52 -38
- data/lib/brutal/format/ruby/filename.rb +15 -0
- data/lib/brutal/format/ruby.rb +75 -25
- data/lib/brutal/format.rb +4 -9
- data/lib/brutal/manifest/file/name.rb +18 -0
- data/lib/brutal/manifest/file.rb +24 -0
- data/lib/brutal/manifest.rb +107 -0
- data/lib/brutal.rb +17 -27
- metadata +6 -21
- data/lib/brutal/configuration.rb +0 -55
- data/lib/brutal/file/read.rb +0 -28
- data/lib/brutal/file/write.rb +0 -31
- data/lib/brutal/file.rb +0 -17
- data/lib/brutal/yaml.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a8a912485598e917360c280db92eee2b3acc53180dd0587b2147ea0eb26e649
|
4
|
+
data.tar.gz: 5a7a1f13b748e8deb7d5a1529ca2e11eb35b687aabab84f7b9fc9ebc58d0b174
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec1ffa0f11a17812ff0000bc7e8b4ba8a04c826e37f9ef5e777681312fcb87962020f034b4802b617471894019f9c046598a012eeb6491c6548f0fef233af66d
|
7
|
+
data.tar.gz: 363647b4219053d2433fa198aac832b8f8f630e36d7b9282f0c36c34c2a255e5874ee91e481440be6bea2c87bb7763225ddbd04bfbf0204ad949bea98b5fbb01
|
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", ">= 1.6.0.
|
41
|
+
gem "brutal", ">= 1.6.0.beta2", require: false
|
42
42
|
```
|
43
43
|
|
44
44
|
And then execute:
|
@@ -53,7 +53,22 @@ Or install it yourself as:
|
|
53
53
|
gem install brutal --pre
|
54
54
|
```
|
55
55
|
|
56
|
-
##
|
56
|
+
## Usage
|
57
|
+
|
58
|
+
### YAML manifest
|
59
|
+
|
60
|
+
__Brutal__ needs configuration files in YAML format to know how to write tests.
|
61
|
+
Configuration file names are suffixed by `_brutal.yaml` and composed of 7 top-level sections:
|
62
|
+
|
63
|
+
* `header` - Specifies a block of lines to be executed once before all examples.
|
64
|
+
* `before` - Specifies a block of lines to be executed before each example.
|
65
|
+
* `subject` - Specifies the template of the code to be declined across contexts.
|
66
|
+
* `contexts` - Specifies a list of variables to populate the subject's template.
|
67
|
+
* `actuals` - Specifies templates to challenge evaluated subjects & get results.
|
68
|
+
* `after` - Specifies a block of lines to be executed after each example.
|
69
|
+
* `footer` - Specifies a block of lines to be executed once after all examples.
|
70
|
+
|
71
|
+
### Command line
|
57
72
|
|
58
73
|
The `brutal` command comes with several options you can use to customize Brutal's behavior.
|
59
74
|
|
@@ -64,25 +79,13 @@ brutal --help
|
|
64
79
|
```
|
65
80
|
|
66
81
|
```txt
|
67
|
-
Usage:
|
82
|
+
Usage: brutal [options] [files or directories]
|
68
83
|
|
69
84
|
--format=FORMAT Choose "ruby" (default).
|
70
85
|
--help Display this help.
|
71
86
|
--version Display the version.
|
72
87
|
```
|
73
88
|
|
74
|
-
## Usage
|
75
|
-
|
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:
|
78
|
-
|
79
|
-
* `header` - Specifies the code to execute before generating the test suite.
|
80
|
-
* `subject` - Specifies the template of the code to be declined across contexts.
|
81
|
-
* `contexts` - Specifies a list of variables to populate the subject's template.
|
82
|
-
* `actuals` - Specifies templates to challenge evaluated subjects & get results.
|
83
|
-
|
84
|
-
When the configuration file is present, the generation of a test suite can be done with the command:
|
85
|
-
|
86
89
|
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
90
|
|
88
91
|
```sh
|
@@ -101,17 +104,19 @@ or even:
|
|
101
104
|
brutal
|
102
105
|
```
|
103
106
|
|
104
|
-
This would create a `
|
107
|
+
This would create a `test_user.rb` file containing the test suite.
|
105
108
|
|
106
109
|
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:
|
107
110
|
|
108
111
|
```sh
|
109
|
-
brutal spec/
|
112
|
+
brutal spec/
|
110
113
|
```
|
111
114
|
|
115
|
+
This would create one test file per configuration file matching `./spec/**/*_brutal.yaml` in to `./spec/**/test_*.rb`.
|
116
|
+
|
112
117
|
### Some examples
|
113
118
|
|
114
|
-
<https://github.com/fixrb/brutal/
|
119
|
+
<https://github.com/fixrb/brutal/tree/main/examples/>
|
115
120
|
|
116
121
|
## Rake integration example
|
117
122
|
|
@@ -119,7 +124,7 @@ Generated test suite files could be matched as follows:
|
|
119
124
|
|
120
125
|
```ruby
|
121
126
|
Rake::TestTask.new do |t|
|
122
|
-
t.pattern = "
|
127
|
+
t.pattern = "**/test_*.rb"
|
123
128
|
end
|
124
129
|
```
|
125
130
|
|
data/bin/brutal
CHANGED
@@ -2,13 +2,17 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require_relative File.join("..", "lib", "brutal", "command_line_arguments_parser")
|
5
|
-
|
5
|
+
|
6
|
+
formats, pathnames = Brutal::CommandLineArgumentsParser.new(*ARGV).call
|
6
7
|
|
7
8
|
require_relative File.join("..", "lib", "brutal")
|
8
|
-
generator = Brutal.new(format: format)
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
formats.each do |format|
|
11
|
+
generator = Brutal.new(format)
|
12
|
+
|
13
|
+
pathnames.each do |pathname|
|
14
|
+
Dir.chdir(pathname.dirname) do
|
15
|
+
generator.call(pathname.basename)
|
16
|
+
end
|
13
17
|
end
|
14
18
|
end
|
@@ -1,64 +1,68 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative File.join("manifest", "file", "name")
|
4
|
+
|
3
5
|
require "pathname"
|
4
6
|
|
5
7
|
class Brutal
|
6
8
|
# Accept an arbitrary number of arguments passed from the command-line.
|
7
9
|
class CommandLineArgumentsParser
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
MANIFEST_FILENAME_SUFFIX = Manifest::File::Name::SUFFIX
|
11
|
+
MANIFEST_FILENAME_PATTERN = ::File.join("**", "*#{MANIFEST_FILENAME_SUFFIX}")
|
12
|
+
CURRENT_DIRECTORY_CONTEXT = "."
|
13
|
+
GEM_NAME = "brutal"
|
14
|
+
HELP_OPTION = "--help"
|
15
|
+
VERSION_OPTION = "--version"
|
16
|
+
RUBY_FORMAT_OPTION = "--format=ruby"
|
17
|
+
DEFAULT_FORMAT_OPTION = RUBY_FORMAT_OPTION
|
18
|
+
|
19
|
+
FORMAT_OPTIONS = {
|
20
|
+
RUBY_FORMAT_OPTION => :Ruby
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
DEFAULT_FORMAT = FORMAT_OPTIONS.fetch(DEFAULT_FORMAT_OPTION)
|
24
|
+
DEFAULT_FORMATS = [DEFAULT_FORMAT].freeze
|
12
25
|
|
13
26
|
attr_reader :pathnames
|
14
27
|
|
15
28
|
def initialize(*args)
|
16
|
-
|
29
|
+
args.each do |arg|
|
30
|
+
help! if arg == HELP_OPTION
|
31
|
+
version! if arg == VERSION_OPTION
|
32
|
+
end
|
33
|
+
|
34
|
+
@formats = []
|
17
35
|
@pathnames = []
|
36
|
+
|
37
|
+
args << CURRENT_DIRECTORY_CONTEXT unless any_path_given?(*args)
|
18
38
|
args.each { |arg| parse!(arg) }
|
19
|
-
parse!(CURRENT_EXECUTION_CONTEXT) unless any_path?
|
20
39
|
end
|
21
40
|
|
22
41
|
def call
|
23
|
-
[
|
42
|
+
[formats, pathnames]
|
24
43
|
end
|
25
44
|
|
26
45
|
private
|
27
46
|
|
28
|
-
def
|
29
|
-
@
|
30
|
-
end
|
31
|
-
|
32
|
-
def format
|
33
|
-
@format || DEFAULT_FORMAT
|
47
|
+
def formats
|
48
|
+
@formats.empty? ? DEFAULT_FORMATS : @formats
|
34
49
|
end
|
35
50
|
|
36
51
|
def parse!(arg)
|
37
52
|
case arg
|
38
|
-
when
|
39
|
-
|
40
|
-
when "--help"
|
41
|
-
help!
|
42
|
-
when "--version"
|
43
|
-
version!
|
53
|
+
when RUBY_FORMAT_OPTION
|
54
|
+
@formats << FORMAT_OPTIONS.fetch(RUBY_FORMAT_OPTION)
|
44
55
|
else
|
45
|
-
|
46
|
-
load!(pathname)
|
56
|
+
load!(::Pathname.new(arg))
|
47
57
|
end
|
48
58
|
end
|
49
59
|
|
50
|
-
def format!(name)
|
51
|
-
raise ::ArgumentError, "Format already filled in." unless format.nil?
|
52
|
-
|
53
|
-
@format = name
|
54
|
-
end
|
55
|
-
|
56
60
|
def help!
|
57
|
-
puts
|
61
|
+
puts help_message
|
58
62
|
exit
|
59
63
|
end
|
60
64
|
|
61
|
-
def
|
65
|
+
def help_message
|
62
66
|
<<~TXT
|
63
67
|
Usage: #{$PROGRAM_NAME} [options] [files or directories]
|
64
68
|
|
@@ -69,21 +73,31 @@ class Brutal
|
|
69
73
|
end
|
70
74
|
|
71
75
|
def load!(pathname)
|
72
|
-
@any_path = true
|
73
|
-
|
74
76
|
if pathname.directory?
|
75
|
-
pathname.glob(
|
77
|
+
pathname.glob(MANIFEST_FILENAME_PATTERN).each { |filename| load!(filename) }
|
76
78
|
elsif pathname.file?
|
77
|
-
|
78
|
-
@pathnames << pathname
|
79
|
-
else
|
80
|
-
warn "Skipping #{pathname} because not suffixed with #{FILE_SUFFIX}."
|
81
|
-
end
|
79
|
+
load_file!(pathname)
|
82
80
|
else
|
83
81
|
raise ::ArgumentError, "#{pathname} is neither a file nor a directory."
|
84
82
|
end
|
85
83
|
end
|
86
84
|
|
85
|
+
def load_file!(pathname)
|
86
|
+
if pathname.fnmatch?(MANIFEST_FILENAME_PATTERN)
|
87
|
+
@pathnames << pathname
|
88
|
+
else
|
89
|
+
warn "Skipping #{pathname} because not matched against #{MANIFEST_FILENAME_PATTERN}."
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def any_path_given?(*args)
|
94
|
+
path_args(*args).any?
|
95
|
+
end
|
96
|
+
|
97
|
+
def path_args(*args)
|
98
|
+
args - FORMAT_OPTIONS.keys
|
99
|
+
end
|
100
|
+
|
87
101
|
def version!
|
88
102
|
abort "Gem not found on the system. Unknown version." if not_loaded_spec?
|
89
103
|
|
@@ -92,7 +106,7 @@ class Brutal
|
|
92
106
|
end
|
93
107
|
|
94
108
|
def loaded_spec
|
95
|
-
::Gem.loaded_specs[
|
109
|
+
::Gem.loaded_specs[GEM_NAME]
|
96
110
|
end
|
97
111
|
|
98
112
|
def not_loaded_spec?
|
data/lib/brutal/format/ruby.rb
CHANGED
@@ -1,33 +1,59 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative File.join("ruby", "filename")
|
4
|
+
|
3
5
|
class Brutal
|
4
6
|
module Format
|
5
7
|
# Ruby format class
|
6
8
|
class Ruby
|
9
|
+
# Whitespace character.
|
10
|
+
SPACE = " "
|
11
|
+
|
12
|
+
# Hyphen-minus character.
|
13
|
+
HYPHEN_MINUS = "-"
|
14
|
+
|
15
|
+
# Two spaces per indentation level.
|
16
|
+
INDENTATION = SPACE * 2
|
17
|
+
|
7
18
|
# Specifies templates to challenge evaluated subjects & get results.
|
8
19
|
attr_reader :actuals
|
9
20
|
|
10
21
|
# Specifies a list of variables to populate the subject's template.
|
11
22
|
attr_reader :contexts
|
12
23
|
|
13
|
-
# Specifies
|
24
|
+
# Specifies a block of lines to be executed once before all examples.
|
14
25
|
attr_reader :header
|
15
26
|
|
27
|
+
# Specifies a block of lines to be executed before each example.
|
28
|
+
attr_reader :before
|
29
|
+
|
16
30
|
# Specifies the template of the code to be declined across contexts.
|
17
31
|
attr_reader :subject
|
18
32
|
|
33
|
+
# Specifies a block of lines to be executed after each example.
|
34
|
+
attr_reader :after
|
35
|
+
|
36
|
+
# Specifies a block of lines to be executed once after all examples.
|
37
|
+
attr_reader :footer
|
38
|
+
|
19
39
|
# Initialize a new scaffold generator.
|
20
|
-
def initialize(header, subject, *actuals, **contexts)
|
21
|
-
|
22
|
-
|
23
|
-
|
40
|
+
def initialize(header, before, subject, after, footer, *actuals, **contexts)
|
41
|
+
header = "# Brutal test suite" if header.empty?
|
42
|
+
before = "# Starting an example" if before.empty?
|
43
|
+
after = "# Finishing an example" if after.empty?
|
44
|
+
footer = "# End of the brutal test" if footer.empty?
|
24
45
|
|
25
|
-
|
46
|
+
warn("Empty subject!") if subject.empty?
|
47
|
+
warn("Empty actual values!") if actuals.empty?
|
48
|
+
warn("Empty contexts!") if contexts.empty?
|
26
49
|
|
27
50
|
@header = header
|
51
|
+
@before = before
|
28
52
|
@subject = subject
|
29
53
|
@actuals = actuals
|
30
54
|
@contexts = contexts
|
55
|
+
@after = after
|
56
|
+
@footer = footer
|
31
57
|
end
|
32
58
|
|
33
59
|
# Return a Ruby string that can be evaluated.
|
@@ -41,7 +67,11 @@ class Brutal
|
|
41
67
|
#
|
42
68
|
# @return [String]
|
43
69
|
def to_s
|
44
|
-
|
70
|
+
eval(header) # rubocop:disable Security/Eval
|
71
|
+
|
72
|
+
ruby_lines.compact.join(separator_code)
|
73
|
+
ensure
|
74
|
+
eval(footer) # rubocop:disable Security/Eval
|
45
75
|
end
|
46
76
|
|
47
77
|
def attributes(*values)
|
@@ -63,13 +93,13 @@ class Brutal
|
|
63
93
|
end
|
64
94
|
|
65
95
|
def ruby_lines
|
66
|
-
[
|
96
|
+
[header_code] + actual_codes + [footer_code]
|
67
97
|
end
|
68
98
|
|
69
|
-
def
|
99
|
+
def actual_codes
|
70
100
|
combinations_values.map do |values|
|
71
101
|
actual_str = format(inspect(subject), **attributes(*values))
|
72
|
-
string =
|
102
|
+
string = actual_code(actual_str)
|
73
103
|
actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
|
74
104
|
|
75
105
|
actuals.each do |actual_value|
|
@@ -81,32 +111,52 @@ class Brutal
|
|
81
111
|
end
|
82
112
|
end
|
83
113
|
|
84
|
-
def
|
85
|
-
<<~
|
114
|
+
def actual_code(actual_str)
|
115
|
+
<<~CODE
|
116
|
+
#{before_code}
|
86
117
|
actual = begin
|
87
|
-
#{actual_str.gsub(/^/,
|
118
|
+
#{actual_str.gsub(/^/, INDENTATION)}
|
88
119
|
end
|
89
120
|
|
90
|
-
|
121
|
+
#{after_code}
|
122
|
+
CODE
|
91
123
|
end
|
92
124
|
|
93
|
-
def
|
94
|
-
<<~
|
125
|
+
def header_code
|
126
|
+
<<~CODE
|
95
127
|
#{header.chomp}
|
96
|
-
|
128
|
+
CODE
|
129
|
+
end
|
130
|
+
|
131
|
+
def before_code
|
132
|
+
<<~CODE
|
133
|
+
#{before.chomp}
|
134
|
+
CODE
|
135
|
+
end
|
136
|
+
|
137
|
+
def after_code
|
138
|
+
<<~CODE
|
139
|
+
#{after.chomp}
|
140
|
+
CODE
|
141
|
+
end
|
142
|
+
|
143
|
+
def footer_code
|
144
|
+
<<~CODE
|
145
|
+
#{footer.chomp}
|
146
|
+
CODE
|
97
147
|
end
|
98
148
|
|
99
|
-
def
|
100
|
-
<<~
|
149
|
+
def separator_code
|
150
|
+
<<~CODE
|
101
151
|
|
102
|
-
#{
|
103
|
-
|
152
|
+
#{thematic_break_code}
|
153
|
+
CODE
|
104
154
|
end
|
105
155
|
|
106
|
-
def
|
107
|
-
<<~
|
108
|
-
# #{
|
109
|
-
|
156
|
+
def thematic_break_code
|
157
|
+
<<~CODE
|
158
|
+
# #{HYPHEN_MINUS * 78}
|
159
|
+
CODE
|
110
160
|
end
|
111
161
|
end
|
112
162
|
end
|
data/lib/brutal/format.rb
CHANGED
@@ -1,16 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
Dir[File.join(File.dirname(__FILE__), "format", "*.rb")].sort.each do |fname|
|
4
|
+
require_relative fname
|
5
|
+
end
|
6
6
|
|
7
7
|
class Brutal
|
8
|
-
#
|
8
|
+
# A collection of formatter classes.
|
9
9
|
module Format
|
10
|
-
SUPPORT = {
|
11
|
-
"ruby" => Ruby
|
12
|
-
}.freeze
|
13
|
-
|
14
|
-
DEFAULT = SUPPORT.keys.sort.fetch(-1)
|
15
10
|
end
|
16
11
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Brutal
|
4
|
+
class Manifest
|
5
|
+
class File
|
6
|
+
module Name
|
7
|
+
# Suffix for configuration file names.
|
8
|
+
SUFFIX = "_brutal.yaml"
|
9
|
+
|
10
|
+
# Suffix pattern for configuration file names.
|
11
|
+
SUFFIX_PATTERN = "*#{SUFFIX}"
|
12
|
+
|
13
|
+
# Suffix regex for configuration file names.
|
14
|
+
SUFFIX_REGEX = /#{SUFFIX}\z/.freeze
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative File.join("file", "name")
|
4
|
+
|
5
|
+
require "yaml"
|
6
|
+
|
7
|
+
class Brutal
|
8
|
+
class Manifest
|
9
|
+
# YAML manifest file parser.
|
10
|
+
class File
|
11
|
+
attr_reader :yaml
|
12
|
+
|
13
|
+
def initialize(pathname)
|
14
|
+
raise ::ArgumentError unless pathname.fnmatch?(Name::SUFFIX_PATTERN)
|
15
|
+
|
16
|
+
@yaml = pathname.read
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse
|
20
|
+
::YAML.safe_load(yaml, symbolize_names: false)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative File.join("manifest", "file")
|
4
|
+
|
5
|
+
class Brutal
|
6
|
+
# Brutal YAML manifest file parser.
|
7
|
+
class Manifest
|
8
|
+
# The _actuals_ top-level section key.
|
9
|
+
ACTUALS_KEY = "actuals"
|
10
|
+
|
11
|
+
# The _contexts_ top-level section key.
|
12
|
+
CONTEXTS_KEY = "contexts"
|
13
|
+
|
14
|
+
# The _header_ top-level section key.
|
15
|
+
HEADER_KEY = "header"
|
16
|
+
|
17
|
+
# The _before_ top-level section key.
|
18
|
+
BEFORE_KEY = "before"
|
19
|
+
|
20
|
+
# The _subject_ top-level section key.
|
21
|
+
SUBJECT_KEY = "subject"
|
22
|
+
|
23
|
+
# The _after_ top-level section key.
|
24
|
+
AFTER_KEY = "after"
|
25
|
+
|
26
|
+
# The _footer_ top-level section key.
|
27
|
+
FOOTER_KEY = "footer"
|
28
|
+
|
29
|
+
# Default _actuals_ collection.
|
30
|
+
DEFAULT_ACTUALS = [].freeze
|
31
|
+
|
32
|
+
# Default _contexts_ collection.
|
33
|
+
DEFAULT_CONTEXTS = {}.freeze
|
34
|
+
|
35
|
+
# Default _header_ code to evaluate.
|
36
|
+
DEFAULT_HEADER = ""
|
37
|
+
|
38
|
+
# Default _before_ code to evaluate.
|
39
|
+
DEFAULT_BEFORE = ""
|
40
|
+
|
41
|
+
# Default _after_ code to evaluate.
|
42
|
+
DEFAULT_AFTER = ""
|
43
|
+
|
44
|
+
# Default _footer_ code to evaluate.
|
45
|
+
DEFAULT_FOOTER = ""
|
46
|
+
|
47
|
+
# Parse a file at `pathname`. Returns the YAML manifest instance.
|
48
|
+
def self.parse_file(pathname)
|
49
|
+
load(File.new(pathname).parse)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Load the configuration parameters.
|
53
|
+
#
|
54
|
+
# @param params [Hash] Receive the 7 top-level section parameters.
|
55
|
+
def self.load(params)
|
56
|
+
new(
|
57
|
+
actuals: params.fetch(ACTUALS_KEY, DEFAULT_ACTUALS),
|
58
|
+
contexts: params.fetch(CONTEXTS_KEY, DEFAULT_CONTEXTS),
|
59
|
+
header: params.fetch(HEADER_KEY, DEFAULT_HEADER),
|
60
|
+
before: params.fetch(BEFORE_KEY, DEFAULT_BEFORE),
|
61
|
+
subject: params.fetch(SUBJECT_KEY),
|
62
|
+
after: params.fetch(AFTER_KEY, DEFAULT_AFTER),
|
63
|
+
footer: params.fetch(FOOTER_KEY, DEFAULT_FOOTER)
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Specifies templates to challenge evaluated subjects & get results.
|
68
|
+
attr_reader :actuals
|
69
|
+
|
70
|
+
# Specifies a list of variables to populate the subject's template.
|
71
|
+
attr_reader :contexts
|
72
|
+
|
73
|
+
# Specifies a block of lines to be executed once before all examples.
|
74
|
+
attr_reader :header
|
75
|
+
|
76
|
+
# Specifies a block of lines to be executed before each example.
|
77
|
+
attr_reader :before
|
78
|
+
|
79
|
+
# Specifies the template of the code to be declined across contexts.
|
80
|
+
attr_reader :subject
|
81
|
+
|
82
|
+
# Specifies a block of lines to be executed after each example.
|
83
|
+
attr_reader :after
|
84
|
+
|
85
|
+
# Specifies a block of lines to be executed once after all examples.
|
86
|
+
attr_reader :footer
|
87
|
+
|
88
|
+
# Initialize a new configuration.
|
89
|
+
def initialize(actuals:, contexts:, header:, before:, subject:, after:, footer:)
|
90
|
+
raise ::TypeError, actuals.inspect unless actuals.is_a?(::Array)
|
91
|
+
raise ::TypeError, contexts.inspect unless contexts.is_a?(::Hash)
|
92
|
+
raise ::TypeError, header.inspect unless header.is_a?(::String)
|
93
|
+
raise ::TypeError, before.inspect unless before.is_a?(::String)
|
94
|
+
raise ::TypeError, subject.inspect unless subject.is_a?(::String)
|
95
|
+
raise ::TypeError, after.inspect unless after.is_a?(::String)
|
96
|
+
raise ::TypeError, footer.inspect unless footer.is_a?(::String)
|
97
|
+
|
98
|
+
@actuals = actuals
|
99
|
+
@contexts = contexts
|
100
|
+
@header = header
|
101
|
+
@before = before
|
102
|
+
@subject = subject
|
103
|
+
@after = after
|
104
|
+
@footer = footer
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/brutal.rb
CHANGED
@@ -1,50 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
%w[
|
4
|
-
configuration
|
5
|
-
file
|
6
4
|
format
|
7
|
-
|
5
|
+
manifest
|
8
6
|
].each { |filename| require_relative(File.join("brutal", filename)) }
|
9
7
|
|
10
8
|
# The Brutal namespace.
|
11
9
|
class Brutal
|
12
|
-
attr_reader :
|
10
|
+
attr_reader :engine
|
13
11
|
|
14
|
-
def initialize(format
|
15
|
-
@
|
12
|
+
def initialize(format)
|
13
|
+
@engine = Format.const_get(format)
|
16
14
|
end
|
17
15
|
|
18
16
|
def call(pathname)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
write(
|
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)
|
17
|
+
manifest = Manifest.parse_file(pathname)
|
18
|
+
scaffold = brutalizer(manifest)
|
19
|
+
pathname = new_pathname(pathname)
|
20
|
+
pathname.write(scaffold)
|
31
21
|
end
|
32
22
|
|
33
23
|
private
|
34
24
|
|
35
|
-
def
|
36
|
-
|
25
|
+
def brutalizer(conf)
|
26
|
+
engine.new(conf.header, conf.before, conf.subject, conf.after, conf.footer, *conf.actuals, **conf.contexts)
|
27
|
+
end
|
37
28
|
|
38
|
-
|
39
|
-
|
29
|
+
def new_pathname(pathname)
|
30
|
+
pathname.dirname + "#{new_prefix}#{pathname.basename.sub(Manifest::File::Name::SUFFIX_REGEX, new_suffix)}"
|
40
31
|
end
|
41
32
|
|
42
|
-
def
|
43
|
-
|
33
|
+
def new_prefix
|
34
|
+
engine.const_get(:Filename).const_get(:PREFIX)
|
44
35
|
end
|
45
36
|
|
46
|
-
def
|
47
|
-
|
48
|
-
File::Write.new(new_pathname).call(ruby)
|
37
|
+
def new_suffix
|
38
|
+
engine.const_get(:Filename).const_get(:SUFFIX)
|
49
39
|
end
|
50
40
|
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.6.0.
|
4
|
+
version: 1.6.0.beta2
|
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-09-
|
11
|
+
date: 2022-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -108,20 +108,6 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: simplecov
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
111
|
- !ruby/object:Gem::Dependency
|
126
112
|
name: yard
|
127
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -148,13 +134,12 @@ files:
|
|
148
134
|
- bin/brutal
|
149
135
|
- lib/brutal.rb
|
150
136
|
- lib/brutal/command_line_arguments_parser.rb
|
151
|
-
- lib/brutal/configuration.rb
|
152
|
-
- lib/brutal/file.rb
|
153
|
-
- lib/brutal/file/read.rb
|
154
|
-
- lib/brutal/file/write.rb
|
155
137
|
- lib/brutal/format.rb
|
156
138
|
- lib/brutal/format/ruby.rb
|
157
|
-
- lib/brutal/
|
139
|
+
- lib/brutal/format/ruby/filename.rb
|
140
|
+
- lib/brutal/manifest.rb
|
141
|
+
- lib/brutal/manifest/file.rb
|
142
|
+
- lib/brutal/manifest/file/name.rb
|
158
143
|
homepage: https://github.com/fixrb/brutal
|
159
144
|
licenses:
|
160
145
|
- MIT
|
data/lib/brutal/configuration.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Brutal
|
4
|
-
# Brutal::Configuration
|
5
|
-
#
|
6
|
-
# @since 1.0.0
|
7
|
-
class Configuration
|
8
|
-
ACTUALS_KEY = "actuals"
|
9
|
-
CONTEXTS_KEY = "contexts"
|
10
|
-
HEADER_KEY = "header"
|
11
|
-
SUBJECT_KEY = "subject"
|
12
|
-
|
13
|
-
DEFAULT_ACTUALS = [].freeze
|
14
|
-
DEFAULT_CONTEXTS = {}.freeze
|
15
|
-
DEFAULT_HEADER = "# Brutal test suite"
|
16
|
-
DEFAULT_SUBJECT = ""
|
17
|
-
|
18
|
-
# Load the configuration parameters.
|
19
|
-
#
|
20
|
-
# @param params [Hash] Receive the 4 top-level section parameters.
|
21
|
-
def self.load(params)
|
22
|
-
new(
|
23
|
-
actuals: params.fetch(ACTUALS_KEY, DEFAULT_ACTUALS),
|
24
|
-
contexts: params.fetch(CONTEXTS_KEY, DEFAULT_CONTEXTS),
|
25
|
-
header: params.fetch(HEADER_KEY, DEFAULT_HEADER),
|
26
|
-
subject: params.fetch(SUBJECT_KEY, DEFAULT_SUBJECT)
|
27
|
-
)
|
28
|
-
end
|
29
|
-
|
30
|
-
# Specifies templates to challenge evaluated subjects & get results.
|
31
|
-
attr_reader :actuals
|
32
|
-
|
33
|
-
# Specifies a list of variables to populate the subject's template.
|
34
|
-
attr_reader :contexts
|
35
|
-
|
36
|
-
# Specifies the code to execute before generating the test suite.
|
37
|
-
attr_reader :header
|
38
|
-
|
39
|
-
# Specifies the template of the code to be declined across contexts.
|
40
|
-
attr_reader :subject
|
41
|
-
|
42
|
-
# Initialize a new configuration.
|
43
|
-
def initialize(actuals:, contexts:, header:, subject:)
|
44
|
-
raise ::TypeError, actuals.inspect unless actuals.is_a?(::Array)
|
45
|
-
raise ::TypeError, contexts.inspect unless contexts.is_a?(::Hash)
|
46
|
-
raise ::TypeError, header.inspect unless header.is_a?(::String)
|
47
|
-
raise ::TypeError, subject.inspect unless subject.is_a?(::String)
|
48
|
-
|
49
|
-
@actuals = actuals.sort
|
50
|
-
@contexts = contexts
|
51
|
-
@header = header
|
52
|
-
@subject = subject
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
data/lib/brutal/file/read.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Brutal
|
4
|
-
module File
|
5
|
-
# Brutal::File::Read
|
6
|
-
#
|
7
|
-
# @since 1.1.0
|
8
|
-
class Read
|
9
|
-
attr_reader :name
|
10
|
-
|
11
|
-
def initialize(name)
|
12
|
-
@name = name
|
13
|
-
end
|
14
|
-
|
15
|
-
def call
|
16
|
-
::File.read(path)
|
17
|
-
rescue ::Errno::ENOENT => _e
|
18
|
-
abort "File #{path} not found!"
|
19
|
-
end
|
20
|
-
|
21
|
-
protected
|
22
|
-
|
23
|
-
def path
|
24
|
-
::File.join(::Dir.pwd, name)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
data/lib/brutal/file/write.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Brutal
|
4
|
-
module File
|
5
|
-
# Brutal::File::Write
|
6
|
-
#
|
7
|
-
# @since 1.1.0
|
8
|
-
class Write
|
9
|
-
attr_reader :name
|
10
|
-
|
11
|
-
def initialize(name)
|
12
|
-
@name = name
|
13
|
-
end
|
14
|
-
|
15
|
-
def call(scaffold)
|
16
|
-
file = ::File.open(path, "w")
|
17
|
-
file.write(scaffold)
|
18
|
-
|
19
|
-
true
|
20
|
-
ensure
|
21
|
-
file.close
|
22
|
-
end
|
23
|
-
|
24
|
-
protected
|
25
|
-
|
26
|
-
def path
|
27
|
-
::File.join(::Dir.pwd, name)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
data/lib/brutal/file.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
%w[
|
4
|
-
read
|
5
|
-
write
|
6
|
-
].each { |filename| require_relative(File.join("file", filename)) }
|
7
|
-
|
8
|
-
class Brutal
|
9
|
-
# Brutal::File
|
10
|
-
module File
|
11
|
-
RUBY_EXTENSION = ".rb"
|
12
|
-
|
13
|
-
def self.generated_pathname(pathname)
|
14
|
-
pathname.sub_ext(RUBY_EXTENSION)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/brutal/yaml.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "yaml"
|
4
|
-
|
5
|
-
class Brutal
|
6
|
-
# Brutal::Yaml
|
7
|
-
module Yaml
|
8
|
-
FILENAME_EXTENSION = ".yaml"
|
9
|
-
|
10
|
-
def self.parse(yaml)
|
11
|
-
::YAML.safe_load(yaml, symbolize_names: false)
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.parse?(pathname)
|
15
|
-
filename_extension = pathname.extname
|
16
|
-
filename_extension.eql?(FILENAME_EXTENSION)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|