brutal 1.1.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.md +1 -1
- data/README.md +31 -29
- data/bin/brutal +1 -1
- data/lib/brutal/configuration.rb +35 -4
- data/lib/brutal/file/read.rb +1 -1
- data/lib/brutal/file/write.rb +2 -2
- data/lib/brutal/scaffold.rb +23 -16
- data/lib/brutal/yaml.rb +2 -2
- data/lib/brutal.rb +4 -3
- metadata +34 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 958cf59b6aea2f5e323b849c83a86399cd9ad99e2433b899160a2422275a319e
|
4
|
+
data.tar.gz: bdb36f749b6bfcb87683f12d3db9e74c716bdbfbf41e21c845864bf1e17f7a7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f4b098047917136946c7bb88b111b5e6facf2a13ab70677bf0bce480bc2b4f0931f89f0be8ad2dc6db152abaaeffbdece5993d7ac5fba2ca85fde601df0c4c2
|
7
|
+
data.tar.gz: 7d5d125189afacf46a008a6b12c79ec68ffeb6e15c4324c1a6924a19be9061dac5760224225a564a8a1d9410b18a3da80591beeb30e5abc15995a5094326fab4
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -1,53 +1,57 @@
|
|
1
1
|
# Brutal 💎🔨
|
2
2
|
|
3
|
-
[![
|
4
|
-
[![
|
5
|
-
[![
|
6
|
-
[![
|
7
|
-
[![
|
3
|
+
[![Version](https://img.shields.io/github/v/tag/fixrb/brutal?label=Version&logo=github)](https://github.com/fixrb/brutal/releases)
|
4
|
+
[![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?logo=github)](https://rubydoc.info/github/fixrb/brutal/main)
|
5
|
+
[![CI](https://github.com/fixrb/brutal/workflows/CI/badge.svg?branch=main)](https://github.com/fixrb/brutal/actions?query=workflow%3Aci+branch%3Amain)
|
6
|
+
[![RuboCop](https://github.com/fixrb/brutal/workflows/RuboCop/badge.svg?branch=main)](https://github.com/fixrb/brutal/actions?query=workflow%3Arubocop+branch%3Amain)
|
7
|
+
[![License](https://img.shields.io/github/license/fixrb/brutal?label=License&logo=github)](https://github.com/fixrb/brutal/raw/main/LICENSE.md)
|
8
8
|
|
9
9
|
> A _code-first_ approach to automate the writing of unit tests.
|
10
10
|
|
11
11
|
## Intro
|
12
12
|
|
13
|
-
[![I Hate Tests](https://github.com/fixrb/brutal/raw/
|
13
|
+
[![I Hate Tests](https://github.com/fixrb/brutal/raw/main/img/rubyhack-2019-ruby3-what-s-missing-by-yukihiro-matsumoto.jpg)](https://www.youtube.com/embed/cmOt9HhszCI?start=1732&end=1736 "I don't like tests. It's not DRY.")
|
14
14
|
|
15
|
-
> I don't like tests. It's not DRY
|
15
|
+
> I don't like tests. It's not DRY.
|
16
16
|
> -- [Matz](https://github.com/matz)
|
17
17
|
|
18
18
|
## Overview
|
19
19
|
|
20
|
-
Let __Brutal__
|
20
|
+
Let __Brutal__ craft for you in no time a (potentially huge) framework-less vanilla Ruby file describing the actual behavior of your code across as many context combinations as necessary.
|
21
21
|
|
22
|
-
By delegating to __Brutal__ the repetitive (and redundant) task of writing tests, you
|
22
|
+
By delegating to __Brutal__ the repetitive (and redundant) task of writing tests, you will be able to focus on your core business: the code itself.
|
23
|
+
|
24
|
+
![Brutal-Driven Development](https://github.com/fixrb/brutal/raw/main/img/brutal-driven-development.jpg)
|
23
25
|
|
24
26
|
## Warning
|
25
27
|
|
26
|
-
|
28
|
+
The _Brutal-Driven Development_ process does not prevent bugs from appearing in the code.
|
29
|
+
|
30
|
+
A generated test suite acts as a _picture of the code's behavior_. Therefore, if the code is wrong, the picture of the code's behavior will also be wrong.
|
27
31
|
|
28
|
-
|
29
|
-
a generated test suite is wrong as long as the code is wrong,
|
30
|
-
regardless of whether all true expectations.
|
32
|
+
The mere fact that all expectations are true does not mean that the code behaves as it should.
|
31
33
|
|
32
|
-
|
33
|
-
It is therefore important to read it well.
|
34
|
-
This is the price for _Brutal-Driven Development_.
|
34
|
+
It is therefore the responsibility of the developer to analyze the generated behavioral pictures to ensure that the code reacts as it is supposed to according to the contexts in which it is evaluated.
|
35
35
|
|
36
36
|
## Installation
|
37
37
|
|
38
38
|
Add this line to your application's Gemfile:
|
39
39
|
|
40
40
|
```ruby
|
41
|
-
gem
|
41
|
+
gem "brutal"
|
42
42
|
```
|
43
43
|
|
44
44
|
And then execute:
|
45
45
|
|
46
|
-
|
46
|
+
```sh
|
47
|
+
bundle install
|
48
|
+
```
|
47
49
|
|
48
50
|
Or install it yourself as:
|
49
51
|
|
50
|
-
|
52
|
+
```sh
|
53
|
+
gem install brutal
|
54
|
+
```
|
51
55
|
|
52
56
|
## Quick Start
|
53
57
|
|
@@ -111,7 +115,7 @@ raise if actual.length != 9
|
|
111
115
|
|
112
116
|
### More examples
|
113
117
|
|
114
|
-
https://github.com/fixrb/brutal/raw/
|
118
|
+
https://github.com/fixrb/brutal/raw/main/examples/
|
115
119
|
|
116
120
|
## Rake integration example
|
117
121
|
|
@@ -119,10 +123,14 @@ A generated `test.rb` file could be matched as follows:
|
|
119
123
|
|
120
124
|
```ruby
|
121
125
|
Rake::TestTask.new do |t|
|
122
|
-
t.pattern =
|
126
|
+
t.pattern = "test.rb"
|
123
127
|
end
|
124
128
|
```
|
125
129
|
|
130
|
+
## Test suite
|
131
|
+
|
132
|
+
__Brutal__'s test set is brutally self-generated here: [./test.rb](https://github.com/fixrb/brutal/blob/main/test.rb)
|
133
|
+
|
126
134
|
## Contact
|
127
135
|
|
128
136
|
* Source code: https://github.com/fixrb/brutal
|
@@ -133,19 +141,13 @@ __Brutal__ follows [Semantic Versioning 2.0](https://semver.org/).
|
|
133
141
|
|
134
142
|
## License
|
135
143
|
|
136
|
-
The gem is available as open source under the terms of the [MIT License](https://
|
144
|
+
The [gem](https://rubygems.org/gems/brutal) is available as open source under the terms of the [MIT License](https://github.com/fixrb/brutal/raw/main/LICENSE.md).
|
137
145
|
|
138
146
|
***
|
139
147
|
|
140
148
|
<p>
|
141
149
|
This project is sponsored by:<br />
|
142
150
|
<a href="https://sashite.com/"><img
|
143
|
-
src="https://github.com/fixrb/brutal/raw/
|
151
|
+
src="https://github.com/fixrb/brutal/raw/main/img/sashite.png"
|
144
152
|
alt="Sashite" /></a>
|
145
153
|
</p>
|
146
|
-
|
147
|
-
[workflow_rubocop]: https://github.com/fixrb/brutal/actions?query=workflow%3ARuboCop
|
148
|
-
[gem]: https://rubygems.org/gems/brutal
|
149
|
-
[travis]: https://travis-ci.org/fixrb/brutal
|
150
|
-
[inchpages]: https://inch-ci.org/github/fixrb/brutal
|
151
|
-
[rubydoc]: https://rubydoc.info/gems/brutal/frames
|
data/bin/brutal
CHANGED
data/lib/brutal/configuration.rb
CHANGED
@@ -5,17 +5,48 @@ module Brutal
|
|
5
5
|
#
|
6
6
|
# @since 1.0.0
|
7
7
|
class Configuration
|
8
|
-
|
8
|
+
ACTUALS_KEY = "actuals"
|
9
|
+
CONTEXTS_KEY = "contexts"
|
10
|
+
HEADER_KEY = "header"
|
11
|
+
SUBJECT_KEY = "subject"
|
9
12
|
|
10
|
-
|
13
|
+
DEFAULT_ACTUALS = [].freeze
|
14
|
+
DEFAULT_CONTEXTS = {}.freeze
|
15
|
+
DEFAULT_HEADER = "# Brutal test suite"
|
16
|
+
DEFAULT_SUBJECT = ""
|
11
17
|
|
12
|
-
|
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:)
|
13
44
|
raise ::TypeError, actuals.inspect unless actuals.is_a?(::Array)
|
14
45
|
raise ::TypeError, contexts.inspect unless contexts.is_a?(::Hash)
|
15
46
|
raise ::TypeError, header.inspect unless header.is_a?(::String)
|
16
47
|
raise ::TypeError, subject.inspect unless subject.is_a?(::String)
|
17
48
|
|
18
|
-
@actuals = actuals
|
49
|
+
@actuals = actuals.sort
|
19
50
|
@contexts = contexts
|
20
51
|
@header = header
|
21
52
|
@subject = subject
|
data/lib/brutal/file/read.rb
CHANGED
data/lib/brutal/file/write.rb
CHANGED
@@ -9,7 +9,7 @@ module Brutal
|
|
9
9
|
#
|
10
10
|
# @since 1.1.0
|
11
11
|
class Write
|
12
|
-
NAME =
|
12
|
+
NAME = "test.rb"
|
13
13
|
|
14
14
|
attr_reader :name
|
15
15
|
|
@@ -18,7 +18,7 @@ module Brutal
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def call(scaffold)
|
21
|
-
file = ::File.open(path,
|
21
|
+
file = ::File.open(path, "w")
|
22
22
|
file.write(scaffold)
|
23
23
|
|
24
24
|
true
|
data/lib/brutal/scaffold.rb
CHANGED
@@ -5,13 +5,23 @@ module Brutal
|
|
5
5
|
#
|
6
6
|
# @since 1.0.0
|
7
7
|
class Scaffold
|
8
|
-
|
8
|
+
# Specifies templates to challenge evaluated subjects & get results.
|
9
|
+
attr_reader :actuals
|
9
10
|
|
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.
|
11
21
|
def initialize(header, subject, *actuals, **contexts)
|
12
|
-
warn(
|
13
|
-
warn(
|
14
|
-
warn(
|
22
|
+
warn("Empty subject!") if subject.empty?
|
23
|
+
warn("Empty actual values!") if actuals.empty?
|
24
|
+
warn("Empty contexts!") if contexts.empty?
|
15
25
|
|
16
26
|
eval(header) # rubocop:disable Security/Eval
|
17
27
|
|
@@ -28,13 +38,11 @@ module Brutal
|
|
28
38
|
object.strip
|
29
39
|
end
|
30
40
|
|
31
|
-
# Return a string representation
|
41
|
+
# Return a string representation.
|
32
42
|
#
|
33
43
|
# @return [String]
|
34
|
-
#
|
35
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
36
44
|
def to_s
|
37
|
-
header.chomp
|
45
|
+
"#{header.chomp}\n#{blank_line}" + combinations_values.map do |values|
|
38
46
|
attributes = context_names.each_with_index.inject({}) do |h, (name, i)|
|
39
47
|
h.merge(name.to_sym => inspect(values.fetch(i)))
|
40
48
|
end
|
@@ -43,7 +51,7 @@ module Brutal
|
|
43
51
|
|
44
52
|
string = <<~CODE
|
45
53
|
actual = begin
|
46
|
-
#{actual_str.gsub(/^/,
|
54
|
+
#{actual_str.gsub(/^/, " ")}
|
47
55
|
end
|
48
56
|
|
49
57
|
CODE
|
@@ -51,19 +59,18 @@ module Brutal
|
|
51
59
|
actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
|
52
60
|
|
53
61
|
actuals.each do |actual_value|
|
54
|
-
result_str = format(actual_value, subject:
|
62
|
+
result_str = format(actual_value, subject: "actual")
|
55
63
|
string += "raise if #{result_str} != #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
|
56
64
|
end
|
57
65
|
|
58
66
|
string
|
59
67
|
end.join(blank_line)
|
60
68
|
end
|
61
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
62
69
|
|
63
70
|
def blank_line
|
64
|
-
"\n"
|
65
|
-
|
66
|
-
|
71
|
+
"\n" \
|
72
|
+
"# #{"-" * 78}\n" \
|
73
|
+
"\n"
|
67
74
|
end
|
68
75
|
|
69
76
|
def context_names
|
@@ -75,7 +82,7 @@ module Brutal
|
|
75
82
|
end
|
76
83
|
|
77
84
|
def combinations_values
|
78
|
-
Array(contexts_values[0]).product(*Array(contexts_values[1
|
85
|
+
Array(contexts_values[0]).product(*Array(contexts_values[1..]))
|
79
86
|
end
|
80
87
|
end
|
81
88
|
end
|
data/lib/brutal/yaml.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "yaml"
|
4
4
|
|
5
5
|
module Brutal
|
6
6
|
# Brutal::Yaml
|
@@ -8,7 +8,7 @@ module Brutal
|
|
8
8
|
# @since 1.1.0
|
9
9
|
module Yaml
|
10
10
|
def self.parse(yaml)
|
11
|
-
::YAML.safe_load(yaml, symbolize_names:
|
11
|
+
::YAML.safe_load(yaml, symbolize_names: false)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/brutal.rb
CHANGED
@@ -6,14 +6,15 @@
|
|
6
6
|
file/write
|
7
7
|
scaffold
|
8
8
|
yaml
|
9
|
-
].each { |file_name| require_relative(File.join(
|
9
|
+
].each { |file_name| require_relative(File.join("brutal", file_name)) }
|
10
10
|
|
11
|
-
# The Brutal namespace
|
11
|
+
# The Brutal namespace.
|
12
12
|
module Brutal
|
13
13
|
def self.generate!
|
14
14
|
yaml = File::Read.new.call
|
15
15
|
hash = Yaml.parse(yaml)
|
16
|
-
conf = Configuration.
|
16
|
+
conf = Configuration.load(hash)
|
17
|
+
|
17
18
|
ruby = Scaffold.new(conf.header,
|
18
19
|
conf.subject,
|
19
20
|
*conf.actuals,
|
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.1
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop-md
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rubocop-performance
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +66,20 @@ dependencies:
|
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: rubocop-thread_safety
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -113,7 +141,8 @@ files:
|
|
113
141
|
homepage: https://github.com/fixrb/brutal
|
114
142
|
licenses:
|
115
143
|
- MIT
|
116
|
-
metadata:
|
144
|
+
metadata:
|
145
|
+
rubygems_mfa_required: 'true'
|
117
146
|
post_install_message:
|
118
147
|
rdoc_options: []
|
119
148
|
require_paths:
|
@@ -122,14 +151,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
151
|
requirements:
|
123
152
|
- - ">="
|
124
153
|
- !ruby/object:Gem::Version
|
125
|
-
version: 2.
|
154
|
+
version: 2.7.0
|
126
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
156
|
requirements:
|
128
157
|
- - ">="
|
129
158
|
- !ruby/object:Gem::Version
|
130
159
|
version: '0'
|
131
160
|
requirements: []
|
132
|
-
rubygems_version: 3.
|
161
|
+
rubygems_version: 3.1.6
|
133
162
|
signing_key:
|
134
163
|
specification_version: 4
|
135
164
|
summary: A code-first approach to automate the writing of unit tests.
|