Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add goproxytest testing API, forward dirhash #272

Merged
merged 2 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 12 additions & 84 deletions dirhash/hash.go
Original file line number Diff line number Diff line change
@@ -1,103 +1,31 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package dirhash defines hashes over directory trees.
// Package dirhash is a thin forwarding layer on top of
// [golang.org/x/mod/sumdb/dirhash]. See that package for documentation.
//
// Deprecated: use [golang.org/x/mod/sumdb/dirhash] instead.
package dirhash

import (
"archive/zip"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"

"golang.org/x/mod/sumdb/dirhash"
)

var DefaultHash = Hash1
var DefaultHash = dirhash.Hash1

type Hash func(files []string, open func(string) (io.ReadCloser, error)) (string, error)
type Hash = dirhash.Hash

func Hash1(files []string, open func(string) (io.ReadCloser, error)) (string, error) {
h := sha256.New()
files = append([]string(nil), files...)
sort.Strings(files)
for _, file := range files {
if strings.Contains(file, "\n") {
return "", errors.New("filenames with newlines are not supported")
}
r, err := open(file)
if err != nil {
return "", err
}
hf := sha256.New()
_, err = io.Copy(hf, r)
r.Close()
if err != nil {
return "", err
}
fmt.Fprintf(h, "%x %s\n", hf.Sum(nil), file)
}
return "h1:" + base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
return dirhash.Hash1(files, open)
}

func HashDir(dir, prefix string, hash Hash) (string, error) {
files, err := DirFiles(dir, prefix)
if err != nil {
return "", err
}
osOpen := func(name string) (io.ReadCloser, error) {
return os.Open(filepath.Join(dir, strings.TrimPrefix(name, prefix)))
}
return hash(files, osOpen)
return dirhash.HashDir(dir, prefix, hash)
}

func DirFiles(dir, prefix string) ([]string, error) {
var files []string
dir = filepath.Clean(dir)
err := filepath.Walk(dir, func(file string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
rel := file
if dir != "." {
rel = file[len(dir)+1:]
}
f := filepath.Join(prefix, rel)
files = append(files, filepath.ToSlash(f))
return nil
})
if err != nil {
return nil, err
}
return files, nil
return dirhash.DirFiles(dir, prefix)
}

func HashZip(zipfile string, hash Hash) (string, error) {
z, err := zip.OpenReader(zipfile)
if err != nil {
return "", err
}
defer z.Close()
var files []string
zfiles := make(map[string]*zip.File)
for _, file := range z.File {
files = append(files, file.Name)
zfiles[file.Name] = file
}
zipOpen := func(name string) (io.ReadCloser, error) {
f := zfiles[name]
if f == nil {
return nil, fmt.Errorf("file %q not found in zip", name) // should never happen
}
return f.Open()
}
return hash(files, zipOpen)
return dirhash.HashZip(zipfile, hash)
}
134 changes: 0 additions & 134 deletions dirhash/hash_test.go

This file was deleted.

40 changes: 29 additions & 11 deletions goproxytest/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"os"
"path/filepath"
"strings"
"testing"

"golang.org/x/mod/module"
"golang.org/x/mod/semver"
Expand All @@ -45,26 +46,43 @@ type Server struct {
server *http.Server
URL string
dir string
logf func(string, ...any)
modList []module.Version
zipCache par.Cache
archiveCache par.Cache
}

// StartProxy starts the Go module proxy listening on the given
// NewTestServer is a wrapper around [NewServer] for use in Go tests.
// Failure to start the server stops the test via [testing.TB.Fatalf],
// all server logs go through [testing.TB.Logf],
// and the server is closed when the test finishes via [testing.TB.Cleanup].
func NewTestServer(tb testing.TB, dir, addr string) *Server {
srv, err := newServer(dir, addr, tb.Logf)
if err != nil {
tb.Fatalf("cannot start Go proxy: %v", err)
}
tb.Cleanup(srv.Close)
return srv
}

