brutal 0.2.1 → 0.3.0
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 +49 -44
- data/bin/brutal +21 -16
- data/lib/brutal.rb +1 -1
- data/lib/brutal/scaffold_generator/base.rb +30 -0
- data/lib/brutal/scaffold_generator/por.rb +52 -0
- metadata +7 -7
- data/lib/brutal/framework/base.rb +0 -22
- data/lib/brutal/framework/por.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e7af479b185bf19e8cb7050360c79d216af2916668e76efb0d7c550e553af4b
|
4
|
+
data.tar.gz: e917683e2edd247429cb9776444e6a97c10b9930265459a10ac8a3b4dbcf2cf6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad95d20234be22ed17de738afa0f1f1e0a5518cbf967fe87f9f1932f571e13d683a454354dd792f07d55010033dfd257b888422a880a4bd6c0f04ed19cf284ed
|
7
|
+
data.tar.gz: c4e82e9a5036e134e83f86c313277573926fe0773ef3e2e43145d30fb725ba6a7a196f881f090f083589ac447889b7aa10d1d7e450ec550f468792991bffb824
|
data/README.md
CHANGED
@@ -1,14 +1,23 @@
|
|
1
|
-
# Brutal
|
1
|
+
# Brutal 👹
|
2
2
|
|
3
|
-
>
|
4
|
-
|
5
|
-

