Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mutant: add INVERT_LOOPCTRL mutation #159

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/docs/schema/configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,20 @@
"default": true
}
}
},
"invert-loopctrl": {
"title": "The invert-loopctrl Schema",
"type": "object",
"required": [
"enabled"
],
"properties": {
"enabled": {
"title": "The enabled Schema",
"type": "boolean",
"default": false
}
}
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions docs/docs/usage/commands/unleash/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,16 @@ Enables/disables the [INVERT LOGICAL](../../mutations/invert_logical.md) mutant
gremlins unleash --invert_logical=true
```

### Invert loop control

:material-flag: `--invert-loopctrl` · :material-sign-direction: Default: `false`

Enables/disables the [INVERT LOOP](../../mutations/invert_loop.md) mutant type.

```shell
gremlins unleash --invert-loopctrl
```

### Workers

:material-flag: `--workers` · :material-sign-direction: Default: `0`
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/usage/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ mutants:
enabled: true
invert-logical:
enabled: false
invert-loopctrl:
enabled: false

```

Expand Down
17 changes: 9 additions & 8 deletions docs/docs/usage/mutations/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ A _mutant_ is the "gremlin" that actually changes the source code.

Each _mutant type_ can be enabled or disabled, and only a subset of mutations is enabled by default.

| MutationType | Default |
|---------------------------------------------------|:---------:|
| [ARITHMETIC BASE](arithmetic_base.md) | YES |
| [CONDITIONALS BOUNDARY](conditionals_boundary.md) | YES |
| [CONDITIONALS NEGATION](conditionals_negation.md) | YES |
| [INCREMENT DECREMENT](increment_decrement.md) | YES |
| [INVERT NEGATIVES ](invert_negatives.md) | YES |
| [INVERT LOGICAL ](invert_logical.md) | FALSE |
| MutationType | Default |
|---------------------------------------------------|:----------:|
| [ARITHMETIC BASE](arithmetic_base.md) | YES |
| [CONDITIONALS BOUNDARY](conditionals_boundary.md) | YES |
| [CONDITIONALS NEGATION](conditionals_negation.md) | YES |
| [INCREMENT DECREMENT](increment_decrement.md) | YES |
| [INVERT NEGATIVES ](invert_negatives.md) | YES |
| [INVERT LOGICAL ](invert_logical.md) | FALSE |
| [INVERT LOOP CTRL ](invert_loop.md) | FALSE |
36 changes: 36 additions & 0 deletions docs/docs/usage/mutations/invert_loop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: Invert loop
---

# Invert loop control

_Invert loop control_ will perform inversions on control operations, which means a `continue` will become a `break`.

## Mutation table

[//]: # (@formatter:off)

| Orig | Mutation |
|:--------:|:--------:|
| continue | break |
| break | continue |

[//]: # (@formatter:on)

## Examples

=== "Original"

```go
for i := 0; i < 3; i++ {
continue
}
```

=== "Mutated"

```go
for i := 0; i < 3; i++ {
break
}
```
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ nav:
- usage/mutations/increment_decrement.md
- usage/mutations/invert_negatives.md
- usage/mutations/invert_logical.md
- usage/mutations/invert_loop.md
- Continuous integration:
- usage/ci/github-action.md
- usage/ci/docker.md
Expand Down
1 change: 1 addition & 0 deletions internal/configuration/mutantenabled.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var mutationEnabled = map[mutator.Type]bool{
mutator.IncrementDecrement: true,
mutator.InvertLogical: false,
mutator.InvertNegatives: true,
mutator.InvertLoopCtrl: false,
}

// IsDefaultEnabled returns the default enabled/disabled state of the mutation.
Expand Down
4 changes: 4 additions & 0 deletions internal/configuration/mutantenables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func TestMutantDefaultStatus(t *testing.T) {
mutantType: mutator.InvertNegatives,
expected: true,
},
{
mutantType: mutator.InvertLoopCtrl,
expected: false,
},
}

for _, tc := range testCases {
Expand Down
16 changes: 16 additions & 0 deletions internal/engine/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,22 @@ func TestMutations(t *testing.T) {
covResult: notCoveredPosition("testdata/fixtures/lor_go"),
mutStatus: mutator.NotCovered,
},
{
name: "it recognizes INVERT_LOOPCTRL with CONTINUE",
fixture: "testdata/fixtures/loop_continue_go",
mutantType: mutator.InvertLoopCtrl,
token: token.CONTINUE,
covResult: notCoveredPosition("testdata/fixtures/loop_continue_go"),
mutStatus: mutator.NotCovered,
},
{
name: "it recognizes INVERT_LOOPCTRL with BREAK",
fixture: "testdata/fixtures/loop_break_go",
mutantType: mutator.InvertLoopCtrl,
token: token.BREAK,
covResult: notCoveredPosition("testdata/fixtures/loop_break_go"),
mutStatus: mutator.NotCovered,
},
// Common behaviours
{
name: "it works with recursion",
Expand Down
36 changes: 21 additions & 15 deletions internal/engine/mappings.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,23 @@ import (
// TokenMutantType is the mapping from each token.Token and all the
// mutator.Type that can be applied to it.
var TokenMutantType = map[token.Token][]mutator.Type{
token.SUB: {mutator.InvertNegatives, mutator.ArithmeticBase},
token.ADD: {mutator.ArithmeticBase},
token.MUL: {mutator.ArithmeticBase},
token.QUO: {mutator.ArithmeticBase},
token.REM: {mutator.ArithmeticBase},
token.EQL: {mutator.ConditionalsNegation},
token.NEQ: {mutator.ConditionalsNegation},
token.GTR: {mutator.ConditionalsBoundary, mutator.ConditionalsNegation},
token.LSS: {mutator.ConditionalsBoundary, mutator.ConditionalsNegation},
token.GEQ: {mutator.ConditionalsBoundary, mutator.ConditionalsNegation},
token.LEQ: {mutator.ConditionalsBoundary, mutator.ConditionalsNegation},
token.INC: {mutator.IncrementDecrement},
token.DEC: {mutator.IncrementDecrement},
token.LAND: {mutator.InvertLogical},
token.LOR: {mutator.InvertLogical},
token.SUB: {mutator.InvertNegatives, mutator.ArithmeticBase},
token.ADD: {mutator.ArithmeticBase},
token.MUL: {mutator.ArithmeticBase},
token.QUO: {mutator.ArithmeticBase},
token.REM: {mutator.ArithmeticBase},
token.EQL: {mutator.ConditionalsNegation},
token.NEQ: {mutator.ConditionalsNegation},
token.GTR: {mutator.ConditionalsBoundary, mutator.ConditionalsNegation},
token.LSS: {mutator.ConditionalsBoundary, mutator.ConditionalsNegation},
token.GEQ: {mutator.ConditionalsBoundary, mutator.ConditionalsNegation},
token.LEQ: {mutator.ConditionalsBoundary, mutator.ConditionalsNegation},
token.INC: {mutator.IncrementDecrement},
token.DEC: {mutator.IncrementDecrement},
token.LAND: {mutator.InvertLogical},
token.LOR: {mutator.InvertLogical},
token.BREAK: {mutator.InvertLoopCtrl},
token.CONTINUE: {mutator.InvertLoopCtrl},
}

var tokenMutations = map[mutator.Type]map[token.Token]token.Token{
Expand Down Expand Up @@ -75,4 +77,8 @@ var tokenMutations = map[mutator.Type]map[token.Token]token.Token{
mutator.InvertNegatives: {
token.SUB: token.ADD,
},
mutator.InvertLoopCtrl: {
token.BREAK: token.CONTINUE,
token.CONTINUE: token.BREAK,
},
}
3 changes: 3 additions & 0 deletions internal/engine/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ func NewTokenNode(n ast.Node) (*NodeToken, bool) {
case *ast.IncDecStmt:
tok = &n.Tok
pos = n.TokPos
case *ast.BranchStmt:
tok = &n.Tok
pos = n.TokPos
default:
return &NodeToken{}, false
}
Expand Down
10 changes: 10 additions & 0 deletions internal/engine/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ func TestNewTokenNode(t *testing.T) {
wantPos: 123,
supported: true,
},
{
name: "BranchStmt",
node: &ast.BranchStmt{
TokPos: 123,
Tok: token.CONTINUE,
},
wantTok: token.CONTINUE,
wantPos: 123,
supported: true,
},
{
name: "not supported",
node: &ast.BasicLit{
Expand Down
7 changes: 7 additions & 0 deletions internal/engine/testdata/fixtures/loop_break_go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

func main() {
for {
break
}
}
7 changes: 7 additions & 0 deletions internal/engine/testdata/fixtures/loop_continue_go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

func main() {
for i := 0; i < 3; i++ {
continue
}
}
4 changes: 4 additions & 0 deletions internal/mutator/mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const (
IncrementDecrement
InvertLogical
InvertNegatives
InvertLoopCtrl
)

// Types allows to iterate over Type.
Expand All @@ -85,6 +86,7 @@ var Types = []Type{
IncrementDecrement,
InvertLogical,
InvertNegatives,
InvertLoopCtrl,
}

func (mt Type) String() string {
Expand All @@ -101,6 +103,8 @@ func (mt Type) String() string {
return "INVERT_NEGATIVES"
case ArithmeticBase:
return "ARITHMETIC_BASE"
case InvertLoopCtrl:
return "INVERT_LOOPCTRL"
default:
panic("this should not happen")
}
Expand Down
5 changes: 5 additions & 0 deletions internal/mutator/mutator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ func TestTypeString(t *testing.T) {
expected: "ARITHMETIC_BASE",
mutantType: mutator.ArithmeticBase,
},
{
name: "INVERT_LOOPCTRL",
expected: "INVERT_LOOPCTRL",
mutantType: mutator.InvertLoopCtrl,
},
}
for _, tc := range testCases {
tc := tc
Expand Down