From 16c48dc046f4476c3e2604b16791e10c77f84c12 Mon Sep 17 00:00:00 2001 From: Laura Date: Wed, 13 Aug 2025 01:21:20 +0200 Subject: [PATCH] temp --- go.mod | 6 +- go.sum | 6 +- integration/integration_test.go | 209 +++++++++++++++----------------- 3 files changed, 107 insertions(+), 114 deletions(-) diff --git a/go.mod b/go.mod index 87b68cd..62aae68 100644 --- a/go.mod +++ b/go.mod @@ -21,19 +21,17 @@ require ( github.com/spakin/netpbm v1.3.2 github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef - github.com/stretchr/testify v1.10.0 github.com/xyproto/xbm v1.0.0 golang.org/x/image v0.30.0 + gotest.tools/v3 v3.5.2 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect github.com/ebitengine/purego v0.8.4 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sergeymakinen/go-bmp v1.0.0 // indirect github.com/tetratelabs/wazero v1.9.0 // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/text v0.28.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ec1c106..c23fb08 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/gen2brain/jpegxl v0.4.5 h1:TWpVEn5xkIfsswzkjHBArd0Cc9AE0tbjBSoa0jDsrb github.com/gen2brain/jpegxl v0.4.5/go.mod h1:4kWYJ18xCEuO2vzocYdGpeqNJ990/Gjy3uLMg5TBN6I= github.com/gen2brain/webp v0.5.5 h1:MvQR75yIPU/9nSqYT5h13k4URaJK3gf9tgz/ksRbyEg= github.com/gen2brain/webp v0.5.5/go.mod h1:xOSMzp4aROt2KFW++9qcK/RBTOVC2S9tJG66ip/9Oc0= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/hullerob/go.farbfeld v0.0.0-20181222022525-3661193c725f h1:1LkiAnH6RhOEbQAcfcEcixM5IsegqFi6IH0Nz0ZGqYs= @@ -56,7 +58,7 @@ golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= diff --git a/integration/integration_test.go b/integration/integration_test.go index ec28a5b..02f5de3 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -1,21 +1,19 @@ +// integration/integration_test.go package integration import ( - "errors" "fmt" "image/jpeg" - "io/fs" "os" - "os/exec" "path/filepath" "runtime" "strings" "testing" - "github.com/stretchr/testify/require" + "gotest.tools/v3/icmd" ) -// skip encoding tests for these extensions +// Skip encoding tests for these extensions (same semantics as your original) var encodeOnly = map[string]bool{ "heic": true, "heif": true, @@ -23,135 +21,130 @@ var encodeOnly = map[string]bool{ "svg": true, } -func TestFFWebP(t *testing.T) { - // build a fresh executable with "full" tags - executable, err := buildExecutable() - require.NoError(t, err) +const ( + fixturesDir = "images" // integration/images/ + exampleJPEG = "example.jpeg" // integration/example.jpeg (256x256) +) - defer os.Remove(executable) +var ffwebpBin string // set in TestMain - // resolve all test files - files, err := listFiles("images") - require.NoError(t, err) +func TestMain(m *testing.M) { + // Build the CLI once with -tags=full into a temp dir, reusing it across tests. + tmp, err := os.MkdirTemp("", "ffwebp-bin-*") + if err != nil { + fmt.Fprintln(os.Stderr, "mktemp:", err) + os.Exit(1) + } + defer os.RemoveAll(tmp) - // test all extensions - for _, path := range files { - ext := strings.TrimLeft(filepath.Ext(path), ".") + exe := "ffwebp" + if runtime.GOOS == "windows" { + exe += ".exe" + } + ffwebpBin = filepath.Join(tmp, exe) - decoded := "decoded.jpeg" - encoded := fmt.Sprintf("encoded.%s", ext) + root, err := findRepoRoot() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } - // test if we can convert from the codec to jpeg - t.Run(fmt.Sprintf("decode %s", ext), func(t *testing.T) { - defer os.Remove(decoded) + // Build from repo root so relative paths to ./cmd/ffwebp resolve. + build := icmd.Command("go", "build", "-tags=full", "-o", ffwebpBin, "./cmd/ffwebp") + build.Dir = root + res := icmd.RunCmd(build) + if res.ExitCode != 0 { + fmt.Fprintln(os.Stderr, res.Combined()) + os.Exit(res.ExitCode) + } - err = runCommand(executable, "-i", path, "-o", decoded) - require.NoError(t, err) + os.Exit(m.Run()) +} - err = validateJPEG(decoded, 0) - require.NoError(t, err) +func TestCLI_CodecDecodeAndRoundTrip(t *testing.T) { + // Discover input samples: integration/images/image. + entries, err := os.ReadDir(fixturesDir) + if err != nil { + t.Fatalf("read %s: %v", fixturesDir, err) + } + + for _, e := range entries { + if e.IsDir() { + continue + } + inPath := filepath.Join(fixturesDir, e.Name()) + ext := strings.TrimPrefix(filepath.Ext(e.Name()), ".") + + // 1) -> jpeg + t.Run("decode_"+ext, func(t *testing.T) { + tmp := t.TempDir() + out := filepath.Join(tmp, "decoded.jpeg") + + run(t, icmd.Command(ffwebpBin, "-s", "-i", inPath, "-o", out)) + + w, h := jpegSize(t, out) + if w == 0 || h == 0 { + t.Fatalf("decoded JPEG has invalid size: %dx%d", w, h) + } }) + // 2) jpeg -> -> jpeg (dimension stays 256x256), unless encodeOnly if encodeOnly[ext] { continue } - // test if we can convert from jpeg to the codec and then back to jpeg - t.Run(fmt.Sprintf("encode %s", ext), func(t *testing.T) { - defer os.Remove(encoded) - defer os.Remove(decoded) + t.Run("encode_"+ext, func(t *testing.T) { + tmp := t.TempDir() + mid := filepath.Join(tmp, "encoded."+ext) + back := filepath.Join(tmp, "roundtrip.jpeg") - err = runCommand(executable, "-i", "example.jpeg", "-o", encoded) - require.NoError(t, err) + run(t, icmd.Command(ffwebpBin, "-s", "-i", exampleJPEG, "-o", mid)) + run(t, icmd.Command(ffwebpBin, "-s", "-i", mid, "-o", back)) - err = runCommand(executable, "-i", encoded, "-o", decoded) - require.NoError(t, err) - - err = validateJPEG(decoded, 256) - require.NoError(t, err) + w, h := jpegSize(t, back) + if w != 256 || h != 256 { + t.Fatalf("roundtrip dimension mismatch: got %dx%d, want 256x256", w, h) + } }) } } -func buildExecutable() (string, error) { - if runtime.GOOS == "windows" { - err := runCommand("go", "build", "-tags", "full", "-o", "ffwebp.exe", "..\\cmd\\ffwebp") - if err != nil { - return "", err - } +func run(t *testing.T, cmd icmd.Cmd) { + t.Helper() + res := icmd.RunCmd(cmd) + // On failure, icmd prints the command line + stdout/stderr, which is handy. + res.Assert(t, icmd.Success) +} - return "./ffwebp.exe", nil +func jpegSize(t *testing.T, path string) (int, int) { + t.Helper() + f, err := os.Open(path) + if err != nil { + t.Fatalf("open %s: %v", path, err) } + defer f.Close() - err := runCommand("go", "build", "-tags", "full", "-o", "ffwebp", "../cmd/ffwebp") + cfg, err := jpeg.DecodeConfig(f) + if err != nil { + t.Fatalf("decode jpeg config %s: %v", path, err) + } + return cfg.Width, cfg.Height +} + +func findRepoRoot() (string, error) { + // Walk up to find go.mod so we can run `go build ./cmd/ffwebp` reliably. + dir, err := os.Getwd() if err != nil { return "", err } - - err = runCommand("chmod", "+x", "ffwebp") - if err != nil { - return "", err - } - - return "./ffwebp", nil -} - -func listFiles(directory string) ([]string, error) { - var files []string - - err := filepath.Walk(directory, func(path string, info fs.FileInfo, err error) error { - if err != nil || info.IsDir() { - return err + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + return dir, nil } - - files = append(files, path) - - return nil - }) - if err != nil { - return nil, err - } - - return files, nil -} - -func runCommand(command string, args ...string) error { - cmd := exec.Command(command, args...) - - out, err := cmd.CombinedOutput() - if err != nil { - if len(out) > 0 { - return errors.New(string(out)) + parent := filepath.Dir(dir) + if parent == dir { + return "", fmt.Errorf("could not locate go.mod starting from %s", dir) } - - return err + dir = parent } - - return nil -} - -func validateJPEG(path string, requireSize int) error { - file, err := os.OpenFile(path, os.O_RDONLY, 0) - if err != nil { - return err - } - - defer file.Close() - - img, err := jpeg.Decode(file) - if err != nil { - return err - } - - bounds := img.Bounds() - - if bounds.Dx() == 0 || bounds.Dy() == 0 { - return fmt.Errorf("invalid dimensions: %dx%d", bounds.Dx(), bounds.Dy()) - } - - if requireSize != 0 && (bounds.Dx() != requireSize || bounds.Dy() != requireSize) { - return fmt.Errorf("mismatched size: %dx%dx", bounds.Dx(), bounds.Dy()) - } - - return nil }