From 6516bd6e9d35256ac843f20e4ba77573ab78c490 Mon Sep 17 00:00:00 2001 From: Jonathan Baldie Date: Thu, 14 May 2026 11:37:19 +0100 Subject: [PATCH 1/2] fix: strip one char from wildcard disable-mutator pattern, not two The --disable flag trims d[:len(d)-2] when the pattern ends with '*', but '*' is only one character, so short names like 'branch*' produce the wrong prefix ('branc' instead of 'branch'). Co-Authored-By: Claude Sonnet 4.6 --- cmd/go-mutesting/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/go-mutesting/main.go b/cmd/go-mutesting/main.go index 5749905..741f114 100644 --- a/cmd/go-mutesting/main.go +++ b/cmd/go-mutesting/main.go @@ -174,7 +174,7 @@ MUTATOR: for _, d := range opts.Mutator.DisableMutators { pattern := strings.HasSuffix(d, "*") - if (pattern && strings.HasPrefix(name, d[:len(d)-2])) || (!pattern && name == d) { + if (pattern && strings.HasPrefix(name, d[:len(d)-1])) || (!pattern && name == d) { continue MUTATOR } } From 7f50eed5765869c91e913804d4479c6df344aea4 Mon Sep 17 00:00:00 2001 From: Jonathan Baldie Date: Thu, 14 May 2026 11:50:26 +0100 Subject: [PATCH 2/2] fix: extract mutatorDisabled and add tests for wildcard pattern matching Extracts the pattern-matching logic into a named function so it can be tested directly. Adds TestMutatorDisabled which pins the off-by-one bug: with len(d)-2, a two-char pattern like "b*" produced an empty prefix and disabled every mutator; len(d)-1 correctly extracts "b". Co-Authored-By: Claude Sonnet 4.6 --- cmd/go-mutesting/main.go | 26 ++++++++++++++++++-------- cmd/go-mutesting/main_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/cmd/go-mutesting/main.go b/cmd/go-mutesting/main.go index 741f114..116e909 100644 --- a/cmd/go-mutesting/main.go +++ b/cmd/go-mutesting/main.go @@ -109,6 +109,22 @@ type mutatorItem struct { Mutator mutator.Mutator } +// mutatorDisabled reports whether name matches any entry in patterns. +// An entry ending in '*' is treated as a prefix: "branch*" disables any +// mutator whose name starts with "branch". +func mutatorDisabled(name string, patterns []string) bool { + for _, d := range patterns { + if strings.HasSuffix(d, "*") { + if strings.HasPrefix(name, d[:len(d)-1]) { + return true + } + } else if name == d { + return true + } + } + return false +} + func mainCmd(args []string) int { var opts = &models.Options{} var mutationBlackList = map[string]struct{}{} @@ -170,14 +186,8 @@ func mainCmd(args []string) int { MUTATOR: for _, name := range mutator.List() { - if len(opts.Mutator.DisableMutators) > 0 { - for _, d := range opts.Mutator.DisableMutators { - pattern := strings.HasSuffix(d, "*") - - if (pattern && strings.HasPrefix(name, d[:len(d)-1])) || (!pattern && name == d) { - continue MUTATOR - } - } + if mutatorDisabled(name, opts.Mutator.DisableMutators) { + continue MUTATOR } console.Verbose(opts, "Enable mutator %q", name) diff --git a/cmd/go-mutesting/main_test.go b/cmd/go-mutesting/main_test.go index fc5fc61..d514e9f 100644 --- a/cmd/go-mutesting/main_test.go +++ b/cmd/go-mutesting/main_test.go @@ -167,3 +167,30 @@ func testMain(t *testing.T, root string, exec []string, expectedExitCode int, co assert.Equal(t, expectedExitCode, exitCode) assert.Contains(t, out, contains) } + +func TestMutatorDisabled(t *testing.T) { + for _, tc := range []struct { + name string + patterns []string + want bool + }{ + // exact match + {"branch/if", []string{"branch/if"}, true}, + {"branch/else", []string{"branch/if"}, false}, + // wildcard prefix + {"branch/if", []string{"branch*"}, true}, + {"branch/else", []string{"branch*"}, true}, + {"arithmetic/base", []string{"branch*"}, false}, + // The old bug: len(d)-2 on a 2-char pattern like "b*" produced an empty + // prefix "", which matched every mutator name. len(d)-1 correctly gives "b". + {"arithmetic/base", []string{"b*"}, false}, + {"branch/if", []string{"b*"}, true}, + // empty patterns + {"branch/if", []string{}, false}, + // multiple patterns, first miss second hit + {"arithmetic/base", []string{"branch*", "arithmetic*"}, true}, + } { + got := mutatorDisabled(tc.name, tc.patterns) + assert.Equal(t, tc.want, got, "mutatorDisabled(%q, %v)", tc.name, tc.patterns) + } +}