Skip to content

Commit 8f33f39

Browse files
aledbfclaude
andcommitted
test: hold the edits mutation testing found in the reference ports
mutate without Docker over this branch's diff left edits no test refused. A win32 basename keeps `{:foo` whole, as node's path.win32.basename does ('{' lowercases past 'z'); oracletest names the JSON error of a bad recorded or Go result; a case's subtest name takes the first of its name and fn#index. Dockerfile drops the escape token nothing reads since base images resolve as TS does, finalStageName leans on instructions.Parse returning no stages with its error, and the two index edits that cannot change a match are exempt. stageLabel loses a check that could not fail: on every FROM line BuildKit accepts, its continuations joined, the FROM-line expression reads BuildKit's label. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 834bf15 commit 8f33f39

5 files changed

Lines changed: 26 additions & 18 deletions

File tree

‎internal/config/varsub_test.go‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,10 @@ func TestComputeDevContainerIDIsPaddedTo52Digits(t *testing.T) {
137137
}
138138
}
139139

140-
// Only a letter followed by a colon is a drive (node path.win32.basename).
140+
// Only a letter followed by a colon is a drive (node path.win32.basename):
141+
// '{' lowercases past 'z', so `{:foo` keeps its colon.
141142
func TestWinBasenameDrive(t *testing.T) {
142-
for in, want := range map[string]string{`C:foo`: "foo", `c:\a\b\`: "b", `1:foo`: "1:foo", `C:`: ""} {
143+
for in, want := range map[string]string{`C:foo`: "foo", `c:\a\b\`: "b", `1:foo`: "1:foo", `C:`: "", `{:foo`: "{:foo", "@:foo": "@:foo"} {
143144
if got := winBasename(in); got != want {
144145
t.Errorf("winBasename(%q) = %q, want %q", in, got, want)
145146
}

‎internal/docker/dockerfile.go‎

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ type Dockerfile struct {
1515
Preamble Preamble
1616
Stages []Stage
1717
StagesByLabel map[string]*Stage
18-
escapeToken rune
1918
}
2019

2120
// Preamble is the content before any FROM statement.
@@ -53,14 +52,12 @@ func ExtractDockerfile(content string) *Dockerfile {
5352
df := &Dockerfile{
5453
Preamble: Preamble{Directives: map[string]string{}},
5554
StagesByLabel: map[string]*Stage{},
56-
escapeToken: '\\',
5755
}
5856

5957
res, err := parser.Parse(strings.NewReader(content))
6058
if err != nil {
6159
return df
6260
}
63-
df.escapeToken = res.EscapeToken
6461
if syntax, _, _, ok := parser.DetectSyntax([]byte(content)); ok {
6562
df.Preamble.Directives["syntax"] = syntax
6663
df.Preamble.Version = syntaxVersion(syntax)
@@ -111,9 +108,11 @@ func ExtractDockerfile(content string) *Dockerfile {
111108
}
112109

113110
// stageLabel is the stage's name as the FROM line spells it: BuildKit
114-
// lowercases it, TS extractDockerfile keeps it, and looks stages up by it.
111+
// lowercases it, TS extractDockerfile keeps it, and looks stages up by it. On
112+
// a FROM line BuildKit accepts (continuations joined), the FROM-line
113+
// expression reads the label BuildKit names.
115114
func stageLabel(s *instructions.Stage) string {
116-
if m := fromLineLabel.FindStringSubmatch(s.SourceCode); m != nil && strings.EqualFold(m[1], s.Name) {
115+
if m := fromLineLabel.FindStringSubmatch(s.SourceCode); m != nil {
117116
return m[1]
118117
}
119118
return s.Name
@@ -202,7 +201,7 @@ var (
202201
// backslash, which no builder accepts; this reads the stage's name with
203202
// BuildKit instead when it has one.
204203
func EnsureFinalStageName(content, defaultName string) (stageName string, modifiedContent string, err error) {
205-
lines := findFromLines.FindAllStringIndex(content, -1)
204+
lines := findFromLines.FindAllStringIndex(content, -1) // mutate-exempt: any n < 0 is every match
206205
if len(lines) == 0 {
207206
return "", "", errors.New("Error parsing Dockerfile: Dockerfile contains no FROM instructions")
208207
}
@@ -216,7 +215,7 @@ func EnsureFinalStageName(content, defaultName string) (stageName string, modifi
216215
if m == nil {
217216
return "", "", errors.New("Error parsing Dockerfile: failed to parse final FROM line")
218217
}
219-
if m[2] != -1 { // the label group, when it matched, is never empty
218+
if m[2] != -1 { // mutate-exempt: m[2] and m[3] are both -1 exactly when the label group is unmatched, and a matched label is never empty
220219
return content[lineStart+m[2] : lineStart+m[3]], "", nil
221220
}
222221
pos := lineStart + m[1]
@@ -230,9 +229,9 @@ func finalStageName(content string) string {
230229
if err != nil {
231230
return ""
232231
}
233-
stages, _, err := instructions.Parse(res.AST, nil)
234-
if err != nil || len(stages) == 0 {
235-
return ""
232+
// instructions.Parse returns no stages with its error.
233+
if stages, _, _ := instructions.Parse(res.AST, nil); len(stages) > 0 {
234+
return stages[len(stages)-1].Name
236235
}
237-
return stages[len(stages)-1].Name
236+
return ""
238237
}

‎internal/docker/dockerfile_held_test.go‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,12 @@ func TestStageLabelsReadsTheNamedStage(t *testing.T) {
5050
t.Errorf("StageLabels(\"\")[k] = %q, want final", got)
5151
}
5252
}
53+
54+
// BuildKit joins a FROM continued onto the next line before the FROM-line
55+
// expression reads its label, so the stage keeps its name.
56+
func TestExtractDockerfileLabelsAContinuedFrom(t *testing.T) {
57+
df := ExtractDockerfile("FROM alpine \\\n AS base\nRUN true\n")
58+
if len(df.Stages) != 1 || df.Stages[0].From.Label != "base" || df.StagesByLabel["base"] == nil {
59+
t.Errorf("stages = %+v, want one labelled base", df.Stages)
60+
}
61+
}

‎internal/tsoracle/oracletest/oracletest.go‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package oracletest
44

55
import (
66
"bytes"
7+
"cmp"
78
"encoding/json"
89
"errors"
910
"fmt"
@@ -68,11 +69,7 @@ func Run(t *testing.T, path string, impls map[string]Func) {
6869
if impl == nil {
6970
continue
7071
}
71-
name := c.Name
72-
if name == "" {
73-
name = c.Fn + "#" + strconv.Itoa(i)
74-
}
75-
t.Run(name, func(t *testing.T) {
72+
t.Run(cmp.Or(c.Name, c.Fn+"#"+strconv.Itoa(i)), func(t *testing.T) {
7673
if c.Result == nil && c.Error == "" {
7774
t.Fatal("no recorded result: run `go tool task testdata:ts-oracle`")
7875
}

‎internal/tsoracle/oracletest/oracletest_test.go‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ func TestCheck(t *testing.T) {
4848
{"falsy false", returns(Truthiness(false), nil), call("f", `false`, ""), ""},
4949
{"truthiness differs", returns(Truthiness(true), nil), call("f", `0`, ""), "truthy: false"},
5050
{"truthy object", returns(Truthiness(true), nil), call("f", `{}`, ""), ""},
51+
{"bad recorded result", returns(1, nil), call("f", `{`, ""), "bad recorded result: unexpected end of JSON input"},
52+
{"Go result is no JSON", returns(func() {}, nil), call("f", `1`, ""), "Go result is no JSON: invalid character"},
5153
}
5254
for _, tc := range tests {
5355
t.Run(tc.name, func(t *testing.T) {

0 commit comments

Comments
 (0)