evilution 0.1.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 +7 -0
- data/.beads/.gitignore +51 -0
- data/.beads/.migration-hint-ts +1 -0
- data/.beads/README.md +81 -0
- data/.beads/config.yaml +67 -0
- data/.beads/interactions.jsonl +0 -0
- data/.beads/issues.jsonl +68 -0
- data/.beads/metadata.json +4 -0
- data/.claude/prompts/architect.md +98 -0
- data/.claude/prompts/devops.md +106 -0
- data/.claude/prompts/tests.md +160 -0
- data/CHANGELOG.md +19 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +190 -0
- data/Rakefile +12 -0
- data/claude-swarm.yml +28 -0
- data/exe/evilution +6 -0
- data/lib/evilution/ast/parser.rb +83 -0
- data/lib/evilution/ast/source_surgeon.rb +13 -0
- data/lib/evilution/cli.rb +78 -0
- data/lib/evilution/config.rb +98 -0
- data/lib/evilution/coverage/collector.rb +47 -0
- data/lib/evilution/coverage/test_map.rb +25 -0
- data/lib/evilution/diff/file_filter.rb +29 -0
- data/lib/evilution/diff/parser.rb +47 -0
- data/lib/evilution/integration/base.rb +11 -0
- data/lib/evilution/integration/rspec.rb +184 -0
- data/lib/evilution/isolation/fork.rb +70 -0
- data/lib/evilution/mutation.rb +45 -0
- data/lib/evilution/mutator/base.rb +54 -0
- data/lib/evilution/mutator/operator/arithmetic_replacement.rb +37 -0
- data/lib/evilution/mutator/operator/array_literal.rb +22 -0
- data/lib/evilution/mutator/operator/boolean_literal_replacement.rb +31 -0
- data/lib/evilution/mutator/operator/boolean_operator_replacement.rb +50 -0
- data/lib/evilution/mutator/operator/collection_replacement.rb +37 -0
- data/lib/evilution/mutator/operator/comparison_replacement.rb +37 -0
- data/lib/evilution/mutator/operator/conditional_branch.rb +36 -0
- data/lib/evilution/mutator/operator/conditional_negation.rb +36 -0
- data/lib/evilution/mutator/operator/float_literal.rb +26 -0
- data/lib/evilution/mutator/operator/hash_literal.rb +22 -0
- data/lib/evilution/mutator/operator/integer_literal.rb +45 -0
- data/lib/evilution/mutator/operator/method_body_replacement.rb +22 -0
- data/lib/evilution/mutator/operator/negation_insertion.rb +22 -0
- data/lib/evilution/mutator/operator/nil_replacement.rb +20 -0
- data/lib/evilution/mutator/operator/return_value_removal.rb +22 -0
- data/lib/evilution/mutator/operator/statement_deletion.rb +24 -0
- data/lib/evilution/mutator/operator/string_literal.rb +22 -0
- data/lib/evilution/mutator/operator/symbol_literal.rb +20 -0
- data/lib/evilution/mutator/registry.rb +55 -0
- data/lib/evilution/parallel/pool.rb +98 -0
- data/lib/evilution/parallel/worker.rb +24 -0
- data/lib/evilution/reporter/cli.rb +72 -0
- data/lib/evilution/reporter/json.rb +59 -0
- data/lib/evilution/reporter/suggestion.rb +51 -0
- data/lib/evilution/result/mutation_result.rb +37 -0
- data/lib/evilution/result/summary.rb +54 -0
- data/lib/evilution/runner.rb +139 -0
- data/lib/evilution/subject.rb +20 -0
- data/lib/evilution/version.rb +5 -0
- data/lib/evilution.rb +51 -0
- data/sig/evilution.rbs +4 -0
- metadata +130 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class ArithmeticReplacement < Base
|
|
7
|
+
REPLACEMENTS = {
|
|
8
|
+
:+ => [:-],
|
|
9
|
+
:- => [:+],
|
|
10
|
+
:* => [:/],
|
|
11
|
+
:/ => [:*],
|
|
12
|
+
:% => [:*],
|
|
13
|
+
:** => [:*]
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
def visit_call_node(node)
|
|
17
|
+
replacements = REPLACEMENTS[node.name]
|
|
18
|
+
return super unless replacements
|
|
19
|
+
|
|
20
|
+
loc = node.message_loc
|
|
21
|
+
return super unless loc
|
|
22
|
+
|
|
23
|
+
replacements.each do |replacement|
|
|
24
|
+
add_mutation(
|
|
25
|
+
offset: loc.start_offset,
|
|
26
|
+
length: loc.length,
|
|
27
|
+
replacement: replacement.to_s,
|
|
28
|
+
node: node
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class ArrayLiteral < Base
|
|
7
|
+
def visit_array_node(node)
|
|
8
|
+
if node.opening_loc && node.elements.any?
|
|
9
|
+
add_mutation(
|
|
10
|
+
offset: node.location.start_offset,
|
|
11
|
+
length: node.location.length,
|
|
12
|
+
replacement: "[]",
|
|
13
|
+
node: node
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class BooleanLiteralReplacement < Base
|
|
7
|
+
def visit_true_node(node)
|
|
8
|
+
add_mutation(
|
|
9
|
+
offset: node.location.start_offset,
|
|
10
|
+
length: node.location.length,
|
|
11
|
+
replacement: "false",
|
|
12
|
+
node: node
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def visit_false_node(node)
|
|
19
|
+
add_mutation(
|
|
20
|
+
offset: node.location.start_offset,
|
|
21
|
+
length: node.location.length,
|
|
22
|
+
replacement: "true",
|
|
23
|
+
node: node
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class BooleanOperatorReplacement < Base
|
|
7
|
+
REPLACEMENTS = {
|
|
8
|
+
"&&" => "||",
|
|
9
|
+
"||" => "&&",
|
|
10
|
+
"and" => "or",
|
|
11
|
+
"or" => "and"
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
def visit_and_node(node)
|
|
15
|
+
loc = node.operator_loc
|
|
16
|
+
operator = loc.slice
|
|
17
|
+
replacement = REPLACEMENTS[operator]
|
|
18
|
+
|
|
19
|
+
if replacement
|
|
20
|
+
add_mutation(
|
|
21
|
+
offset: loc.start_offset,
|
|
22
|
+
length: loc.length,
|
|
23
|
+
replacement: replacement,
|
|
24
|
+
node: node
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
super
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def visit_or_node(node)
|
|
32
|
+
loc = node.operator_loc
|
|
33
|
+
operator = loc.slice
|
|
34
|
+
replacement = REPLACEMENTS[operator]
|
|
35
|
+
|
|
36
|
+
if replacement
|
|
37
|
+
add_mutation(
|
|
38
|
+
offset: loc.start_offset,
|
|
39
|
+
length: loc.length,
|
|
40
|
+
replacement: replacement,
|
|
41
|
+
node: node
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class CollectionReplacement < Base
|
|
7
|
+
REPLACEMENTS = {
|
|
8
|
+
map: [:each],
|
|
9
|
+
each: [:map],
|
|
10
|
+
select: [:reject],
|
|
11
|
+
reject: [:select],
|
|
12
|
+
flat_map: [:map],
|
|
13
|
+
collect: [:each]
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
def visit_call_node(node)
|
|
17
|
+
replacements = REPLACEMENTS[node.name]
|
|
18
|
+
return super unless replacements
|
|
19
|
+
|
|
20
|
+
loc = node.message_loc
|
|
21
|
+
return super unless loc
|
|
22
|
+
|
|
23
|
+
replacements.each do |replacement|
|
|
24
|
+
add_mutation(
|
|
25
|
+
offset: loc.start_offset,
|
|
26
|
+
length: loc.length,
|
|
27
|
+
replacement: replacement.to_s,
|
|
28
|
+
node: node
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class ComparisonReplacement < Base
|
|
7
|
+
REPLACEMENTS = {
|
|
8
|
+
:> => %i[>= ==],
|
|
9
|
+
:< => %i[<= ==],
|
|
10
|
+
:>= => %i[> ==],
|
|
11
|
+
:<= => %i[< ==],
|
|
12
|
+
:== => [:!=],
|
|
13
|
+
:!= => [:==]
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
def visit_call_node(node)
|
|
17
|
+
replacements = REPLACEMENTS[node.name]
|
|
18
|
+
return super unless replacements
|
|
19
|
+
|
|
20
|
+
loc = node.message_loc
|
|
21
|
+
return super unless loc
|
|
22
|
+
|
|
23
|
+
replacements.each do |replacement|
|
|
24
|
+
add_mutation(
|
|
25
|
+
offset: loc.start_offset,
|
|
26
|
+
length: loc.length,
|
|
27
|
+
replacement: replacement.to_s,
|
|
28
|
+
node: node
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class ConditionalBranch < Base
|
|
7
|
+
def visit_if_node(node)
|
|
8
|
+
if node.statements && node.subsequent.nil?
|
|
9
|
+
add_mutation(
|
|
10
|
+
offset: node.statements.location.start_offset,
|
|
11
|
+
length: node.statements.location.length,
|
|
12
|
+
replacement: "nil",
|
|
13
|
+
node: node
|
|
14
|
+
)
|
|
15
|
+
elsif node.statements && node.subsequent&.statements
|
|
16
|
+
add_mutation(
|
|
17
|
+
offset: node.statements.location.start_offset,
|
|
18
|
+
length: node.statements.location.length,
|
|
19
|
+
replacement: "nil",
|
|
20
|
+
node: node
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
add_mutation(
|
|
24
|
+
offset: node.subsequent.statements.location.start_offset,
|
|
25
|
+
length: node.subsequent.statements.location.length,
|
|
26
|
+
replacement: "nil",
|
|
27
|
+
node: node
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class ConditionalNegation < Base
|
|
7
|
+
def visit_if_node(node)
|
|
8
|
+
mutate_predicate(node)
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def visit_unless_node(node)
|
|
13
|
+
mutate_predicate(node)
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def mutate_predicate(node)
|
|
20
|
+
add_mutation(
|
|
21
|
+
offset: node.predicate.location.start_offset,
|
|
22
|
+
length: node.predicate.location.length,
|
|
23
|
+
replacement: "true",
|
|
24
|
+
node: node
|
|
25
|
+
)
|
|
26
|
+
add_mutation(
|
|
27
|
+
offset: node.predicate.location.start_offset,
|
|
28
|
+
length: node.predicate.location.length,
|
|
29
|
+
replacement: "false",
|
|
30
|
+
node: node
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class FloatLiteral < Base
|
|
7
|
+
def visit_float_node(node)
|
|
8
|
+
replacement = case node.value
|
|
9
|
+
when 0.0 then "1.0"
|
|
10
|
+
when 1.0 then "0.0"
|
|
11
|
+
else "0.0"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
add_mutation(
|
|
15
|
+
offset: node.location.start_offset,
|
|
16
|
+
length: node.location.length,
|
|
17
|
+
replacement: replacement,
|
|
18
|
+
node: node
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class HashLiteral < Base
|
|
7
|
+
def visit_hash_node(node)
|
|
8
|
+
if node.elements.any?
|
|
9
|
+
add_mutation(
|
|
10
|
+
offset: node.location.start_offset,
|
|
11
|
+
length: node.location.length,
|
|
12
|
+
replacement: "{}",
|
|
13
|
+
node: node
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class IntegerLiteral < Base
|
|
7
|
+
def visit_integer_node(node)
|
|
8
|
+
value = node.value
|
|
9
|
+
|
|
10
|
+
if value.zero?
|
|
11
|
+
add_mutation(
|
|
12
|
+
offset: node.location.start_offset,
|
|
13
|
+
length: node.location.length,
|
|
14
|
+
replacement: "1",
|
|
15
|
+
node: node
|
|
16
|
+
)
|
|
17
|
+
elsif value == 1
|
|
18
|
+
add_mutation(
|
|
19
|
+
offset: node.location.start_offset,
|
|
20
|
+
length: node.location.length,
|
|
21
|
+
replacement: "0",
|
|
22
|
+
node: node
|
|
23
|
+
)
|
|
24
|
+
else
|
|
25
|
+
add_mutation(
|
|
26
|
+
offset: node.location.start_offset,
|
|
27
|
+
length: node.location.length,
|
|
28
|
+
replacement: "0",
|
|
29
|
+
node: node
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
add_mutation(
|
|
33
|
+
offset: node.location.start_offset,
|
|
34
|
+
length: node.location.length,
|
|
35
|
+
replacement: (node.value + 1).to_s,
|
|
36
|
+
node: node
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
super
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class MethodBodyReplacement < Base
|
|
7
|
+
def visit_def_node(node)
|
|
8
|
+
if node.body
|
|
9
|
+
add_mutation(
|
|
10
|
+
offset: node.body.location.start_offset,
|
|
11
|
+
length: node.body.location.length,
|
|
12
|
+
replacement: "nil",
|
|
13
|
+
node: node
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class NegationInsertion < Base
|
|
7
|
+
def visit_call_node(node)
|
|
8
|
+
if node.name.to_s.end_with?("?")
|
|
9
|
+
add_mutation(
|
|
10
|
+
offset: node.location.start_offset,
|
|
11
|
+
length: 0,
|
|
12
|
+
replacement: "!",
|
|
13
|
+
node: node
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class NilReplacement < Base
|
|
7
|
+
def visit_nil_node(node)
|
|
8
|
+
add_mutation(
|
|
9
|
+
offset: node.location.start_offset,
|
|
10
|
+
length: node.location.length,
|
|
11
|
+
replacement: "true",
|
|
12
|
+
node: node
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class ReturnValueRemoval < Base
|
|
7
|
+
def visit_return_node(node)
|
|
8
|
+
if node.arguments
|
|
9
|
+
add_mutation(
|
|
10
|
+
offset: node.location.start_offset,
|
|
11
|
+
length: node.location.length,
|
|
12
|
+
replacement: "return",
|
|
13
|
+
node: node
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class StatementDeletion < Base
|
|
7
|
+
def visit_statements_node(node)
|
|
8
|
+
if node.body.length > 1
|
|
9
|
+
node.body.each do |child|
|
|
10
|
+
add_mutation(
|
|
11
|
+
offset: child.location.start_offset,
|
|
12
|
+
length: child.location.length,
|
|
13
|
+
replacement: "",
|
|
14
|
+
node: child
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class StringLiteral < Base
|
|
7
|
+
def visit_string_node(node)
|
|
8
|
+
replacement = node.content.empty? ? '"mutation"' : '""'
|
|
9
|
+
|
|
10
|
+
add_mutation(
|
|
11
|
+
offset: node.location.start_offset,
|
|
12
|
+
length: node.location.length,
|
|
13
|
+
replacement: replacement,
|
|
14
|
+
node: node
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
module Operator
|
|
6
|
+
class SymbolLiteral < Base
|
|
7
|
+
def visit_symbol_node(node)
|
|
8
|
+
add_mutation(
|
|
9
|
+
offset: node.location.start_offset,
|
|
10
|
+
length: node.location.length,
|
|
11
|
+
replacement: ":__evilution_mutated__",
|
|
12
|
+
node: node
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Evilution
|
|
4
|
+
module Mutator
|
|
5
|
+
class Registry
|
|
6
|
+
def self.default
|
|
7
|
+
registry = new
|
|
8
|
+
[
|
|
9
|
+
Operator::ComparisonReplacement,
|
|
10
|
+
Operator::ArithmeticReplacement,
|
|
11
|
+
Operator::BooleanOperatorReplacement,
|
|
12
|
+
Operator::BooleanLiteralReplacement,
|
|
13
|
+
Operator::NilReplacement,
|
|
14
|
+
Operator::IntegerLiteral,
|
|
15
|
+
Operator::FloatLiteral,
|
|
16
|
+
Operator::StringLiteral,
|
|
17
|
+
Operator::ArrayLiteral,
|
|
18
|
+
Operator::HashLiteral,
|
|
19
|
+
Operator::SymbolLiteral,
|
|
20
|
+
Operator::ConditionalNegation,
|
|
21
|
+
Operator::ConditionalBranch,
|
|
22
|
+
Operator::StatementDeletion,
|
|
23
|
+
Operator::MethodBodyReplacement,
|
|
24
|
+
Operator::NegationInsertion,
|
|
25
|
+
Operator::ReturnValueRemoval,
|
|
26
|
+
Operator::CollectionReplacement
|
|
27
|
+
].each { |op| registry.register(op) }
|
|
28
|
+
registry
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def initialize
|
|
32
|
+
@operators = []
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def register(operator_class)
|
|
36
|
+
@operators << operator_class
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def mutations_for(subject)
|
|
41
|
+
@operators.flat_map do |operator_class|
|
|
42
|
+
operator_class.new.call(subject)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def operator_count
|
|
47
|
+
@operators.length
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def operators
|
|
51
|
+
@operators.dup
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|