// NewServer starts the Go module proxy listening on the given
// network address. It serves modules taken from the given directory
// name. If addr is empty, it will listen on an arbitrary
// localhost port. If dir is empty, "testmod" will be used.
//
// The returned Server should be closed after use.
func NewServer(dir, addr string) (*Server, error) {
var srv Server
return newServer(dir, addr, log.Printf)
}

func newServer(dir, addr string, logf func(string, ...any)) (*Server, error) {
if addr == "" {
addr = "localhost:0"
}
if dir == "" {
dir = "testmod"
}
srv.dir = dir
srv := Server{dir: dir, logf: logf}
if err := srv.readModList(); err != nil {
return nil, fmt.Errorf("cannot read modules: %v", err)
}
Expand All @@ -79,7 +97,7 @@ func NewServer(dir, addr string) (*Server, error) {
srv.URL = "http://" + addr + "/mod"
go func() {
if err := srv.server.Serve(l); err != nil && err != http.ErrServerClosed {
log.Printf("go proxy: http.Serve: %v", err)
srv.logf("go proxy: http.Serve: %v", err)
}
}()
return &srv, nil
Expand Down Expand Up @@ -141,7 +159,7 @@ func (srv *Server) handler(w http.ResponseWriter, r *http.Request) {
enc, file := path[:i], path[i+len("/@v/"):]
path, err := module.UnescapePath(enc)
if err != nil {
fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err)
srv.logf("go proxy_test: %v\n", err)
http.NotFound(w, r)
return
}
Expand Down Expand Up @@ -169,7 +187,7 @@ func (srv *Server) handler(w http.ResponseWriter, r *http.Request) {
encVers, ext := file[:i], file[i+1:]
vers, err := module.UnescapeVersion(encVers)
if err != nil {
fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err)
srv.logf("go proxy_test: %v\n", err)
http.NotFound(w, r)
return
}
Expand Down Expand Up @@ -204,7 +222,7 @@ func (srv *Server) handler(w http.ResponseWriter, r *http.Request) {
// to resolve github.com, github.com/hello and github.com/hello/world.
// cmd/go expects a 404/410 response if there is nothing there. Hence we
// cannot return with a 500.
fmt.Fprintf(os.Stderr, "go proxy: no archive %s %s\n", path, vers)
srv.logf("go proxy: no archive %s %s\n", path, vers)
http.NotFound(w, r)
return
}
Expand Down Expand Up @@ -246,7 +264,7 @@ func (srv *Server) handler(w http.ResponseWriter, r *http.Request) {
}).(cached)

if c.err != nil {
fmt.Fprintf(os.Stderr, "go proxy: %v\n", c.err)
srv.logf("go proxy: %v\n", c.err)
http.Error(w, c.err.Error(), 500)
return
}
Expand Down Expand Up @@ -277,12 +295,12 @@ func (srv *Server) findHash(m module.Version) string {
func (srv *Server) readArchive(path, vers string) *txtar.Archive {
enc, err := module.EscapePath(path)
if err != nil {
fmt.Fprintf(os.Stderr, "go proxy: %v\n", err)
srv.logf("go proxy: %v\n", err)
return nil
}
encVers, err := module.EscapeVersion(vers)
if err != nil {
fmt.Fprintf(os.Stderr, "go proxy: %v\n", err)
srv.logf("go proxy: %v\n", err)
return nil
}

Expand Down Expand Up @@ -324,7 +342,7 @@ func (srv *Server) readArchive(path, vers string) *txtar.Archive {
}
if err != nil {
if !os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "go proxy: %v\n", err)
srv.logf("go proxy: %v\n", err)
}
a = nil
}
Expand Down
5 changes: 1 addition & 4 deletions goproxytest/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ import (
)

func TestScripts(t *testing.T) {
srv, err := goproxytest.NewServer(filepath.Join("testdata", "mod"), "")
if err != nil {
t.Fatalf("cannot start proxy: %v", err)
}
srv := goproxytest.NewTestServer(t, filepath.Join("testdata", "mod"), "")
p := testscript.Params{
Dir: "testdata",
Setup: func(e *testscript.Env) error {
Expand Down
Loading