|
3
|
+
> I don't like testing because it's redundant. If we are smart enough, we can avoid it. <br/>
|
4
|
+
> -- Matz
|
6
5
|
|
7
6
|
[][travis]
|
8
7
|
[][gem]
|
9
8
|
[][inchpages]
|
10
9
|
[][rubydoc]
|
11
10
|
|
11
|
+
__Brutal__ is a _code-first_ approach that automates the writing of unit tests.
|
12
|
+
|
13
|
+
By being able to generate test cases from the code itself, developers can focus on their creation, save time and energy, for more happiness.
|
14
|
+
|
15
|
+
<p>
|
16
|
+
<img
|
17
|
+
src="https://github.com/fixrb/brutal/raw/master/img/Ferdinand_Hodler_-_Woodcutter_-_Google_Art_Project.jpg"
|
18
|
+
alt="A lumberjack brutally cutting a tree" />
|
19
|
+
</p>
|
20
|
+
|
12
21
|
## Installation
|
13
22
|
|
14
23
|
Add this line to your application's Gemfile:
|
@@ -34,9 +43,10 @@ Just type `brutal` in a Ruby project's folder and watch the magic happen.
|
|
34
43
|
The Brutal YAML file handles 4 keys:
|
35
44
|
|
36
45
|
* `header` (optional): Some code to execute before the test suite.
|
37
|
-
* `
|
38
|
-
* `
|
39
|
-
* `
|
46
|
+
* `front_object` (required): The front object of the test suite.
|
47
|
+
* `subject` (required): The object of each context.
|
48
|
+
* `variables` (required): A hash to generate the subject of each context.
|
49
|
+
* `actual_values` (required): A list of tests to challenge the subject.
|
40
50
|
|
41
51
|
## Example
|
42
52
|
|
@@ -44,61 +54,43 @@ Given this `.brutal.yml` config file:
|
|
44
54
|
|
45
55
|
```yaml
|
46
56
|
---
|
47
|
-
|
48
|
-
|
57
|
+
front_object: |
|
58
|
+
"Hello "
|
49
59
|
|
50
60
|
subject: |
|
51
|
-
|
61
|
+
%{front_object} + %{string}
|
52
62
|
|
53
63
|
variables:
|
54
|
-
:
|
55
|
-
-
|
56
|
-
- "
|
57
|
-
|
58
|
-
:punctuation_mark:
|
59
|
-
- "!"
|
60
|
-
- ...
|
64
|
+
string:
|
65
|
+
- "'Alice'"
|
66
|
+
- "'Bob'"
|
61
67
|
|
62
|
-
|
63
|
-
- "%{
|
64
|
-
- "%{
|
68
|
+
actual_values:
|
69
|
+
- "%{subject}.to_s"
|
70
|
+
- "%{subject}.length"
|
65
71
|
```
|
66
72
|
|
67
|
-
The `brutal` command would
|
73
|
+
The `brutal` command would save this Plain Old Ruby in to a `test.rb` file:
|
68
74
|
|
69
75
|
```ruby
|
70
|
-
|
71
|
-
|
72
|
-
# ------------------------------------------------------------------------------
|
73
|
-
|
74
|
-
actual = "Hello" + "!"
|
75
|
-
|
76
|
-
raise unless actual.to_s == "Hello!"
|
77
|
-
raise unless actual.length == 6
|
78
|
-
|
79
|
-
# ------------------------------------------------------------------------------
|
80
|
-
|
81
|
-
actual = "Hello" + "..."
|
82
|
-
|
83
|
-
raise unless actual.to_s == "Hello..."
|
84
|
-
raise unless actual.length == 8
|
76
|
+
front_object = "Hello "
|
85
77
|
|
86
78
|
# ------------------------------------------------------------------------------
|
87
79
|
|
88
|
-
actual =
|
89
|
-
|
90
|
-
raise unless actual.to_s == "Hello, Bob!"
|
80
|
+
actual = front_object + 'Alice'
|
81
|
+
raise unless actual.to_s == "Hello Alice"
|
91
82
|
raise unless actual.length == 11
|
92
83
|
|
93
84
|
# ------------------------------------------------------------------------------
|
94
85
|
|
95
|
-
actual =
|
96
|
-
|
97
|
-
raise unless actual.
|
98
|
-
raise unless actual.length == 13
|
86
|
+
actual = front_object + 'Bob'
|
87
|
+
raise unless actual.to_s == "Hello Bob"
|
88
|
+
raise unless actual.length == 9
|
99
89
|
```
|
100
90
|
|
101
|
-
|
91
|
+
[More examples](examples/) are available.
|
92
|
+
|
93
|
+
## Rake integration example
|
102
94
|
|
103
95
|
The generated brutal test suite `test.rb` file can be declared as follows:
|
104
96
|
|
@@ -119,6 +111,10 @@ end
|
|
119
111
|
* [Rubinius](https://rubinius.com/)
|
120
112
|
* [JRuby](https://www.jruby.org/)
|
121
113
|
|
114
|
+
## Versioning
|
115
|
+
|
116
|
+
__Brutal__ follows [Semantic Versioning 2.0](https://semver.org/).
|
117
|
+
|
122
118
|
## Contributing
|
123
119
|
|
124
120
|
Bug reports and pull requests are welcome on GitHub at https://github.com/fixrb/brutal. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/fixrb/brutal/blob/master/CODE_OF_CONDUCT.md).
|
@@ -135,3 +131,12 @@ Everyone interacting in the GreatGuardian project's codebases, issue trackers, c
|
|
135
131
|
[travis]: https://travis-ci.org/fixrb/brutal
|
136
132
|
[inchpages]: https://inch-ci.org/github/fixrb/brutal
|
137
133
|
[rubydoc]: https://rubydoc.info/gems/brutal/frames
|
134
|
+
|
135
|
+
***
|
136
|
+
|
137
|
+
<p>
|
138
|
+
This project is sponsored by:<br />
|
139
|
+
<a href="https://sashite.com/"><img
|
140
|
+
src="https://github.com/fixrb/brutal/raw/master/img/sashite.png"
|
141
|
+
alt="Sashite" /></a>
|
142
|
+
</p>
|
data/bin/brutal
CHANGED
@@ -10,29 +10,34 @@ abort "File #{CONF_PATH} not found!" unless ::File.exist?(CONF_PATH)
|
|
10
10
|
|
11
11
|
conf = ::YAML.load_file(CONF_PATH)
|
12
12
|
|
13
|
-
header
|
14
|
-
|
15
|
-
|
16
|
-
variables
|
13
|
+
header = conf.fetch('header', '').to_s.chomp
|
14
|
+
front_object = conf.fetch('front_object')
|
15
|
+
subject = conf.fetch('subject')
|
16
|
+
variables = conf.fetch('variables', {})
|
17
|
+
actual_values = conf.fetch('actual_values')
|
17
18
|
|
18
|
-
raise ::TypeError unless
|
19
|
-
raise ::TypeError unless challenges.is_a?(::Array)
|
19
|
+
raise ::TypeError unless subject.is_a?(::String)
|
20
20
|
raise ::TypeError unless variables.is_a?(::Hash)
|
21
|
+
raise ::TypeError unless actual_values.is_a?(::Array)
|
22
|
+
|
23
|
+
front_object = front_object.chomp if front_object.is_a?(::String)
|
21
24
|
|
22
25
|
require_relative ::File.join('..', 'lib', 'brutal')
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
scaffold = case ARGV.fetch(0, nil)
|
28
|
+
when nil
|
29
|
+
::Brutal::ScaffoldGenerator::Por
|
30
|
+
else
|
31
|
+
abort "#{ARGV[0].inspect} not (yet) supported!"
|
32
|
+
end
|
30
33
|
|
31
|
-
eval(header)
|
34
|
+
eval(header) # rubocop:disable Security/Eval
|
32
35
|
|
33
|
-
|
34
|
-
|
36
|
+
doc = [
|
37
|
+
header,
|
38
|
+
scaffold.new(front_object, subject, *actual_values, **variables).to_s
|
39
|
+
].reject(&:empty?).join("\n")
|
35
40
|
|
36
41
|
file = ::File.open('test.rb', 'w')
|
37
|
-
file.write(
|
42
|
+
file.write(doc)
|
38
43
|
file.close
|
data/lib/brutal.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Brutal
|
4
|
+
module ScaffoldGenerator
|
5
|
+
# Base class
|
6
|
+
class Base
|
7
|
+
# @api private
|
8
|
+
attr_reader :front_object_str, :front_object, :subject, :actual_values, :variables
|
9
|
+
|
10
|
+
# Initialize a new scaffold generator
|
11
|
+
def initialize(front_object, subject, *actual_values, **variables)
|
12
|
+
raise 'Empty actual values!' if actual_values.empty?
|
13
|
+
raise 'Empty variables!' if variables.empty?
|
14
|
+
|
15
|
+
@front_object_str = inspect(front_object)
|
16
|
+
@front_object = eval(@front_object_str) # rubocop:disable Security/Eval
|
17
|
+
@subject = subject
|
18
|
+
@actual_values = actual_values
|
19
|
+
@variables = variables
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return a Ruby string that can be evaluated.
|
23
|
+
def inspect(object)
|
24
|
+
return object.to_s unless object.is_a?(::String)
|
25
|
+
|
26
|
+
object.strip
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module Brutal
|
6
|
+
module ScaffoldGenerator
|
7
|
+
# Plain Old Ruby.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
#
|
11
|
+
class Por < Base
|
12
|
+
# Return a string representation
|
13
|
+
#
|
14
|
+
# @return [String]
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def to_s
|
18
|
+
names = variables.keys.sort
|
19
|
+
values_arr = names.map { |name| variables.fetch(name) }
|
20
|
+
|
21
|
+
test_params = Array(values_arr[0]).product(*Array(values_arr[1..-1]))
|
22
|
+
|
23
|
+
blank_line +
|
24
|
+
"front_object = #{front_object_str}\n" +
|
25
|
+
blank_line +
|
26
|
+
test_params.map do |values|
|
27
|
+
attributes = names.each_with_index.inject(front_object: 'front_object') do |h, (name, i)|
|
28
|
+
h.merge(name.to_sym => inspect(values.fetch(i)))
|
29
|
+
end
|
30
|
+
|
31
|
+
actual_str = format(inspect(subject), **attributes)
|
32
|
+
|
33
|
+
string = "actual = #{actual_str}\n"
|
34
|
+
actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
|
35
|
+
|
36
|
+
actual_values.each do |actual_value|
|
37
|
+
result_str = format(actual_value, subject: 'actual')
|
38
|
+
string += "raise unless #{result_str} == #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
|
39
|
+
end
|
40
|
+
|
41
|
+
string
|
42
|
+
end.join(blank_line)
|
43
|
+
end
|
44
|
+
|
45
|
+
def blank_line
|
46
|
+
"\n" \
|
47
|
+
"# #{('-' * 78)}\n" \
|
48
|
+
"\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
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: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-12-
|
11
|
+
date: 2019-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,7 +94,7 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0.9'
|
97
|
-
description:
|
97
|
+
description: Scaffolds test cases from the code itself.
|
98
98
|
email: contact@cyril.email
|
99
99
|
executables:
|
100
100
|
- brutal
|
@@ -105,8 +105,8 @@ files:
|
|
105
105
|
- README.md
|
106
106
|
- bin/brutal
|
107
107
|
- lib/brutal.rb
|
108
|
-
- lib/brutal/
|
109
|
-
- lib/brutal/
|
108
|
+
- lib/brutal/scaffold_generator/base.rb
|
109
|
+
- lib/brutal/scaffold_generator/por.rb
|
110
110
|
homepage: https://github.com/fixrb/brutal
|
111
111
|
licenses:
|
112
112
|
- MIT
|
@@ -126,8 +126,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
126
|
- !ruby/object:Gem::Version
|
127
127
|
version: '0'
|
128
128
|
requirements: []
|
129
|
-
rubygems_version: 3.
|
129
|
+
rubygems_version: 3.1.2
|
130
130
|
signing_key:
|
131
131
|
specification_version: 4
|
132
|
-
summary:
|
132
|
+
summary: Scaffolds test cases from the code itself.
|
133
133
|
test_files: []
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Brutal
|
2
|
-
module Framework
|
3
|
-
class Base
|
4
|
-
# @api private
|
5
|
-
attr_reader :subject, :challenges, :variables
|
6
|
-
|
7
|
-
# Initialize a new framework
|
8
|
-
def initialize(subject, *challenges, **variables)
|
9
|
-
@subject = subject
|
10
|
-
@challenges = challenges
|
11
|
-
@variables = variables
|
12
|
-
end
|
13
|
-
|
14
|
-
# Return a string representation
|
15
|
-
#
|
16
|
-
# @api private
|
17
|
-
def to_s
|
18
|
-
raise ::NotImplementedError
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
data/lib/brutal/framework/por.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
require_relative 'base'
|
2
|
-
|
3
|
-
module Brutal
|
4
|
-
module Framework
|
5
|
-
# Plain Old Ruby
|
6
|
-
class Por < Base
|
7
|
-
# Return a string representation
|
8
|
-
#
|
9
|
-
# @return [String]
|
10
|
-
#
|
11
|
-
# @api public
|
12
|
-
def to_s
|
13
|
-
names = variables.keys.sort
|
14
|
-
values_arr = names.map { |name| variables.fetch(name) }
|
15
|
-
test_params = Array(values_arr[0]).product(*Array(values_arr[1..-1]))
|
16
|
-
|
17
|
-
test_params.inject('') do |string, values|
|
18
|
-
attributes = names.each_with_index.inject({}) do |h, (name, i)|
|
19
|
-
h.merge(name.to_sym => values.fetch(i))
|
20
|
-
end
|
21
|
-
|
22
|
-
actual_str = subject % attributes
|
23
|
-
|
24
|
-
string += "\n" \
|
25
|
-
"# #{('-' * 78)}\n" \
|
26
|
-
"\n" \
|
27
|
-
"actual = #{actual_str}\n"
|
28
|
-
|
29
|
-
actual = eval(actual_str)
|
30
|
-
|
31
|
-
challenges.each do |challenge|
|
32
|
-
result = challenge % { actual: 'actual' }
|
33
|
-
string += "raise unless #{result} == #{eval(result).inspect}\n"
|
34
|
-
end
|
35
|
-
|
36
|
-
string
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|