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