// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package textdiff_test import ( "fmt" "math/rand/v2" "runtime" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/textdiff" ) func TestDiff(t *testing.T) { t.Parallel() for _, test := range []struct { name string a, b string expected string }{ { name: "empty", a: "", b: "", expected: "", }, { name: "identical", a: "line 1\nline 2\nline 3\n", b: "line 1\nline 2\nline 3\n", expected: "", }, { name: "completely different", a: "line 1\nline 2\nline 3\n", b: "line A\nline B\nline C\n", expected: `--- a +++ b @@ -1,3 +1,3 @@ -line 1 -line 2 -line 3 +line A +line B +line C `, }, { name: "inserted line", a: "line 1\nline 3\n", b: "line 1\nline 2\nline 3\n", expected: `--- a +++ b @@ -1,2 +1,3 @@ line 1 +line 2 line 3 `, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() diff, err := textdiff.Diff(test.a, test.b) require.NoError(t, err) assert.Equal(t, test.expected, diff) }) } } func TestDiffWithCustomPaths(t *testing.T) { t.Parallel() diff, err := textdiff.DiffWithCustomPaths("line 1\nline 3\n", "line 1\nline 2\nline 3\n", "fileA.txt", "fileB.txt") require.NoError(t, err) assert.Equal(t, `--- fileA.txt +++ fileB.txt @@ -1,2 +1,3 @@ line 1 +line 2 line 3 `, diff) } func genRandomLines(n int) string { var sb strings.Builder for range n { fmt.Fprintf(&sb, "line %d\n", rand.Int()) } return sb.String() } func TestDiffMemoryBudge(t *testing.T) { // not parallel, we need to measure memory allocations linesA := genRandomLines(1000) linesB := genRandomLines(20000) linesC := genRandomLines(1000) for _, test := range []struct { name string a, b string memoryBudget uint64 }{ { name: "empty", a: "", b: "", memoryBudget: 1024, // 1KB }, { name: "large", a: linesA + linesB, b: linesB + linesC, memoryBudget: 3 * 1024 * 1024, // 3MB }, { name: "large 2", a: linesA + linesB, b: linesA + linesB + linesC, memoryBudget: 2 * 1024 * 1024, // 2MB }, { name: "completely different", a: linesA, b: linesC, memoryBudget: 512 * 1024, // 512KB }, { name: "large to empty", a: linesA + linesB + linesC, b: "", memoryBudget: 6 * 1024 * 1024, // 6MB }, { name: "empty to large", a: "", b: linesA + linesB + linesC, memoryBudget: 6 * 1024 * 1024, // 6MB }, } { t.Run(test.name, func(t *testing.T) { differ := textdiff.Diff allocs := MemoryAllocated(func() { _, err := differ(test.a, test.b) require.NoError(t, err) }) require.LessOrEqual(t, allocs, test.memoryBudget, "memory allocations exceeded the budget") }) } } func MemoryAllocated(f func()) uint64 { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) runtime.GC() // Measure the starting statistics var memstats runtime.MemStats runtime.ReadMemStats(&memstats) allocs := 0 - memstats.TotalAlloc f() // Read the final statistics runtime.ReadMemStats(&memstats) allocs += memstats.TotalAlloc return allocs } func FuzzDiff(f *testing.F) { f.Add("", "") f.Add("line 1\nline 2\n", "line 1\nline 2\n") f.Add("line 1\nline 2\n", "line 1\nline 2\nline 3\n") f.Fuzz(func(t *testing.T, a, b string) { _, err := textdiff.Diff(a, b) require.NoError(t, err) }) }