Skip to content

Commit ee6a499

Browse files
authored
feat(migration): aligned migration tool to support latest additions (#106)
Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
1 parent dd05b22 commit ee6a499

5 files changed

Lines changed: 201 additions & 4 deletions

File tree

hack/migrate-testify/generics.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ func trySimpleUpgrade(
246246
result = checkSingleConstraint(argTypes, constraintBoolean, skipNotBoolean)
247247
case "Same", "NotSame":
248248
result = checkPairConstraint(argTypes, constraintPointer, skipNotPointer, rule)
249-
case "ElementsMatch", "Subset":
249+
case "ElementsMatch", "NotElementsMatch", "Subset", "NotSubset":
250250
result = checkSlicePairConstraint(argTypes, rule)
251251
case "IsIncreasing", "IsDecreasing", "IsNonIncreasing", "IsNonDecreasing":
252252
result = checkOrderedSliceConstraint(argTypes)

hack/migrate-testify/generics_test.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,90 @@ func TestBool(t *testing.T) {
220220
}`,
221221
expected: "assert.TrueT(t, true)\n\tassert.FalseT(t, false)",
222222
},
223+
{
224+
name: "elementsmatch slice upgrade",
225+
input: `package p
226+
import (
227+
"testing"
228+
"github.com/go-openapi/testify/v2/assert"
229+
)
230+
func TestElementsMatch(t *testing.T) {
231+
assert.ElementsMatch(t, []int{1, 2, 3}, []int{3, 2, 1})
232+
}`,
233+
expected: `assert.ElementsMatchT(t, []int{1, 2, 3}, []int{3, 2, 1})`,
234+
},
235+
{
236+
name: "notelementsmatch slice upgrade",
237+
input: `package p
238+
import (
239+
"testing"
240+
"github.com/go-openapi/testify/v2/assert"
241+
)
242+
func TestNotElementsMatch(t *testing.T) {
243+
assert.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4})
244+
}`,
245+
expected: `assert.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4})`,
246+
},
247+
{
248+
name: "notelementsmatchf slice format variant",
249+
input: `package p
250+
import (
251+
"testing"
252+
"github.com/go-openapi/testify/v2/assert"
253+
)
254+
func TestNotElementsMatchf(t *testing.T) {
255+
assert.NotElementsMatchf(t, []int{1, 2, 3}, []int{1, 2, 4}, "should differ")
256+
}`,
257+
expected: `assert.NotElementsMatchTf(t, []int{1, 2, 3}, []int{1, 2, 4}, "should differ")`,
258+
},
259+
{
260+
name: "subset slice upgrade",
261+
input: `package p
262+
import (
263+
"testing"
264+
"github.com/go-openapi/testify/v2/assert"
265+
)
266+
func TestSubset(t *testing.T) {
267+
assert.Subset(t, []int{1, 2, 3}, []int{1, 2})
268+
}`,
269+
expected: `assert.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2})`,
270+
},
271+
{
272+
name: "notsubset slice upgrade",
273+
input: `package p
274+
import (
275+
"testing"
276+
"github.com/go-openapi/testify/v2/assert"
277+
)
278+
func TestNotSubset(t *testing.T) {
279+
assert.NotSubset(t, []int{1, 2, 3}, []int{4, 5})
280+
}`,
281+
expected: `assert.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5})`,
282+
},
283+
{
284+
name: "notsubsetf slice format variant",
285+
input: `package p
286+
import (
287+
"testing"
288+
"github.com/go-openapi/testify/v2/assert"
289+
)
290+
func TestNotSubsetf(t *testing.T) {
291+
assert.NotSubsetf(t, []int{1, 2, 3}, []int{4, 5}, "should not contain")
292+
}`,
293+
expected: `assert.SliceNotSubsetTf(t, []int{1, 2, 3}, []int{4, 5}, "should not contain")`,
294+
},
295+
{
296+
name: "skip notsubset map (not a slice)",
297+
input: `package p
298+
import (
299+
"testing"
300+
"github.com/go-openapi/testify/v2/assert"
301+
)
302+
func TestNotSubsetMap(t *testing.T) {
303+
assert.NotSubset(t, map[string]int{"a": 1}, map[string]int{"b": 2})
304+
}`,
305+
expected: `assert.NotSubset(t, map[string]int{"a": 1}, map[string]int{"b": 2})`,
306+
},
223307
})
224308
}
225309

@@ -402,6 +486,88 @@ func TestSkipDifferent(t *testing.T) {
402486
}
403487
}
404488

489+
func TestGenericUpgradeManualReview(t *testing.T) {
490+
t.Parallel()
491+
492+
// IsType and IsNotType both change argument count when upgraded to the
493+
// generic form (IsOfTypeT / IsNotOfTypeT take the expected type as a
494+
// type parameter). The tool must not rewrite the call; it must emit a
495+
// manual-review warning.
496+
cases := []struct {
497+
name string
498+
source string
499+
wantTarget string
500+
}{
501+
{
502+
name: "IsType",
503+
source: `package p
504+
import (
505+
"testing"
506+
"github.com/go-openapi/testify/v2/assert"
507+
)
508+
type MyType struct{}
509+
func TestIsType(t *testing.T) {
510+
assert.IsType(t, &MyType{}, &MyType{})
511+
}`,
512+
wantTarget: "IsOfTypeT",
513+
},
514+
{
515+
name: "IsNotType",
516+
source: `package p
517+
import (
518+
"testing"
519+
"github.com/go-openapi/testify/v2/assert"
520+
)
521+
type MyType struct{}
522+
func TestIsNotType(t *testing.T) {
523+
assert.IsNotType(t, &MyType{}, 42)
524+
}`,
525+
wantTarget: "IsNotOfTypeT",
526+
},
527+
}
528+
529+
for _, tc := range cases {
530+
t.Run(tc.name, func(t *testing.T) {
531+
t.Parallel()
532+
533+
fset := token.NewFileSet()
534+
f, err := parser.ParseFile(fset, "test.go", tc.source, parser.ParseComments)
535+
if err != nil {
536+
t.Fatalf("parse: %v", err)
537+
}
538+
539+
info := typeCheckWithMockAssert(t, fset, f)
540+
if info == nil {
541+
t.Fatal("type-check failed")
542+
}
543+
544+
pkg := &packages.Package{
545+
TypesInfo: info,
546+
Syntax: []*ast.File{f},
547+
GoFiles: []string{"test.go"},
548+
}
549+
550+
rpt := &report{}
551+
changes := upgradeFile(f, pkg, fset, rpt, "test.go", false)
552+
553+
if changes != 0 {
554+
t.Errorf("expected 0 changes (manual review), got %d", changes)
555+
}
556+
557+
found := false
558+
for _, d := range rpt.diagnostics {
559+
if d.kind == "warning" && strings.Contains(d.message, tc.wantTarget) && strings.Contains(d.message, "manual review") {
560+
found = true
561+
break
562+
}
563+
}
564+
if !found {
565+
t.Errorf("expected manual-review warning mentioning %s, got diagnostics: %v", tc.wantTarget, rpt.diagnostics)
566+
}
567+
})
568+
}
569+
}
570+
405571
// typeCheckWithMockAssert creates a mock "assert" package that has a few functions
406572
// taking (testing.T, any...) and type-checks the file against it.
407573
func typeCheckWithMockAssert(t *testing.T, fset *token.FileSet, f *ast.File) *types.Info {
@@ -472,6 +638,20 @@ func typeCheckWithMockAssert(t *testing.T, fset *token.FileSet, f *ast.File) *ty
472638
makeAssertFunc("TrueT", anyParam("value"))
473639
makeAssertFunc("False", anyParam("value"))
474640
makeAssertFunc("FalseT", anyParam("value"))
641+
makeAssertFunc("ElementsMatch", anyParam("listA"), anyParam("listB"))
642+
makeAssertFunc("ElementsMatchT", anyParam("listA"), anyParam("listB"))
643+
makeAssertFunc("NotElementsMatch", anyParam("listA"), anyParam("listB"))
644+
makeAssertFunc("NotElementsMatchT", anyParam("listA"), anyParam("listB"))
645+
makeAssertFunc("NotElementsMatchf", anyParam("listA"), anyParam("listB"))
646+
makeAssertFunc("NotElementsMatchTf", anyParam("listA"), anyParam("listB"))
647+
makeAssertFunc("Subset", anyParam("list"), anyParam("subset"))
648+
makeAssertFunc("SliceSubsetT", anyParam("list"), anyParam("subset"))
649+
makeAssertFunc("NotSubset", anyParam("list"), anyParam("subset"))
650+
makeAssertFunc("NotSubsetf", anyParam("list"), anyParam("subset"))
651+
makeAssertFunc("SliceNotSubsetT", anyParam("list"), anyParam("subset"))
652+
makeAssertFunc("SliceNotSubsetTf", anyParam("list"), anyParam("subset"))
653+
makeAssertFunc("IsType", anyParam("expectedType"), anyParam("object"))
654+
makeAssertFunc("IsNotType", anyParam("theType"), anyParam("object"))
475655

476656
assertPkg.MarkComplete()
477657

hack/migrate-testify/migrate_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func runMigrateSubtest(t *testing.T, c migrateTestCase) {
115115
if c.warnContains != "" {
116116
found := false
117117
for _, d := range rpt.diagnostics {
118-
if d.kind == "warning" && strings.Contains(d.message, c.warnContains) {
118+
if d.kind == warn && strings.Contains(d.message, c.warnContains) {
119119
found = true
120120
break
121121
}

hack/migrate-testify/rename_map.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,21 @@ var genericUpgrades = map[string]upgradeRule{ //nolint:gochecknoglobals // looku
175175
argConstraints: []constraintKind{constraintComparable},
176176
sameType: true,
177177
},
178+
"NotElementsMatch": {
179+
target: "NotElementsMatchT",
180+
argConstraints: []constraintKind{constraintComparable},
181+
sameType: true,
182+
},
178183
"Subset": {
179184
target: "SliceSubsetT",
180185
argConstraints: []constraintKind{constraintComparable},
181186
sameType: true,
182187
},
188+
"NotSubset": {
189+
target: "SliceNotSubsetT",
190+
argConstraints: []constraintKind{constraintComparable},
191+
sameType: true,
192+
},
183193

184194
// Ordering (slice)
185195
"IsIncreasing": {
@@ -239,6 +249,11 @@ var genericUpgrades = map[string]upgradeRule{ //nolint:gochecknoglobals // looku
239249
manualReview: true,
240250
argConstraints: []constraintKind{},
241251
},
252+
"IsNotType": {
253+
target: "IsNotOfTypeT",
254+
manualReview: true,
255+
argConstraints: []constraintKind{},
256+
},
242257

243258
// Boolean
244259
"True": {

hack/migrate-testify/report.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"strings"
1515
)
1616

17+
const warn = "warning"
18+
1719
// diagnostic represents a single warning or info message from the migration tool.
1820
type diagnostic struct {
1921
file string
@@ -44,7 +46,7 @@ type report struct {
4446
}
4547

4648
func (r *report) warn(file string, line int, msg string) {
47-
r.diagnostics = append(r.diagnostics, diagnostic{file: file, line: line, message: msg, kind: "warning"})
49+
r.diagnostics = append(r.diagnostics, diagnostic{file: file, line: line, message: msg, kind: warn})
4850
}
4951

5052
func (r *report) info(file string, line int, msg string) {
@@ -115,7 +117,7 @@ func (r *report) printPass1Summary() {
115117

116118
warnings := 0
117119
for _, d := range r.diagnostics {
118-
if d.kind == "warning" {
120+
if d.kind == warn {
119121
warnings++
120122
}
121123
}

0 commit comments

Comments
 (0)