mirror of https://github.com/golang/go.git
cmd/go: delete internal packages moved to x/mod
This change deletes several internal packages, replaces imports to them with the equivalent golang.org/x/mod packages, updates x/mod, and re-runs 'go mod vendor'. Packages are replaced as follows: cmd/go/internal/modfile → golang.org/x/mod/modfile cmd/go/internal/module → golang.org/x/mod/module cmd/go/internal/semver → golang.org/x/mod/semver cmd/go/internal/sumdb → golang.org/x/mod/sumdb cmd/go/internal/dirhash → golang.org/x/mod/sumdb/dirhash cmd/go/internal/note → golang.org/x/mod/sumdb/note cmd/go/internal/tlog → golang.org/x/mod/sumdb/tlog Updates #31761 Fixes #34924 Change-Id: Ie3bf677bb0be49af969f654a0214243a6547eb57 Reviewed-on: https://go-review.googlesource.com/c/go/+/202698 Run-TryBot: Jay Conrod <jayconrod@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
parent
cc47b0d2cd
commit
1fb7d5472e
|
|
@ -6,7 +6,8 @@ require (
|
|||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44 // indirect
|
||||
golang.org/x/arch v0.0.0-20190815191158-8a70ba74b3a1
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
||||
golang.org/x/mod v0.1.1-0.20191029194233-18c3998b6452
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect
|
||||
golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,15 @@ github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44/go.mod h1:
|
|||
golang.org/x/arch v0.0.0-20190815191158-8a70ba74b3a1 h1:A71BZbKSu+DtCNry/x5JKn20C+64DirDHmePEA8k0FY=
|
||||
golang.org/x/arch v0.0.0-20190815191158-8a70ba74b3a1/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191029194233-18c3998b6452 h1:ES2W0A+AqNBCkgWga22gheu9IUDjq3TDmeCdY1A7jhk=
|
||||
golang.org/x/mod v0.1.1-0.20191029194233-18c3998b6452/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w=
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
|
|
@ -17,4 +21,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|||
golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5 h1:TFUhCYbgGMOGnRxJv+j0iAcxCjk8oGjXXWNejQBhUUs=
|
||||
golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
// 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
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func h(s string) string {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
|
||||
}
|
||||
|
||||
func htop(k string, s string) string {
|
||||
sum := sha256.Sum256([]byte(s))
|
||||
return k + ":" + base64.StdEncoding.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func TestHash1(t *testing.T) {
|
||||
files := []string{"xyz", "abc"}
|
||||
open := func(name string) (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(strings.NewReader("data for " + name)), nil
|
||||
}
|
||||
want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "abc", h("data for xyz"), "xyz"))
|
||||
out, err := Hash1(files, open)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out != want {
|
||||
t.Errorf("Hash1(...) = %s, want %s", out, want)
|
||||
}
|
||||
|
||||
_, err = Hash1([]string{"xyz", "a\nbc"}, open)
|
||||
if err == nil {
|
||||
t.Error("Hash1: expected error on newline in filenames")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashDir(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "dirhash-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz"))
|
||||
out, err := HashDir(dir, "prefix", Hash1)
|
||||
if err != nil {
|
||||
t.Fatalf("HashDir: %v", err)
|
||||
}
|
||||
if out != want {
|
||||
t.Errorf("HashDir(...) = %s, want %s", out, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashZip(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "dirhash-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
defer f.Close()
|
||||
|
||||
z := zip.NewWriter(f)
|
||||
w, err := z.Create("prefix/xyz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w.Write([]byte("data for xyz"))
|
||||
w, err = z.Create("prefix/abc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w.Write([]byte("data for abc"))
|
||||
if err := z.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz"))
|
||||
out, err := HashZip(f.Name(), Hash1)
|
||||
if err != nil {
|
||||
t.Fatalf("HashDir: %v", err)
|
||||
}
|
||||
if out != want {
|
||||
t.Errorf("HashDir(...) = %s, want %s", out, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirFiles(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "dirfiles-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Mkdir(filepath.Join(dir, "subdir"), 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "subdir", "xyz"), []byte("data for subdir xyz"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prefix := "foo/bar@v2.3.4"
|
||||
out, err := DirFiles(dir, prefix)
|
||||
if err != nil {
|
||||
t.Fatalf("DirFiles: %v", err)
|
||||
}
|
||||
for _, file := range out {
|
||||
if !strings.HasPrefix(file, prefix) {
|
||||
t.Errorf("Dir file = %s, want prefix %s", file, prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ import (
|
|||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// The following functions are copied verbatim from cmd/go/internal/module/module.go,
|
||||
// The following functions are copied verbatim from golang.org/x/mod/module/module.go,
|
||||
// with a change to additionally reject Windows short-names,
|
||||
// and one to accept arbitrary letters (golang.org/issue/29101).
|
||||
//
|
||||
|
|
|
|||
|
|
@ -5,16 +5,17 @@
|
|||
package modcmd
|
||||
|
||||
import (
|
||||
"cmd/go/internal/cfg"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/work"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
var cmdDownload = &base.Command{
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@ import (
|
|||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/work"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
var cmdEdit = &base.Command{
|
||||
|
|
|
|||
|
|
@ -8,15 +8,16 @@ package modcmd
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"cmd/go/internal/cfg"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/work"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
var cmdGraph = &base.Command{
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ import (
|
|||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/work"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
var cmdTidy = &base.Command{
|
||||
|
|
|
|||
|
|
@ -18,9 +18,10 @@ import (
|
|||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/imports"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/work"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var cmdVendor = &base.Command{
|
||||
|
|
|
|||
|
|
@ -6,17 +6,18 @@ package modcmd
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/go/internal/cfg"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/dirhash"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/work"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/sumdb/dirhash"
|
||||
)
|
||||
|
||||
var cmdVerify = &base.Command{
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@
|
|||
package modcmd
|
||||
|
||||
import (
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/work"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/work"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
var cmdWhy = &base.Command{
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ import (
|
|||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/semver"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// ConvertLegacyConfig converts legacy config to modfile.
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ import (
|
|||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/semver"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
func ParseGopkgLock(file string, data []byte) (*modfile.File, error) {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ package modconv
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func ParseGlideLock(file string, data []byte) (*modfile.File, error) {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ package modconv
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func ParseGLOCKFILE(file string, data []byte) (*modfile.File, error) {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ package modconv
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func ParseGodepsJSON(file string, data []byte) (*modfile.File, error) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package modconv
|
||||
|
||||
import "cmd/go/internal/modfile"
|
||||
import "golang.org/x/mod/modfile"
|
||||
|
||||
var Converters = map[string]func(string, []byte) (*modfile.File, error){
|
||||
"GLOCKFILE": ParseGLOCKFILE,
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ package modconv
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func ParseDependenciesTSV(file string, data []byte) (*modfile.File, error) {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ package modconv
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func ParseVendorConf(file string, data []byte) (*modfile.File, error) {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ package modconv
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func ParseVendorJSON(file string, data []byte) (*modfile.File, error) {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ package modconv
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func ParseVendorManifest(file string, data []byte) (*modfile.File, error) {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ package modconv
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func ParseVendorYML(file string, data []byte) (*modfile.File, error) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
package modfetch
|
||||
|
||||
import "cmd/go/internal/module"
|
||||
import "golang.org/x/mod/module"
|
||||
|
||||
func useSumDB(mod module.Version) bool {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@ import (
|
|||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/lockedfile"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/renameio"
|
||||
"cmd/go/internal/semver"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var QuietLookup bool // do not print about lookups
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ import (
|
|||
|
||||
"cmd/go/internal/lockedfile"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/web"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// GitRepo returns the code repository at the given Git remote reference.
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@ import (
|
|||
"time"
|
||||
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/semver"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// A codeRepo implements modfetch.Repo using an underlying codehost.Repo.
|
||||
|
|
|
|||
|
|
@ -19,10 +19,11 @@ import (
|
|||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/dirhash"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/renameio"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/sumdb/dirhash"
|
||||
)
|
||||
|
||||
var downloadCache par.Cache
|
||||
|
|
|
|||
|
|
@ -22,9 +22,10 @@ import (
|
|||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/web"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var HelpGoproxy = &base.Command{
|
||||
|
|
|
|||
|
|
@ -40,9 +40,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/semver"
|
||||
"internal/lazyregexp"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var pseudoVersionRE = lazyregexp.New(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$`)
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@ import (
|
|||
"cmd/go/internal/get"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/str"
|
||||
web "cmd/go/internal/web"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
const traceRepo = false // trace all repo actions, for debugging
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ import (
|
|||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/get"
|
||||
"cmd/go/internal/lockedfile"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/note"
|
||||
"cmd/go/internal/str"
|
||||
"cmd/go/internal/sumdb"
|
||||
"cmd/go/internal/web"
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/sumdb"
|
||||
"golang.org/x/mod/sumdb/note"
|
||||
)
|
||||
|
||||
// useSumDB reports whether to use the Go checksum database for the given module.
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/str"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func Unzip(dir, zipfile, prefix string, maxSize int64) error {
|
||||
|
|
|
|||
|
|
@ -1,388 +0,0 @@
|
|||
// 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 modfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// exists reports whether the named file exists.
|
||||
func exists(name string) bool {
|
||||
_, err := os.Stat(name)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Test that reading and then writing the golden files
|
||||
// does not change their output.
|
||||
func TestPrintGolden(t *testing.T) {
|
||||
outs, err := filepath.Glob("testdata/*.golden")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, out := range outs {
|
||||
testPrint(t, out, out)
|
||||
}
|
||||
}
|
||||
|
||||
// testPrint is a helper for testing the printer.
|
||||
// It reads the file named in, reformats it, and compares
|
||||
// the result to the file named out.
|
||||
func testPrint(t *testing.T, in, out string) {
|
||||
data, err := ioutil.ReadFile(in)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
golden, err := ioutil.ReadFile(out)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
base := "testdata/" + filepath.Base(in)
|
||||
f, err := parse(in, data)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
ndata := Format(f)
|
||||
|
||||
if !bytes.Equal(ndata, golden) {
|
||||
t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base)
|
||||
tdiff(t, string(golden), string(ndata))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLax(t *testing.T) {
|
||||
badFile := []byte(`module m
|
||||
surprise attack
|
||||
x y (
|
||||
z
|
||||
)
|
||||
exclude v1.2.3
|
||||
replace <-!!!
|
||||
`)
|
||||
_, err := ParseLax("file", badFile, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseLax did not ignore irrelevant errors: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that when files in the testdata directory are parsed
|
||||
// and printed and parsed again, we get the same parse tree
|
||||
// both times.
|
||||
func TestPrintParse(t *testing.T) {
|
||||
outs, err := filepath.Glob("testdata/*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, out := range outs {
|
||||
data, err := ioutil.ReadFile(out)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
base := "testdata/" + filepath.Base(out)
|
||||
f, err := parse(base, data)
|
||||
if err != nil {
|
||||
t.Errorf("parsing original: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
ndata := Format(f)
|
||||
f2, err := parse(base, ndata)
|
||||
if err != nil {
|
||||
t.Errorf("parsing reformatted: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
eq := eqchecker{file: base}
|
||||
if err := eq.check(f, f2); err != nil {
|
||||
t.Errorf("not equal (parse/Format/parse): %v", err)
|
||||
}
|
||||
|
||||
pf1, err := Parse(base, data, nil)
|
||||
if err != nil {
|
||||
switch base {
|
||||
case "testdata/replace2.in", "testdata/gopkg.in.golden":
|
||||
t.Errorf("should parse %v: %v", base, err)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
pf2, err := Parse(base, ndata, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parsing reformatted: %v", err)
|
||||
continue
|
||||
}
|
||||
eq := eqchecker{file: base}
|
||||
if err := eq.check(pf1, pf2); err != nil {
|
||||
t.Errorf("not equal (parse/Format/Parse): %v", err)
|
||||
}
|
||||
|
||||
ndata2, err := pf1.Format()
|
||||
if err != nil {
|
||||
t.Errorf("reformat: %v", err)
|
||||
}
|
||||
pf3, err := Parse(base, ndata2, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Parsing reformatted2: %v", err)
|
||||
continue
|
||||
}
|
||||
eq = eqchecker{file: base}
|
||||
if err := eq.check(pf1, pf3); err != nil {
|
||||
t.Errorf("not equal (Parse/Format/Parse): %v", err)
|
||||
}
|
||||
ndata = ndata2
|
||||
}
|
||||
|
||||
if strings.HasSuffix(out, ".in") {
|
||||
golden, err := ioutil.ReadFile(strings.TrimSuffix(out, ".in") + ".golden")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(ndata, golden) {
|
||||
t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base)
|
||||
tdiff(t, string(golden), string(ndata))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An eqchecker holds state for checking the equality of two parse trees.
|
||||
type eqchecker struct {
|
||||
file string
|
||||
pos Position
|
||||
}
|
||||
|
||||
// errorf returns an error described by the printf-style format and arguments,
|
||||
// inserting the current file position before the error text.
|
||||
func (eq *eqchecker) errorf(format string, args ...interface{}) error {
|
||||
return fmt.Errorf("%s:%d: %s", eq.file, eq.pos.Line,
|
||||
fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// check checks that v and w represent the same parse tree.
|
||||
// If not, it returns an error describing the first difference.
|
||||
func (eq *eqchecker) check(v, w interface{}) error {
|
||||
return eq.checkValue(reflect.ValueOf(v), reflect.ValueOf(w))
|
||||
}
|
||||
|
||||
var (
|
||||
posType = reflect.TypeOf(Position{})
|
||||
commentsType = reflect.TypeOf(Comments{})
|
||||
)
|
||||
|
||||
// checkValue checks that v and w represent the same parse tree.
|
||||
// If not, it returns an error describing the first difference.
|
||||
func (eq *eqchecker) checkValue(v, w reflect.Value) error {
|
||||
// inner returns the innermost expression for v.
|
||||
// if v is a non-nil interface value, it returns the concrete
|
||||
// value in the interface.
|
||||
inner := func(v reflect.Value) reflect.Value {
|
||||
for {
|
||||
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
v = inner(v)
|
||||
w = inner(w)
|
||||
if v.Kind() == reflect.Invalid && w.Kind() == reflect.Invalid {
|
||||
return nil
|
||||
}
|
||||
if v.Kind() == reflect.Invalid {
|
||||
return eq.errorf("nil interface became %s", w.Type())
|
||||
}
|
||||
if w.Kind() == reflect.Invalid {
|
||||
return eq.errorf("%s became nil interface", v.Type())
|
||||
}
|
||||
|
||||
if v.Type() != w.Type() {
|
||||
return eq.errorf("%s became %s", v.Type(), w.Type())
|
||||
}
|
||||
|
||||
if p, ok := v.Interface().(Expr); ok {
|
||||
eq.pos, _ = p.Span()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
default:
|
||||
return eq.errorf("unexpected type %s", v.Type())
|
||||
|
||||
case reflect.Bool, reflect.Int, reflect.String:
|
||||
vi := v.Interface()
|
||||
wi := w.Interface()
|
||||
if vi != wi {
|
||||
return eq.errorf("%v became %v", vi, wi)
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
vl := v.Len()
|
||||
wl := w.Len()
|
||||
for i := 0; i < vl || i < wl; i++ {
|
||||
if i >= vl {
|
||||
return eq.errorf("unexpected %s", w.Index(i).Type())
|
||||
}
|
||||
if i >= wl {
|
||||
return eq.errorf("missing %s", v.Index(i).Type())
|
||||
}
|
||||
if err := eq.checkValue(v.Index(i), w.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
// Fields in struct must match.
|
||||
t := v.Type()
|
||||
n := t.NumField()
|
||||
for i := 0; i < n; i++ {
|
||||
tf := t.Field(i)
|
||||
switch {
|
||||
default:
|
||||
if err := eq.checkValue(v.Field(i), w.Field(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case tf.Type == posType: // ignore positions
|
||||
case tf.Type == commentsType: // ignore comment assignment
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
if v.IsNil() != w.IsNil() {
|
||||
if v.IsNil() {
|
||||
return eq.errorf("unexpected %s", w.Elem().Type())
|
||||
}
|
||||
return eq.errorf("missing %s", v.Elem().Type())
|
||||
}
|
||||
if err := eq.checkValue(v.Elem(), w.Elem()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// diff returns the output of running diff on b1 and b2.
|
||||
func diff(b1, b2 []byte) (data []byte, err error) {
|
||||
f1, err := ioutil.TempFile("", "testdiff")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.Remove(f1.Name())
|
||||
defer f1.Close()
|
||||
|
||||
f2, err := ioutil.TempFile("", "testdiff")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.Remove(f2.Name())
|
||||
defer f2.Close()
|
||||
|
||||
f1.Write(b1)
|
||||
f2.Write(b2)
|
||||
|
||||
data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
|
||||
if len(data) > 0 {
|
||||
// diff exits with a non-zero status when the files don't match.
|
||||
// Ignore that failure as long as we get output.
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// tdiff logs the diff output to t.Error.
|
||||
func tdiff(t *testing.T, a, b string) {
|
||||
data, err := diff([]byte(a), []byte(b))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Error(string(data))
|
||||
}
|
||||
|
||||
var modulePathTests = []struct {
|
||||
input []byte
|
||||
expected string
|
||||
}{
|
||||
{input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"},
|
||||
{input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"},
|
||||
{input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"},
|
||||
{input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"},
|
||||
{input: []byte("module `github.com/rsc/vgotest`"), expected: "github.com/rsc/vgotest"},
|
||||
{input: []byte("module \"github.com/rsc/vgotest/v2\""), expected: "github.com/rsc/vgotest/v2"},
|
||||
{input: []byte("module github.com/rsc/vgotest/v2"), expected: "github.com/rsc/vgotest/v2"},
|
||||
{input: []byte("module \"gopkg.in/yaml.v2\""), expected: "gopkg.in/yaml.v2"},
|
||||
{input: []byte("module gopkg.in/yaml.v2"), expected: "gopkg.in/yaml.v2"},
|
||||
{input: []byte("module \"gopkg.in/check.v1\"\n"), expected: "gopkg.in/check.v1"},
|
||||
{input: []byte("module \"gopkg.in/check.v1\n\""), expected: ""},
|
||||
{input: []byte("module gopkg.in/check.v1\n"), expected: "gopkg.in/check.v1"},
|
||||
{input: []byte("module \"gopkg.in/check.v1\"\r\n"), expected: "gopkg.in/check.v1"},
|
||||
{input: []byte("module gopkg.in/check.v1\r\n"), expected: "gopkg.in/check.v1"},
|
||||
{input: []byte("module \"gopkg.in/check.v1\"\n\n"), expected: "gopkg.in/check.v1"},
|
||||
{input: []byte("module gopkg.in/check.v1\n\n"), expected: "gopkg.in/check.v1"},
|
||||
{input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""},
|
||||
{input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""},
|
||||
{input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""},
|
||||
{input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""},
|
||||
{input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""},
|
||||
{input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""},
|
||||
{input: []byte("module \nmodule a/b/c "), expected: "a/b/c"},
|
||||
{input: []byte("module \" \""), expected: " "},
|
||||
{input: []byte("module "), expected: ""},
|
||||
{input: []byte("module \" a/b/c \""), expected: " a/b/c "},
|
||||
{input: []byte("module \"github.com/rsc/vgotest1\" // with a comment"), expected: "github.com/rsc/vgotest1"},
|
||||
}
|
||||
|
||||
func TestModulePath(t *testing.T) {
|
||||
for _, test := range modulePathTests {
|
||||
t.Run(string(test.input), func(t *testing.T) {
|
||||
result := ModulePath(test.input)
|
||||
if result != test.expected {
|
||||
t.Fatalf("ModulePath(%q): %s, want %s", string(test.input), result, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoVersion(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
desc, input string
|
||||
ok bool
|
||||
}{
|
||||
{desc: "empty", input: "module m\ngo \n", ok: false},
|
||||
{desc: "one", input: "module m\ngo 1\n", ok: false},
|
||||
{desc: "two", input: "module m\ngo 1.22\n", ok: true},
|
||||
{desc: "three", input: "module m\ngo 1.22.333", ok: false},
|
||||
{desc: "before", input: "module m\ngo v1.2\n", ok: false},
|
||||
{desc: "after", input: "module m\ngo 1.2rc1\n", ok: false},
|
||||
{desc: "space", input: "module m\ngo 1.2 3.4\n", ok: false},
|
||||
} {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
if _, err := Parse("go.mod", []byte(test.input), nil); err == nil && !test.ok {
|
||||
t.Error("unexpected success")
|
||||
} else if err != nil && test.ok {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
// 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 modfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"cmd/go/internal/module"
|
||||
)
|
||||
|
||||
var addRequireTests = []struct {
|
||||
in string
|
||||
path string
|
||||
vers string
|
||||
out string
|
||||
}{
|
||||
{
|
||||
`
|
||||
module m
|
||||
require x.y/z v1.2.3
|
||||
`,
|
||||
"x.y/z", "v1.5.6",
|
||||
`
|
||||
module m
|
||||
require x.y/z v1.5.6
|
||||
`,
|
||||
},
|
||||
{
|
||||
`
|
||||
module m
|
||||
require x.y/z v1.2.3
|
||||
`,
|
||||
"x.y/w", "v1.5.6",
|
||||
`
|
||||
module m
|
||||
require (
|
||||
x.y/z v1.2.3
|
||||
x.y/w v1.5.6
|
||||
)
|
||||
`,
|
||||
},
|
||||
{
|
||||
`
|
||||
module m
|
||||
require x.y/z v1.2.3
|
||||
require x.y/q/v2 v2.3.4
|
||||
`,
|
||||
"x.y/w", "v1.5.6",
|
||||
`
|
||||
module m
|
||||
require x.y/z v1.2.3
|
||||
require (
|
||||
x.y/q/v2 v2.3.4
|
||||
x.y/w v1.5.6
|
||||
)
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
var setRequireTests = []struct {
|
||||
in string
|
||||
mods []struct {
|
||||
path string
|
||||
vers string
|
||||
}
|
||||
out string
|
||||
}{
|
||||
{
|
||||
`module m
|
||||
require (
|
||||
x.y/b v1.2.3
|
||||
|
||||
x.y/a v1.2.3
|
||||
)
|
||||
`,
|
||||
[]struct {
|
||||
path string
|
||||
vers string
|
||||
}{
|
||||
{"x.y/a", "v1.2.3"},
|
||||
{"x.y/b", "v1.2.3"},
|
||||
{"x.y/c", "v1.2.3"},
|
||||
},
|
||||
`module m
|
||||
require (
|
||||
x.y/a v1.2.3
|
||||
x.y/b v1.2.3
|
||||
x.y/c v1.2.3
|
||||
)
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestAddRequire(t *testing.T) {
|
||||
for i, tt := range addRequireTests {
|
||||
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
|
||||
f, err := Parse("in", []byte(tt.in), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
g, err := Parse("out", []byte(tt.out), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
golden, err := g.Format()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := f.AddRequire(tt.path, tt.vers); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := f.Format()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, golden) {
|
||||
t.Errorf("have:\n%s\nwant:\n%s", out, golden)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetRequire(t *testing.T) {
|
||||
for i, tt := range setRequireTests {
|
||||
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
|
||||
f, err := Parse("in", []byte(tt.in), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
g, err := Parse("out", []byte(tt.out), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
golden, err := g.Format()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var mods []*Require
|
||||
for _, mod := range tt.mods {
|
||||
mods = append(mods, &Require{
|
||||
Mod: module.Version{
|
||||
Path: mod.path,
|
||||
Version: mod.vers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
f.SetRequire(mods)
|
||||
out, err := f.Format()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(out, golden) {
|
||||
t.Errorf("have:\n%s\nwant:\n%s", out, golden)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
// comment
|
||||
x "y" z
|
||||
|
||||
// block
|
||||
block ( // block-eol
|
||||
// x-before-line
|
||||
|
||||
"x" ( y // x-eol
|
||||
"x1"
|
||||
"x2"
|
||||
// line
|
||||
"x3"
|
||||
"x4"
|
||||
|
||||
"x5"
|
||||
|
||||
// y-line
|
||||
"y" // y-eol
|
||||
|
||||
"z" // z-eol
|
||||
) // block-eol2
|
||||
|
||||
block2 (
|
||||
x
|
||||
y
|
||||
z
|
||||
)
|
||||
|
||||
// eof
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
// comment
|
||||
x "y" z
|
||||
|
||||
// block
|
||||
block ( // block-eol
|
||||
// x-before-line
|
||||
|
||||
"x" ( y // x-eol
|
||||
"x1"
|
||||
"x2"
|
||||
// line
|
||||
"x3"
|
||||
"x4"
|
||||
|
||||
"x5"
|
||||
|
||||
// y-line
|
||||
"y" // y-eol
|
||||
|
||||
"z" // z-eol
|
||||
) // block-eol2
|
||||
|
||||
|
||||
block2 (x
|
||||
y
|
||||
z
|
||||
)
|
||||
|
||||
// eof
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// comment
|
||||
module "x" // eol
|
||||
|
||||
// mid comment
|
||||
|
||||
// comment 2
|
||||
// comment 2 line 2
|
||||
module "y" // eoy
|
||||
|
||||
// comment 3
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
// comment
|
||||
module "x" // eol
|
||||
// mid comment
|
||||
|
||||
// comment 2
|
||||
// comment 2 line 2
|
||||
module "y" // eoy
|
||||
// comment 3
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
module x
|
||||
|
||||
require (
|
||||
gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528
|
||||
gopkg.in/yaml.v2 v2.2.1
|
||||
)
|
||||
|
|
@ -1 +0,0 @@
|
|||
module abc
|
||||
|
|
@ -1 +0,0 @@
|
|||
module "abc"
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module abc
|
||||
|
||||
replace xyz v1.2.3 => /tmp/z
|
||||
|
||||
replace xyz v1.3.4 => my/xyz v1.3.4-me
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module "abc"
|
||||
|
||||
replace "xyz" v1.2.3 => "/tmp/z"
|
||||
|
||||
replace "xyz" v1.3.4 => "my/xyz" v1.3.4-me
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
module abc
|
||||
|
||||
replace (
|
||||
xyz v1.2.3 => /tmp/z
|
||||
xyz v1.3.4 => my/xyz v1.3.4-me
|
||||
xyz v1.4.5 => "/tmp/my dir"
|
||||
xyz v1.5.6 => my/xyz v1.5.6
|
||||
|
||||
xyz => my/other/xyz v1.5.4
|
||||
)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
module "abc"
|
||||
|
||||
replace (
|
||||
"xyz" v1.2.3 => "/tmp/z"
|
||||
"xyz" v1.3.4 => "my/xyz" "v1.3.4-me"
|
||||
xyz "v1.4.5" => "/tmp/my dir"
|
||||
xyz v1.5.6 => my/xyz v1.5.6
|
||||
|
||||
xyz => my/other/xyz v1.5.4
|
||||
)
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
module "x"
|
||||
|
||||
module "y"
|
||||
|
||||
require "x"
|
||||
|
||||
require x
|
||||
|
|
@ -6,17 +6,6 @@
|
|||
package modget
|
||||
|
||||
import (
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/get"
|
||||
"cmd/go/internal/imports"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/mvs"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/search"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/work"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
|
@ -24,6 +13,19 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/get"
|
||||
"cmd/go/internal/imports"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/mvs"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/search"
|
||||
"cmd/go/internal/work"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var CmdGet = &base.Command{
|
||||
|
|
|
|||
|
|
@ -6,13 +6,6 @@ package modload
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modinfo"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/search"
|
||||
"cmd/go/internal/semver"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"internal/goroot"
|
||||
|
|
@ -20,6 +13,15 @@ import (
|
|||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modinfo"
|
||||
"cmd/go/internal/search"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -18,11 +18,12 @@ import (
|
|||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/search"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/str"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
type ImportMissingError struct {
|
||||
|
|
|
|||
|
|
@ -25,12 +25,13 @@ import (
|
|||
"cmd/go/internal/modconv"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/mvs"
|
||||
"cmd/go/internal/renameio"
|
||||
"cmd/go/internal/search"
|
||||
"cmd/go/internal/semver"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ import (
|
|||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modinfo"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/search"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePublic {
|
||||
|
|
|
|||
|
|
@ -22,13 +22,14 @@ import (
|
|||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/imports"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/mvs"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/search"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/str"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// buildList is the list of modules to use for building packages.
|
||||
|
|
|
|||
|
|
@ -14,10 +14,11 @@ import (
|
|||
|
||||
"cmd/go/internal/imports"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/search"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/str"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// Query looks up a revision of a given module given a version query string.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import (
|
|||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/module"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ import (
|
|||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/imports"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/search"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
// matchPackages returns a list of packages in the list of modules
|
||||
|
|
|
|||
|
|
@ -1,343 +0,0 @@
|
|||
// 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 module
|
||||
|
||||
import "testing"
|
||||
|
||||
var checkTests = []struct {
|
||||
path string
|
||||
version string
|
||||
ok bool
|
||||
}{
|
||||
{"rsc.io/quote", "0.1.0", false},
|
||||
{"rsc io/quote", "v1.0.0", false},
|
||||
|
||||
{"github.com/go-yaml/yaml", "v0.8.0", true},
|
||||
{"github.com/go-yaml/yaml", "v1.0.0", true},
|
||||
{"github.com/go-yaml/yaml", "v2.0.0", false},
|
||||
{"github.com/go-yaml/yaml", "v2.1.5", false},
|
||||
{"github.com/go-yaml/yaml", "v3.0.0", false},
|
||||
|
||||
{"github.com/go-yaml/yaml/v2", "v1.0.0", false},
|
||||
{"github.com/go-yaml/yaml/v2", "v2.0.0", true},
|
||||
{"github.com/go-yaml/yaml/v2", "v2.1.5", true},
|
||||
{"github.com/go-yaml/yaml/v2", "v3.0.0", false},
|
||||
|
||||
{"gopkg.in/yaml.v0", "v0.8.0", true},
|
||||
{"gopkg.in/yaml.v0", "v1.0.0", false},
|
||||
{"gopkg.in/yaml.v0", "v2.0.0", false},
|
||||
{"gopkg.in/yaml.v0", "v2.1.5", false},
|
||||
{"gopkg.in/yaml.v0", "v3.0.0", false},
|
||||
|
||||
{"gopkg.in/yaml.v1", "v0.8.0", false},
|
||||
{"gopkg.in/yaml.v1", "v1.0.0", true},
|
||||
{"gopkg.in/yaml.v1", "v2.0.0", false},
|
||||
{"gopkg.in/yaml.v1", "v2.1.5", false},
|
||||
{"gopkg.in/yaml.v1", "v3.0.0", false},
|
||||
|
||||
// For gopkg.in, .v1 means v1 only (not v0).
|
||||
// But early versions of vgo still generated v0 pseudo-versions for it.
|
||||
// Even though now we'd generate those as v1 pseudo-versions,
|
||||
// we accept the old pseudo-versions to avoid breaking existing go.mod files.
|
||||
// For example gopkg.in/yaml.v2@v2.2.1's go.mod requires check.v1 at a v0 pseudo-version.
|
||||
{"gopkg.in/check.v1", "v0.0.0", false},
|
||||
{"gopkg.in/check.v1", "v0.0.0-20160102150405-abcdef123456", true},
|
||||
|
||||
{"gopkg.in/yaml.v2", "v1.0.0", false},
|
||||
{"gopkg.in/yaml.v2", "v2.0.0", true},
|
||||
{"gopkg.in/yaml.v2", "v2.1.5", true},
|
||||
{"gopkg.in/yaml.v2", "v3.0.0", false},
|
||||
|
||||
{"rsc.io/quote", "v17.0.0", false},
|
||||
{"rsc.io/quote", "v17.0.0+incompatible", true},
|
||||
}
|
||||
|
||||
func TestCheck(t *testing.T) {
|
||||
for _, tt := range checkTests {
|
||||
err := Check(tt.path, tt.version)
|
||||
if tt.ok && err != nil {
|
||||
t.Errorf("Check(%q, %q) = %v, wanted nil error", tt.path, tt.version, err)
|
||||
} else if !tt.ok && err == nil {
|
||||
t.Errorf("Check(%q, %q) succeeded, wanted error", tt.path, tt.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var checkPathTests = []struct {
|
||||
path string
|
||||
ok bool
|
||||
importOK bool
|
||||
fileOK bool
|
||||
}{
|
||||
{"x.y/z", true, true, true},
|
||||
{"x.y", true, true, true},
|
||||
|
||||
{"", false, false, false},
|
||||
{"x.y/\xFFz", false, false, false},
|
||||
{"/x.y/z", false, false, false},
|
||||
{"x./z", false, false, false},
|
||||
{".x/z", false, false, true},
|
||||
{"-x/z", false, false, false},
|
||||
{"x..y/z", true, true, true},
|
||||
{"x.y/z/../../w", false, false, false},
|
||||
{"x.y//z", false, false, false},
|
||||
{"x.y/z//w", false, false, false},
|
||||
{"x.y/z/", false, false, false},
|
||||
|
||||
{"x.y/z/v0", false, true, true},
|
||||
{"x.y/z/v1", false, true, true},
|
||||
{"x.y/z/v2", true, true, true},
|
||||
{"x.y/z/v2.0", false, true, true},
|
||||
{"X.y/z", false, true, true},
|
||||
|
||||
{"!x.y/z", false, false, true},
|
||||
{"_x.y/z", false, true, true},
|
||||
{"x.y!/z", false, false, true},
|
||||
{"x.y\"/z", false, false, false},
|
||||
{"x.y#/z", false, false, true},
|
||||
{"x.y$/z", false, false, true},
|
||||
{"x.y%/z", false, false, true},
|
||||
{"x.y&/z", false, false, true},
|
||||
{"x.y'/z", false, false, false},
|
||||
{"x.y(/z", false, false, true},
|
||||
{"x.y)/z", false, false, true},
|
||||
{"x.y*/z", false, false, false},
|
||||
{"x.y+/z", false, true, true},
|
||||
{"x.y,/z", false, false, true},
|
||||
{"x.y-/z", true, true, true},
|
||||
{"x.y./zt", false, false, false},
|
||||
{"x.y:/z", false, false, false},
|
||||
{"x.y;/z", false, false, false},
|
||||
{"x.y</z", false, false, false},
|
||||
{"x.y=/z", false, false, true},
|
||||
{"x.y>/z", false, false, false},
|
||||
{"x.y?/z", false, false, false},
|
||||
{"x.y@/z", false, false, true},
|
||||
{"x.y[/z", false, false, true},
|
||||
{"x.y\\/z", false, false, false},
|
||||
{"x.y]/z", false, false, true},
|
||||
{"x.y^/z", false, false, true},
|
||||
{"x.y_/z", false, true, true},
|
||||
{"x.y`/z", false, false, false},
|
||||
{"x.y{/z", false, false, true},
|
||||
{"x.y}/z", false, false, true},
|
||||
{"x.y~/z", false, true, true},
|
||||
{"x.y/z!", false, false, true},
|
||||
{"x.y/z\"", false, false, false},
|
||||
{"x.y/z#", false, false, true},
|
||||
{"x.y/z$", false, false, true},
|
||||
{"x.y/z%", false, false, true},
|
||||
{"x.y/z&", false, false, true},
|
||||
{"x.y/z'", false, false, false},
|
||||
{"x.y/z(", false, false, true},
|
||||
{"x.y/z)", false, false, true},
|
||||
{"x.y/z*", false, false, false},
|
||||
{"x.y/z+", true, true, true},
|
||||
{"x.y/z,", false, false, true},
|
||||
{"x.y/z-", true, true, true},
|
||||
{"x.y/z.t", true, true, true},
|
||||
{"x.y/z/t", true, true, true},
|
||||
{"x.y/z:", false, false, false},
|
||||
{"x.y/z;", false, false, false},
|
||||
{"x.y/z<", false, false, false},
|
||||
{"x.y/z=", false, false, true},
|
||||
{"x.y/z>", false, false, false},
|
||||
{"x.y/z?", false, false, false},
|
||||
{"x.y/z@", false, false, true},
|
||||
{"x.y/z[", false, false, true},
|
||||
{"x.y/z\\", false, false, false},
|
||||
{"x.y/z]", false, false, true},
|
||||
{"x.y/z^", false, false, true},
|
||||
{"x.y/z_", true, true, true},
|
||||
{"x.y/z`", false, false, false},
|
||||
{"x.y/z{", false, false, true},
|
||||
{"x.y/z}", false, false, true},
|
||||
{"x.y/z~", true, true, true},
|
||||
{"x.y/x.foo", true, true, true},
|
||||
{"x.y/aux.foo", false, false, false},
|
||||
{"x.y/prn", false, false, false},
|
||||
{"x.y/prn2", true, true, true},
|
||||
{"x.y/com", true, true, true},
|
||||
{"x.y/com1", false, false, false},
|
||||
{"x.y/com1.txt", false, false, false},
|
||||
{"x.y/calm1", true, true, true},
|
||||
{"github.com/!123/logrus", false, false, true},
|
||||
|
||||
// TODO: CL 41822 allowed Unicode letters in old "go get"
|
||||
// without due consideration of the implications, and only on github.com (!).
|
||||
// For now, we disallow non-ASCII characters in module mode,
|
||||
// in both module paths and general import paths,
|
||||
// until we can get the implications right.
|
||||
// When we do, we'll enable them everywhere, not just for GitHub.
|
||||
{"github.com/user/unicode/испытание", false, false, true},
|
||||
|
||||
{".../x", false, false, false},
|
||||
{"../x", false, false, false},
|
||||
{"./y", false, false, false},
|
||||
{"x:y", false, false, false},
|
||||
{`\temp\foo`, false, false, false},
|
||||
{".gitignore", false, false, true},
|
||||
{".github/ISSUE_TEMPLATE", false, false, true},
|
||||
{"x☺y", false, false, false},
|
||||
}
|
||||
|
||||
func TestCheckPath(t *testing.T) {
|
||||
for _, tt := range checkPathTests {
|
||||
err := CheckPath(tt.path)
|
||||
if tt.ok && err != nil {
|
||||
t.Errorf("CheckPath(%q) = %v, wanted nil error", tt.path, err)
|
||||
} else if !tt.ok && err == nil {
|
||||
t.Errorf("CheckPath(%q) succeeded, wanted error", tt.path)
|
||||
}
|
||||
|
||||
err = CheckImportPath(tt.path)
|
||||
if tt.importOK && err != nil {
|
||||
t.Errorf("CheckImportPath(%q) = %v, wanted nil error", tt.path, err)
|
||||
} else if !tt.importOK && err == nil {
|
||||
t.Errorf("CheckImportPath(%q) succeeded, wanted error", tt.path)
|
||||
}
|
||||
|
||||
err = CheckFilePath(tt.path)
|
||||
if tt.fileOK && err != nil {
|
||||
t.Errorf("CheckFilePath(%q) = %v, wanted nil error", tt.path, err)
|
||||
} else if !tt.fileOK && err == nil {
|
||||
t.Errorf("CheckFilePath(%q) succeeded, wanted error", tt.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var splitPathVersionTests = []struct {
|
||||
pathPrefix string
|
||||
version string
|
||||
}{
|
||||
{"x.y/z", ""},
|
||||
{"x.y/z", "/v2"},
|
||||
{"x.y/z", "/v3"},
|
||||
{"x.y/v", ""},
|
||||
{"gopkg.in/yaml", ".v0"},
|
||||
{"gopkg.in/yaml", ".v1"},
|
||||
{"gopkg.in/yaml", ".v2"},
|
||||
{"gopkg.in/yaml", ".v3"},
|
||||
}
|
||||
|
||||
func TestSplitPathVersion(t *testing.T) {
|
||||
for _, tt := range splitPathVersionTests {
|
||||
pathPrefix, version, ok := SplitPathVersion(tt.pathPrefix + tt.version)
|
||||
if pathPrefix != tt.pathPrefix || version != tt.version || !ok {
|
||||
t.Errorf("SplitPathVersion(%q) = %q, %q, %v, want %q, %q, true", tt.pathPrefix+tt.version, pathPrefix, version, ok, tt.pathPrefix, tt.version)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range checkPathTests {
|
||||
pathPrefix, version, ok := SplitPathVersion(tt.path)
|
||||
if pathPrefix+version != tt.path {
|
||||
t.Errorf("SplitPathVersion(%q) = %q, %q, %v, doesn't add to input", tt.path, pathPrefix, version, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var escapeTests = []struct {
|
||||
path string
|
||||
esc string // empty means same as path
|
||||
}{
|
||||
{path: "ascii.com/abcdefghijklmnopqrstuvwxyz.-+/~_0123456789"},
|
||||
{path: "github.com/GoogleCloudPlatform/omega", esc: "github.com/!google!cloud!platform/omega"},
|
||||
}
|
||||
|
||||
func TestEscapePath(t *testing.T) {
|
||||
// Check invalid paths.
|
||||
for _, tt := range checkPathTests {
|
||||
if !tt.ok {
|
||||
_, err := EscapePath(tt.path)
|
||||
if err == nil {
|
||||
t.Errorf("EscapePath(%q): succeeded, want error (invalid path)", tt.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check encodings.
|
||||
for _, tt := range escapeTests {
|
||||
esc, err := EscapePath(tt.path)
|
||||
if err != nil {
|
||||
t.Errorf("EscapePath(%q): unexpected error: %v", tt.path, err)
|
||||
continue
|
||||
}
|
||||
want := tt.esc
|
||||
if want == "" {
|
||||
want = tt.path
|
||||
}
|
||||
if esc != want {
|
||||
t.Errorf("EscapePath(%q) = %q, want %q", tt.path, esc, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var badUnescape = []string{
|
||||
"github.com/GoogleCloudPlatform/omega",
|
||||
"github.com/!google!cloud!platform!/omega",
|
||||
"github.com/!0google!cloud!platform/omega",
|
||||
"github.com/!_google!cloud!platform/omega",
|
||||
"github.com/!!google!cloud!platform/omega",
|
||||
"",
|
||||
}
|
||||
|
||||
func TestUnescapePath(t *testing.T) {
|
||||
// Check invalid decodings.
|
||||
for _, bad := range badUnescape {
|
||||
_, err := UnescapePath(bad)
|
||||
if err == nil {
|
||||
t.Errorf("UnescapePath(%q): succeeded, want error (invalid decoding)", bad)
|
||||
}
|
||||
}
|
||||
|
||||
// Check invalid paths (or maybe decodings).
|
||||
for _, tt := range checkPathTests {
|
||||
if !tt.ok {
|
||||
path, err := UnescapePath(tt.path)
|
||||
if err == nil {
|
||||
t.Errorf("UnescapePath(%q) = %q, want error (invalid path)", tt.path, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check encodings.
|
||||
for _, tt := range escapeTests {
|
||||
esc := tt.esc
|
||||
if esc == "" {
|
||||
esc = tt.path
|
||||
}
|
||||
path, err := UnescapePath(esc)
|
||||
if err != nil {
|
||||
t.Errorf("UnescapePath(%q): unexpected error: %v", esc, err)
|
||||
continue
|
||||
}
|
||||
if path != tt.path {
|
||||
t.Errorf("UnescapePath(%q) = %q, want %q", esc, path, tt.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchPathMajor(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
v, pathMajor string
|
||||
want bool
|
||||
}{
|
||||
{"v0.0.0", "", true},
|
||||
{"v0.0.0", "/v2", false},
|
||||
{"v0.0.0", ".v0", true},
|
||||
{"v0.0.0-20190510104115-cbcb75029529", ".v1", true},
|
||||
{"v1.0.0", "/v2", false},
|
||||
{"v1.0.0", ".v1", true},
|
||||
{"v1.0.0", ".v1-unstable", true},
|
||||
{"v2.0.0+incompatible", "", true},
|
||||
{"v2.0.0", "", false},
|
||||
{"v2.0.0", "/v2", true},
|
||||
{"v2.0.0", ".v2", true},
|
||||
} {
|
||||
if got := MatchPathMajor(test.v, test.pathMajor); got != test.want {
|
||||
t.Errorf("MatchPathMajor(%q, %q) = %v, want %v", test.v, test.pathMajor, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,8 +13,9 @@ import (
|
|||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
// A Reqs is the requirement graph on which Minimal Version Selection (MVS) operates.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"cmd/go/internal/module"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
var tests = `
|
||||
|
|
|
|||
|
|
@ -1,128 +0,0 @@
|
|||
// Copyright 2019 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 note_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"cmd/go/internal/note"
|
||||
)
|
||||
|
||||
func ExampleSign() {
|
||||
skey := "PRIVATE+KEY+PeterNeumann+c74f20a3+AYEKFALVFGyNhPJEMzD1QIDr+Y7hfZx09iUvxdXHKDFz"
|
||||
text := "If you think cryptography is the answer to your problem,\n" +
|
||||
"then you don't know what your problem is.\n"
|
||||
|
||||
signer, err := note.NewSigner(skey)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := note.Sign(¬e.Note{Text: text}, signer)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
os.Stdout.Write(msg)
|
||||
|
||||
// Output:
|
||||
// If you think cryptography is the answer to your problem,
|
||||
// then you don't know what your problem is.
|
||||
//
|
||||
// — PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=
|
||||
}
|
||||
|
||||
func ExampleOpen() {
|
||||
vkey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW"
|
||||
msg := []byte("If you think cryptography is the answer to your problem,\n" +
|
||||
"then you don't know what your problem is.\n" +
|
||||
"\n" +
|
||||
"— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=\n")
|
||||
|
||||
verifier, err := note.NewVerifier(vkey)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
verifiers := note.VerifierList(verifier)
|
||||
|
||||
n, err := note.Open(msg, verifiers)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s (%08x):\n%s", n.Sigs[0].Name, n.Sigs[0].Hash, n.Text)
|
||||
|
||||
// Output:
|
||||
// PeterNeumann (c74f20a3):
|
||||
// If you think cryptography is the answer to your problem,
|
||||
// then you don't know what your problem is.
|
||||
}
|
||||
|
||||
var rand = struct {
|
||||
Reader io.Reader
|
||||
}{
|
||||
zeroReader{},
|
||||
}
|
||||
|
||||
type zeroReader struct{}
|
||||
|
||||
func (zeroReader) Read(buf []byte) (int, error) {
|
||||
for i := range buf {
|
||||
buf[i] = 0
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func ExampleSign_add_signatures() {
|
||||
vkey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW"
|
||||
msg := []byte("If you think cryptography is the answer to your problem,\n" +
|
||||
"then you don't know what your problem is.\n" +
|
||||
"\n" +
|
||||
"— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=\n")
|
||||
|
||||
verifier, err := note.NewVerifier(vkey)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
verifiers := note.VerifierList(verifier)
|
||||
|
||||
n, err := note.Open([]byte(msg), verifiers)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
skey, vkey, err := note.GenerateKey(rand.Reader, "EnochRoot")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
_ = vkey // give to verifiers
|
||||
|
||||
me, err := note.NewSigner(skey)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
msg, err = note.Sign(n, me)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
os.Stdout.Write(msg)
|
||||
|
||||
// Output:
|
||||
// If you think cryptography is the answer to your problem,
|
||||
// then you don't know what your problem is.
|
||||
//
|
||||
// — PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=
|
||||
// — EnochRoot rwz+eBzmZa0SO3NbfRGzPCpDckykFXSdeX+MNtCOXm2/5n2tiOHp+vAF1aGrQ5ovTG01oOTGwnWLox33WWd1RvMc+QQ=
|
||||
}
|
||||
|
|
@ -1,472 +0,0 @@
|
|||
// Copyright 2019 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 note
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/iotest"
|
||||
)
|
||||
|
||||
func TestNewVerifier(t *testing.T) {
|
||||
vkey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW"
|
||||
_, err := NewVerifier(vkey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check various manglings are not accepted.
|
||||
badKey := func(k string) {
|
||||
_, err := NewVerifier(k)
|
||||
if err == nil {
|
||||
t.Errorf("NewVerifier(%q) succeeded, should have failed", k)
|
||||
}
|
||||
}
|
||||
|
||||
b := []byte(vkey)
|
||||
for i := 0; i <= len(b); i++ {
|
||||
for j := i + 1; j <= len(b); j++ {
|
||||
if i != 0 || j != len(b) {
|
||||
badKey(string(b[i:j]))
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i]++
|
||||
badKey(string(b))
|
||||
b[i]--
|
||||
}
|
||||
|
||||
badKey("PeterNeumann+cc469956+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TWBADKEY==") // wrong length key, with adjusted key hash
|
||||
badKey("PeterNeumann+173116ae+ZRpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW") // unknown algorithm, with adjusted key hash
|
||||
}
|
||||
|
||||
func TestNewSigner(t *testing.T) {
|
||||
skey := "PRIVATE+KEY+PeterNeumann+c74f20a3+AYEKFALVFGyNhPJEMzD1QIDr+Y7hfZx09iUvxdXHKDFz"
|
||||
_, err := NewSigner(skey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check various manglings are not accepted.
|
||||
b := []byte(skey)
|
||||
for i := 0; i <= len(b); i++ {
|
||||
for j := i + 1; j <= len(b); j++ {
|
||||
if i == 0 && j == len(b) {
|
||||
continue
|
||||
}
|
||||
_, err := NewSigner(string(b[i:j]))
|
||||
if err == nil {
|
||||
t.Errorf("NewSigner(%q) succeeded, should have failed", b[i:j])
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i]++
|
||||
_, err := NewSigner(string(b))
|
||||
if err == nil {
|
||||
t.Errorf("NewSigner(%q) succeeded, should have failed", b)
|
||||
}
|
||||
b[i]--
|
||||
}
|
||||
}
|
||||
|
||||
func testSignerAndVerifier(t *testing.T, Name string, signer Signer, verifier Verifier) {
|
||||
if name := signer.Name(); name != Name {
|
||||
t.Errorf("signer.Name() = %q, want %q", name, Name)
|
||||
}
|
||||
if name := verifier.Name(); name != Name {
|
||||
t.Errorf("verifier.Name() = %q, want %q", name, Name)
|
||||
}
|
||||
shash := signer.KeyHash()
|
||||
vhash := verifier.KeyHash()
|
||||
if shash != vhash {
|
||||
t.Errorf("signer.KeyHash() = %#08x != verifier.KeyHash() = %#08x", shash, vhash)
|
||||
}
|
||||
|
||||
msg := []byte("hi")
|
||||
sig, err := signer.Sign(msg)
|
||||
if err != nil {
|
||||
t.Fatalf("signer.Sign: %v", err)
|
||||
}
|
||||
if !verifier.Verify(msg, sig) {
|
||||
t.Fatalf("verifier.Verify failed on signature returned by signer.Sign")
|
||||
}
|
||||
sig[0]++
|
||||
if verifier.Verify(msg, sig) {
|
||||
t.Fatalf("verifier.Verify succceeded on corrupt signature")
|
||||
}
|
||||
sig[0]--
|
||||
msg[0]++
|
||||
if verifier.Verify(msg, sig) {
|
||||
t.Fatalf("verifier.Verify succceeded on corrupt message")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateKey(t *testing.T) {
|
||||
// Generate key pair, make sure it is all self-consistent.
|
||||
const Name = "EnochRoot"
|
||||
|
||||
skey, vkey, err := GenerateKey(rand.Reader, Name)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKey: %v", err)
|
||||
}
|
||||
signer, err := NewSigner(skey)
|
||||
if err != nil {
|
||||
t.Fatalf("NewSigner: %v", err)
|
||||
}
|
||||
verifier, err := NewVerifier(vkey)
|
||||
if err != nil {
|
||||
t.Fatalf("NewVerifier: %v", err)
|
||||
}
|
||||
|
||||
testSignerAndVerifier(t, Name, signer, verifier)
|
||||
|
||||
// Check that GenerateKey returns error from rand reader.
|
||||
_, _, err = GenerateKey(iotest.TimeoutReader(iotest.OneByteReader(rand.Reader)), Name)
|
||||
if err == nil {
|
||||
t.Fatalf("GenerateKey succeeded with error-returning rand reader")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromEd25519(t *testing.T) {
|
||||
const Name = "EnochRoot"
|
||||
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKey: %v", err)
|
||||
}
|
||||
signer, err := newSignerFromEd25519Seed(Name, priv.Seed())
|
||||
if err != nil {
|
||||
t.Fatalf("newSignerFromEd25519Seed: %v", err)
|
||||
}
|
||||
vkey, err := NewEd25519VerifierKey(Name, pub)
|
||||
if err != nil {
|
||||
t.Fatalf("NewEd25519VerifierKey: %v", err)
|
||||
}
|
||||
verifier, err := NewVerifier(vkey)
|
||||
if err != nil {
|
||||
t.Fatalf("NewVerifier: %v", err)
|
||||
}
|
||||
|
||||
testSignerAndVerifier(t, Name, signer, verifier)
|
||||
|
||||
// Check that wrong key sizes return errors.
|
||||
_, err = NewEd25519VerifierKey(Name, pub[:len(pub)-1])
|
||||
if err == nil {
|
||||
t.Errorf("NewEd25519VerifierKey succeeded with a seed of the wrong size")
|
||||
}
|
||||
}
|
||||
|
||||
// newSignerFromEd25519Seed constructs a new signer from a verifier name and a
|
||||
// crypto/ed25519 private key seed.
|
||||
func newSignerFromEd25519Seed(name string, seed []byte) (Signer, error) {
|
||||
if len(seed) != ed25519.SeedSize {
|
||||
return nil, errors.New("invalid seed size")
|
||||
}
|
||||
priv := ed25519.NewKeyFromSeed(seed)
|
||||
pub := priv[32:]
|
||||
|
||||
pubkey := append([]byte{algEd25519}, pub...)
|
||||
hash := keyHash(name, pubkey)
|
||||
|
||||
s := &signer{
|
||||
name: name,
|
||||
hash: uint32(hash),
|
||||
sign: func(msg []byte) ([]byte, error) {
|
||||
return ed25519.Sign(priv, msg), nil
|
||||
},
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
skey := "PRIVATE+KEY+PeterNeumann+c74f20a3+AYEKFALVFGyNhPJEMzD1QIDr+Y7hfZx09iUvxdXHKDFz"
|
||||
text := "If you think cryptography is the answer to your problem,\n" +
|
||||
"then you don't know what your problem is.\n"
|
||||
|
||||
signer, err := NewSigner(skey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg, err := Sign(&Note{Text: text}, signer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := `If you think cryptography is the answer to your problem,
|
||||
then you don't know what your problem is.
|
||||
|
||||
— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=
|
||||
`
|
||||
if string(msg) != want {
|
||||
t.Errorf("Sign: wrong output\nhave:\n%s\nwant:\n%s", msg, want)
|
||||
}
|
||||
|
||||
// Check that existing signature is replaced by new one.
|
||||
msg, err = Sign(&Note{Text: text, Sigs: []Signature{{Name: "PeterNeumann", Hash: 0xc74f20a3, Base64: "BADSIGN="}}}, signer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(msg) != want {
|
||||
t.Errorf("Sign replacing signature: wrong output\nhave:\n%s\nwant:\n%s", msg, want)
|
||||
}
|
||||
|
||||
// Check various bad inputs.
|
||||
_, err = Sign(&Note{Text: "abc"}, signer)
|
||||
if err == nil || err.Error() != "malformed note" {
|
||||
t.Fatalf("Sign with short text: %v, want malformed note error", err)
|
||||
}
|
||||
|
||||
_, err = Sign(&Note{Text: text, Sigs: []Signature{{Name: "a+b", Base64: "ABCD"}}})
|
||||
if err == nil || err.Error() != "malformed note" {
|
||||
t.Fatalf("Sign with bad name: %v, want malformed note error", err)
|
||||
}
|
||||
|
||||
_, err = Sign(&Note{Text: text, Sigs: []Signature{{Name: "PeterNeumann", Hash: 0xc74f20a3, Base64: "BADHASH="}}})
|
||||
if err == nil || err.Error() != "malformed note" {
|
||||
t.Fatalf("Sign with bad pre-filled signature: %v, want malformed note error", err)
|
||||
}
|
||||
|
||||
_, err = Sign(&Note{Text: text}, &badSigner{signer})
|
||||
if err == nil || err.Error() != "invalid signer" {
|
||||
t.Fatalf("Sign with bad signer: %v, want invalid signer error", err)
|
||||
}
|
||||
|
||||
_, err = Sign(&Note{Text: text}, &errSigner{signer})
|
||||
if err != errSurprise {
|
||||
t.Fatalf("Sign with failing signer: %v, want errSurprise", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifierList(t *testing.T) {
|
||||
peterKey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW"
|
||||
peterVerifier, err := NewVerifier(peterKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
enochKey := "EnochRoot+af0cfe78+ATtqJ7zOtqQtYqOo0CpvDXNlMhV3HeJDpjrASKGLWdop"
|
||||
enochVerifier, err := NewVerifier(enochKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
list := VerifierList(peterVerifier, enochVerifier, enochVerifier)
|
||||
v, err := list.Verifier("PeterNeumann", 0xc74f20a3)
|
||||
if v != peterVerifier || err != nil {
|
||||
t.Fatalf("list.Verifier(peter) = %v, %v, want %v, nil", v, err, peterVerifier)
|
||||
}
|
||||
v, err = list.Verifier("PeterNeumann", 0xc74f20a4)
|
||||
if v != nil || err == nil || err.Error() != "unknown key PeterNeumann+c74f20a4" {
|
||||
t.Fatalf("list.Verifier(peter bad hash) = %v, %v, want nil, unknown key error", v, err)
|
||||
}
|
||||
|
||||
v, err = list.Verifier("PeterNeuman", 0xc74f20a3)
|
||||
if v != nil || err == nil || err.Error() != "unknown key PeterNeuman+c74f20a3" {
|
||||
t.Fatalf("list.Verifier(peter bad name) = %v, %v, want nil, unknown key error", v, err)
|
||||
}
|
||||
v, err = list.Verifier("EnochRoot", 0xaf0cfe78)
|
||||
if v != nil || err == nil || err.Error() != "ambiguous key EnochRoot+af0cfe78" {
|
||||
t.Fatalf("list.Verifier(enoch) = %v, %v, want nil, ambiguous key error", v, err)
|
||||
}
|
||||
}
|
||||
|
||||
type badSigner struct {
|
||||
Signer
|
||||
}
|
||||
|
||||
func (b *badSigner) Name() string {
|
||||
return "bad name"
|
||||
}
|
||||
|
||||
var errSurprise = errors.New("surprise!")
|
||||
|
||||
type errSigner struct {
|
||||
Signer
|
||||
}
|
||||
|
||||
func (e *errSigner) Sign([]byte) ([]byte, error) {
|
||||
return nil, errSurprise
|
||||
}
|
||||
|
||||
func TestOpen(t *testing.T) {
|
||||
peterKey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW"
|
||||
peterVerifier, err := NewVerifier(peterKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
enochKey := "EnochRoot+af0cfe78+ATtqJ7zOtqQtYqOo0CpvDXNlMhV3HeJDpjrASKGLWdop"
|
||||
enochVerifier, err := NewVerifier(enochKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
text := `If you think cryptography is the answer to your problem,
|
||||
then you don't know what your problem is.
|
||||
`
|
||||
peterSig := "— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=\n"
|
||||
enochSig := "— EnochRoot rwz+eBzmZa0SO3NbfRGzPCpDckykFXSdeX+MNtCOXm2/5n2tiOHp+vAF1aGrQ5ovTG01oOTGwnWLox33WWd1RvMc+QQ=\n"
|
||||
|
||||
peter := Signature{"PeterNeumann", 0xc74f20a3, "x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM="}
|
||||
enoch := Signature{"EnochRoot", 0xaf0cfe78, "rwz+eBzmZa0SO3NbfRGzPCpDckykFXSdeX+MNtCOXm2/5n2tiOHp+vAF1aGrQ5ovTG01oOTGwnWLox33WWd1RvMc+QQ="}
|
||||
|
||||
// Check one signature verified, one not.
|
||||
n, err := Open([]byte(text+"\n"+peterSig+enochSig), VerifierList(peterVerifier))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n.Text != text {
|
||||
t.Errorf("n.Text = %q, want %q", n.Text, text)
|
||||
}
|
||||
if len(n.Sigs) != 1 || n.Sigs[0] != peter {
|
||||
t.Errorf("n.Sigs:\nhave %v\nwant %v", n.Sigs, []Signature{peter})
|
||||
}
|
||||
if len(n.UnverifiedSigs) != 1 || n.UnverifiedSigs[0] != enoch {
|
||||
t.Errorf("n.UnverifiedSigs:\nhave %v\nwant %v", n.Sigs, []Signature{peter})
|
||||
}
|
||||
|
||||
// Check both verified.
|
||||
n, err = Open([]byte(text+"\n"+peterSig+enochSig), VerifierList(peterVerifier, enochVerifier))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(n.Sigs) != 2 || n.Sigs[0] != peter || n.Sigs[1] != enoch {
|
||||
t.Errorf("n.Sigs:\nhave %v\nwant %v", n.Sigs, []Signature{peter, enoch})
|
||||
}
|
||||
if len(n.UnverifiedSigs) != 0 {
|
||||
t.Errorf("n.UnverifiedSigs:\nhave %v\nwant %v", n.Sigs, []Signature{})
|
||||
}
|
||||
|
||||
// Check both unverified.
|
||||
n, err = Open([]byte(text+"\n"+peterSig+enochSig), VerifierList())
|
||||
if n != nil || err == nil {
|
||||
t.Fatalf("Open unverified = %v, %v, want nil, error", n, err)
|
||||
}
|
||||
e, ok := err.(*UnverifiedNoteError)
|
||||
if !ok {
|
||||
t.Fatalf("Open unverified: err is %T, want *UnverifiedNoteError", err)
|
||||
}
|
||||
if err.Error() != "note has no verifiable signatures" {
|
||||
t.Fatalf("Open unverified: err.Error() = %q, want %q", err.Error(), "note has no verifiable signatures")
|
||||
}
|
||||
|
||||
n = e.Note
|
||||
if n == nil {
|
||||
t.Fatalf("Open unverified: missing note in UnverifiedNoteError")
|
||||
}
|
||||
if len(n.Sigs) != 0 {
|
||||
t.Errorf("n.Sigs:\nhave %v\nwant %v", n.Sigs, []Signature{})
|
||||
}
|
||||
if len(n.UnverifiedSigs) != 2 || n.UnverifiedSigs[0] != peter || n.UnverifiedSigs[1] != enoch {
|
||||
t.Errorf("n.UnverifiedSigs:\nhave %v\nwant %v", n.Sigs, []Signature{peter, enoch})
|
||||
}
|
||||
|
||||
// Check duplicated verifier.
|
||||
_, err = Open([]byte(text+"\n"+enochSig), VerifierList(enochVerifier, peterVerifier, enochVerifier))
|
||||
if err == nil || err.Error() != "ambiguous key EnochRoot+af0cfe78" {
|
||||
t.Fatalf("Open with duplicated verifier: err=%v, want ambiguous key", err)
|
||||
}
|
||||
|
||||
// Check unused duplicated verifier.
|
||||
_, err = Open([]byte(text+"\n"+peterSig), VerifierList(enochVerifier, peterVerifier, enochVerifier))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check too many signatures.
|
||||
n, err = Open([]byte(text+"\n"+strings.Repeat(peterSig, 101)), VerifierList(peterVerifier))
|
||||
if n != nil || err == nil || err.Error() != "malformed note" {
|
||||
t.Fatalf("Open too many verified signatures = %v, %v, want nil, malformed note error", n, err)
|
||||
}
|
||||
n, err = Open([]byte(text+"\n"+strings.Repeat(peterSig, 101)), VerifierList())
|
||||
if n != nil || err == nil || err.Error() != "malformed note" {
|
||||
t.Fatalf("Open too many verified signatures = %v, %v, want nil, malformed note error", n, err)
|
||||
}
|
||||
|
||||
// Invalid signature.
|
||||
n, err = Open([]byte(text+"\n"+peterSig[:60]+"ABCD"+peterSig[60:]), VerifierList(peterVerifier))
|
||||
if n != nil || err == nil || err.Error() != "invalid signature for key PeterNeumann+c74f20a3" {
|
||||
t.Fatalf("Open too many verified signatures = %v, %v, want nil, invalid signature error", n, err)
|
||||
}
|
||||
|
||||
// Duplicated verified and unverified signatures.
|
||||
enochABCD := Signature{"EnochRoot", 0xaf0cfe78, "rwz+eBzmZa0SO3NbfRGzPCpDckykFXSdeX+MNtCOXm2/5n" + "ABCD" + "2tiOHp+vAF1aGrQ5ovTG01oOTGwnWLox33WWd1RvMc+QQ="}
|
||||
n, err = Open([]byte(text+"\n"+peterSig+peterSig+enochSig+enochSig+enochSig[:60]+"ABCD"+enochSig[60:]), VerifierList(peterVerifier))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(n.Sigs) != 1 || n.Sigs[0] != peter {
|
||||
t.Errorf("n.Sigs:\nhave %v\nwant %v", n.Sigs, []Signature{peter})
|
||||
}
|
||||
if len(n.UnverifiedSigs) != 2 || n.UnverifiedSigs[0] != enoch || n.UnverifiedSigs[1] != enochABCD {
|
||||
t.Errorf("n.UnverifiedSigs:\nhave %v\nwant %v", n.UnverifiedSigs, []Signature{enoch, enochABCD})
|
||||
}
|
||||
|
||||
// Invalid encoded message syntax.
|
||||
badMsgs := []string{
|
||||
text,
|
||||
text + "\n",
|
||||
text + "\n" + peterSig[:len(peterSig)-1],
|
||||
"\x01" + text + "\n" + peterSig,
|
||||
"\xff" + text + "\n" + peterSig,
|
||||
text + "\n" + "— Bad Name x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=",
|
||||
text + "\n" + peterSig + "Unexpected line.\n",
|
||||
}
|
||||
for _, msg := range badMsgs {
|
||||
n, err := Open([]byte(msg), VerifierList(peterVerifier))
|
||||
if n != nil || err == nil || err.Error() != "malformed note" {
|
||||
t.Fatalf("Open bad msg = %v, %v, want nil, malformed note error\nmsg:\n%s", n, err, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkOpen(b *testing.B) {
|
||||
vkey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW"
|
||||
msg := []byte("If you think cryptography is the answer to your problem,\n" +
|
||||
"then you don't know what your problem is.\n" +
|
||||
"\n" +
|
||||
"— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=\n")
|
||||
|
||||
verifier, err := NewVerifier(vkey)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
verifiers := VerifierList(verifier)
|
||||
verifiers0 := VerifierList()
|
||||
|
||||
// Try with 0 signatures and 1 signature so we can tell how much each signature adds.
|
||||
|
||||
b.Run("Sig0", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := Open(msg, verifiers0)
|
||||
e, ok := err.(*UnverifiedNoteError)
|
||||
if !ok {
|
||||
b.Fatal("expected UnverifiedNoteError")
|
||||
}
|
||||
n := e.Note
|
||||
if len(n.Sigs) != 0 || len(n.UnverifiedSigs) != 1 {
|
||||
b.Fatal("wrong signature count")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("Sig1", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
n, err := Open(msg, verifiers)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if len(n.Sigs) != 1 || len(n.UnverifiedSigs) != 0 {
|
||||
b.Fatal("wrong signature count")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
// 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 semver
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var tests = []struct {
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{"bad", ""},
|
||||
{"v1-alpha.beta.gamma", ""},
|
||||
{"v1-pre", ""},
|
||||
{"v1+meta", ""},
|
||||
{"v1-pre+meta", ""},
|
||||
{"v1.2-pre", ""},
|
||||
{"v1.2+meta", ""},
|
||||
{"v1.2-pre+meta", ""},
|
||||
{"v1.0.0-alpha", "v1.0.0-alpha"},
|
||||
{"v1.0.0-alpha.1", "v1.0.0-alpha.1"},
|
||||
{"v1.0.0-alpha.beta", "v1.0.0-alpha.beta"},
|
||||
{"v1.0.0-beta", "v1.0.0-beta"},
|
||||
{"v1.0.0-beta.2", "v1.0.0-beta.2"},
|
||||
{"v1.0.0-beta.11", "v1.0.0-beta.11"},
|
||||
{"v1.0.0-rc.1", "v1.0.0-rc.1"},
|
||||
{"v1", "v1.0.0"},
|
||||
{"v1.0", "v1.0.0"},
|
||||
{"v1.0.0", "v1.0.0"},
|
||||
{"v1.2", "v1.2.0"},
|
||||
{"v1.2.0", "v1.2.0"},
|
||||
{"v1.2.3-456", "v1.2.3-456"},
|
||||
{"v1.2.3-456.789", "v1.2.3-456.789"},
|
||||
{"v1.2.3-456-789", "v1.2.3-456-789"},
|
||||
{"v1.2.3-456a", "v1.2.3-456a"},
|
||||
{"v1.2.3-pre", "v1.2.3-pre"},
|
||||
{"v1.2.3-pre+meta", "v1.2.3-pre"},
|
||||
{"v1.2.3-pre.1", "v1.2.3-pre.1"},
|
||||
{"v1.2.3-zzz", "v1.2.3-zzz"},
|
||||
{"v1.2.3", "v1.2.3"},
|
||||
{"v1.2.3+meta", "v1.2.3"},
|
||||
{"v1.2.3+meta-pre", "v1.2.3"},
|
||||
{"v1.2.3+meta-pre.sha.256a", "v1.2.3"},
|
||||
}
|
||||
|
||||
func TestIsValid(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
ok := IsValid(tt.in)
|
||||
if ok != (tt.out != "") {
|
||||
t.Errorf("IsValid(%q) = %v, want %v", tt.in, ok, !ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonical(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
out := Canonical(tt.in)
|
||||
if out != tt.out {
|
||||
t.Errorf("Canonical(%q) = %q, want %q", tt.in, out, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMajor(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
out := Major(tt.in)
|
||||
want := ""
|
||||
if i := strings.Index(tt.out, "."); i >= 0 {
|
||||
want = tt.out[:i]
|
||||
}
|
||||
if out != want {
|
||||
t.Errorf("Major(%q) = %q, want %q", tt.in, out, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMajorMinor(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
out := MajorMinor(tt.in)
|
||||
var want string
|
||||
if tt.out != "" {
|
||||
want = tt.in
|
||||
if i := strings.Index(want, "+"); i >= 0 {
|
||||
want = want[:i]
|
||||
}
|
||||
if i := strings.Index(want, "-"); i >= 0 {
|
||||
want = want[:i]
|
||||
}
|
||||
switch strings.Count(want, ".") {
|
||||
case 0:
|
||||
want += ".0"
|
||||
case 1:
|
||||
// ok
|
||||
case 2:
|
||||
want = want[:strings.LastIndex(want, ".")]
|
||||
}
|
||||
}
|
||||
if out != want {
|
||||
t.Errorf("MajorMinor(%q) = %q, want %q", tt.in, out, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrerelease(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
pre := Prerelease(tt.in)
|
||||
var want string
|
||||
if tt.out != "" {
|
||||
if i := strings.Index(tt.out, "-"); i >= 0 {
|
||||
want = tt.out[i:]
|
||||
}
|
||||
}
|
||||
if pre != want {
|
||||
t.Errorf("Prerelease(%q) = %q, want %q", tt.in, pre, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuild(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
build := Build(tt.in)
|
||||
var want string
|
||||
if tt.out != "" {
|
||||
if i := strings.Index(tt.in, "+"); i >= 0 {
|
||||
want = tt.in[i:]
|
||||
}
|
||||
}
|
||||
if build != want {
|
||||
t.Errorf("Build(%q) = %q, want %q", tt.in, build, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompare(t *testing.T) {
|
||||
for i, ti := range tests {
|
||||
for j, tj := range tests {
|
||||
cmp := Compare(ti.in, tj.in)
|
||||
var want int
|
||||
if ti.out == tj.out {
|
||||
want = 0
|
||||
} else if i < j {
|
||||
want = -1
|
||||
} else {
|
||||
want = +1
|
||||
}
|
||||
if cmp != want {
|
||||
t.Errorf("Compare(%q, %q) = %d, want %d", ti.in, tj.in, cmp, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMax(t *testing.T) {
|
||||
for i, ti := range tests {
|
||||
for j, tj := range tests {
|
||||
max := Max(ti.in, tj.in)
|
||||
want := Canonical(ti.in)
|
||||
if i < j {
|
||||
want = Canonical(tj.in)
|
||||
}
|
||||
if max != want {
|
||||
t.Errorf("Max(%q, %q) = %q, want %q", ti.in, tj.in, max, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
v1 = "v1.0.0+metadata-dash"
|
||||
v2 = "v1.0.0+metadata-dash1"
|
||||
)
|
||||
|
||||
func BenchmarkCompare(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if Compare(v1, v2) != 0 {
|
||||
b.Fatalf("bad compare")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,460 +0,0 @@
|
|||
// Copyright 2019 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 sumdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"cmd/go/internal/note"
|
||||
"cmd/go/internal/tlog"
|
||||
)
|
||||
|
||||
const (
|
||||
testName = "localhost.localdev/sumdb"
|
||||
testVerifierKey = "localhost.localdev/sumdb+00000c67+AcTrnkbUA+TU4heY3hkjiSES/DSQniBqIeQ/YppAUtK6"
|
||||
testSignerKey = "PRIVATE+KEY+localhost.localdev/sumdb+00000c67+AXu6+oaVaOYuQOFrf1V59JK1owcFlJcHwwXHDfDGxSPk"
|
||||
)
|
||||
|
||||
func TestClientLookup(t *testing.T) {
|
||||
tc := newTestClient(t)
|
||||
tc.mustHaveLatest(1)
|
||||
|
||||
// Basic lookup.
|
||||
tc.mustLookup("rsc.io/sampler", "v1.3.0", "rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=")
|
||||
tc.mustHaveLatest(3)
|
||||
|
||||
// Everything should now be cached, both for the original package and its /go.mod.
|
||||
tc.getOK = false
|
||||
tc.mustLookup("rsc.io/sampler", "v1.3.0", "rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=")
|
||||
tc.mustLookup("rsc.io/sampler", "v1.3.0/go.mod", "rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=")
|
||||
tc.mustHaveLatest(3)
|
||||
tc.getOK = true
|
||||
tc.getTileOK = false // the cache has what we need
|
||||
|
||||
// Lookup with multiple returned lines.
|
||||
tc.mustLookup("rsc.io/quote", "v1.5.2", "rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=\nrsc.io/quote v1.5.2 h2:xyzzy")
|
||||
tc.mustHaveLatest(3)
|
||||
|
||||
// Lookup with need for !-encoding.
|
||||
// rsc.io/Quote is the only record written after rsc.io/samper,
|
||||
// so it is the only one that should need more tiles.
|
||||
tc.getTileOK = true
|
||||
tc.mustLookup("rsc.io/Quote", "v1.5.2", "rsc.io/Quote v1.5.2 h1:uppercase!=")
|
||||
tc.mustHaveLatest(4)
|
||||
}
|
||||
|
||||
func TestClientBadTiles(t *testing.T) {
|
||||
tc := newTestClient(t)
|
||||
|
||||
flipBits := func() {
|
||||
for url, data := range tc.remote {
|
||||
if strings.Contains(url, "/tile/") {
|
||||
for i := range data {
|
||||
data[i] ^= 0x80
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bad tiles in initial download.
|
||||
tc.mustHaveLatest(1)
|
||||
flipBits()
|
||||
_, err := tc.client.Lookup("rsc.io/sampler", "v1.3.0")
|
||||
tc.mustError(err, "rsc.io/sampler@v1.3.0: initializing sumdb.Client: checking tree#1: downloaded inconsistent tile")
|
||||
flipBits()
|
||||
tc.newClient()
|
||||
tc.mustLookup("rsc.io/sampler", "v1.3.0", "rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=")
|
||||
|
||||
// Bad tiles after initial download.
|
||||
flipBits()
|
||||
_, err = tc.client.Lookup("rsc.io/Quote", "v1.5.2")
|
||||
tc.mustError(err, "rsc.io/Quote@v1.5.2: checking tree#3 against tree#4: downloaded inconsistent tile")
|
||||
flipBits()
|
||||
tc.newClient()
|
||||
tc.mustLookup("rsc.io/Quote", "v1.5.2", "rsc.io/Quote v1.5.2 h1:uppercase!=")
|
||||
|
||||
// Bad starting tree hash looks like bad tiles.
|
||||
tc.newClient()
|
||||
text := tlog.FormatTree(tlog.Tree{N: 1, Hash: tlog.Hash{}})
|
||||
data, err := note.Sign(¬e.Note{Text: string(text)}, tc.signer)
|
||||
if err != nil {
|
||||
tc.t.Fatal(err)
|
||||
}
|
||||
tc.config[testName+"/latest"] = data
|
||||
_, err = tc.client.Lookup("rsc.io/sampler", "v1.3.0")
|
||||
tc.mustError(err, "rsc.io/sampler@v1.3.0: initializing sumdb.Client: checking tree#1: downloaded inconsistent tile")
|
||||
}
|
||||
|
||||
func TestClientFork(t *testing.T) {
|
||||
tc := newTestClient(t)
|
||||
tc2 := tc.fork()
|
||||
|
||||
tc.addRecord("rsc.io/pkg1@v1.5.2", `rsc.io/pkg1 v1.5.2 h1:hash!=
|
||||
`)
|
||||
tc.addRecord("rsc.io/pkg1@v1.5.4", `rsc.io/pkg1 v1.5.4 h1:hash!=
|
||||
`)
|
||||
tc.mustLookup("rsc.io/pkg1", "v1.5.2", "rsc.io/pkg1 v1.5.2 h1:hash!=")
|
||||
|
||||
tc2.addRecord("rsc.io/pkg1@v1.5.3", `rsc.io/pkg1 v1.5.3 h1:hash!=
|
||||
`)
|
||||
tc2.addRecord("rsc.io/pkg1@v1.5.4", `rsc.io/pkg1 v1.5.4 h1:hash!=
|
||||
`)
|
||||
tc2.mustLookup("rsc.io/pkg1", "v1.5.4", "rsc.io/pkg1 v1.5.4 h1:hash!=")
|
||||
|
||||
key := "/lookup/rsc.io/pkg1@v1.5.2"
|
||||
tc2.remote[key] = tc.remote[key]
|
||||
_, err := tc2.client.Lookup("rsc.io/pkg1", "v1.5.2")
|
||||
tc2.mustError(err, ErrSecurity.Error())
|
||||
|
||||
/*
|
||||
SECURITY ERROR
|
||||
go.sum database server misbehavior detected!
|
||||
|
||||
old database:
|
||||
go.sum database tree!
|
||||
5
|
||||
nWzN20+pwMt62p7jbv1/NlN95ePTlHijabv5zO/s36w=
|
||||
|
||||
— localhost.localdev/sumdb AAAMZ5/2FVAdMH58kmnz/0h299pwyskEbzDzoa2/YaPdhvLya4YWDFQQxu2TQb5GpwAH4NdWnTwuhILafisyf3CNbgg=
|
||||
|
||||
new database:
|
||||
go.sum database tree
|
||||
6
|
||||
wc4SkQt52o5W2nQ8To2ARs+mWuUJjss+sdleoiqxMmM=
|
||||
|
||||
— localhost.localdev/sumdb AAAMZ6oRNswlEZ6ZZhxrCvgl1MBy+nusq4JU+TG6Fe2NihWLqOzb+y2c2kzRLoCr4tvw9o36ucQEnhc20e4nA4Qc/wc=
|
||||
|
||||
proof of misbehavior:
|
||||
T7i+H/8ER4nXOiw4Bj0koZOkGjkxoNvlI34GpvhHhQg=
|
||||
Nsuejv72de9hYNM5bqFv8rv3gm3zJQwv/DT/WNbLDLA=
|
||||
mOmqqZ1aI/lzS94oq/JSbj7pD8Rv9S+xDyi12BtVSHo=
|
||||
/7Aw5jVSMM9sFjQhaMg+iiDYPMk6decH7QLOGrL9Lx0=
|
||||
*/
|
||||
|
||||
wants := []string{
|
||||
"SECURITY ERROR",
|
||||
"go.sum database server misbehavior detected!",
|
||||
"old database:\n\tgo.sum database tree\n\t5\n",
|
||||
"— localhost.localdev/sumdb AAAMZ5/2FVAd",
|
||||
"new database:\n\tgo.sum database tree\n\t6\n",
|
||||
"— localhost.localdev/sumdb AAAMZ6oRNswl",
|
||||
"proof of misbehavior:\n\tT7i+H/8ER4nXOiw4Bj0k",
|
||||
}
|
||||
text := tc2.security.String()
|
||||
for _, want := range wants {
|
||||
if !strings.Contains(text, want) {
|
||||
t.Fatalf("cannot find %q in security text:\n%s", want, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientGONOSUMDB(t *testing.T) {
|
||||
tc := newTestClient(t)
|
||||
tc.client.SetGONOSUMDB("p,*/q")
|
||||
tc.client.Lookup("rsc.io/sampler", "v1.3.0") // initialize before we turn off network
|
||||
tc.getOK = false
|
||||
|
||||
ok := []string{
|
||||
"abc",
|
||||
"a/p",
|
||||
"pq",
|
||||
"q",
|
||||
"n/o/p/q",
|
||||
}
|
||||
skip := []string{
|
||||
"p",
|
||||
"p/x",
|
||||
"x/q",
|
||||
"x/q/z",
|
||||
}
|
||||
|
||||
for _, path := range ok {
|
||||
_, err := tc.client.Lookup(path, "v1.0.0")
|
||||
if err == ErrGONOSUMDB {
|
||||
t.Errorf("Lookup(%q): ErrGONOSUMDB, wanted failed actual lookup", path)
|
||||
}
|
||||
}
|
||||
for _, path := range skip {
|
||||
_, err := tc.client.Lookup(path, "v1.0.0")
|
||||
if err != ErrGONOSUMDB {
|
||||
t.Errorf("Lookup(%q): %v, wanted ErrGONOSUMDB", path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A testClient is a self-contained client-side testing environment.
|
||||
type testClient struct {
|
||||
t *testing.T // active test
|
||||
client *Client // client being tested
|
||||
tileHeight int // tile height to use (default 2)
|
||||
getOK bool // should tc.GetURL succeed?
|
||||
getTileOK bool // should tc.GetURL of tiles succeed?
|
||||
treeSize int64
|
||||
hashes []tlog.Hash
|
||||
remote map[string][]byte
|
||||
signer note.Signer
|
||||
|
||||
// mu protects config, cache, log, security
|
||||
// during concurrent use of the exported methods
|
||||
// by the client itself (testClient is the Client's ClientOps,
|
||||
// and the Client methods can both read and write these fields).
|
||||
// Unexported methods invoked directly by the test
|
||||
// (for example, addRecord) need not hold the mutex:
|
||||
// for proper test execution those methods should only
|
||||
// be called when the Client is idle and not using its ClientOps.
|
||||
// Not holding the mutex in those methods ensures
|
||||
// that if a mistake is made, go test -race will report it.
|
||||
// (Holding the mutex would eliminate the race report but
|
||||
// not the underlying problem.)
|
||||
// Similarly, the get map is not protected by the mutex,
|
||||
// because the Client methods only read it.
|
||||
mu sync.Mutex // prot
|
||||
config map[string][]byte
|
||||
cache map[string][]byte
|
||||
security bytes.Buffer
|
||||
}
|
||||
|
||||
// newTestClient returns a new testClient that will call t.Fatal on error
|
||||
// and has a few records already available on the remote server.
|
||||
func newTestClient(t *testing.T) *testClient {
|
||||
tc := &testClient{
|
||||
t: t,
|
||||
tileHeight: 2,
|
||||
getOK: true,
|
||||
getTileOK: true,
|
||||
config: make(map[string][]byte),
|
||||
cache: make(map[string][]byte),
|
||||
remote: make(map[string][]byte),
|
||||
}
|
||||
|
||||
tc.config["key"] = []byte(testVerifierKey + "\n")
|
||||
var err error
|
||||
tc.signer, err = note.NewSigner(testSignerKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tc.newClient()
|
||||
|
||||
tc.addRecord("rsc.io/quote@v1.5.2", `rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
|
||||
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
|
||||
rsc.io/quote v1.5.2 h2:xyzzy
|
||||
`)
|
||||
|
||||
tc.addRecord("golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c", `golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
`)
|
||||
tc.addRecord("rsc.io/sampler@v1.3.0", `rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
`)
|
||||
tc.config[testName+"/latest"] = tc.signTree(1)
|
||||
|
||||
tc.addRecord("rsc.io/!quote@v1.5.2", `rsc.io/Quote v1.5.2 h1:uppercase!=
|
||||
`)
|
||||
return tc
|
||||
}
|
||||
|
||||
// newClient resets the Client associated with tc.
|
||||
// This clears any in-memory cache from the Client
|
||||
// but not tc's on-disk cache.
|
||||
func (tc *testClient) newClient() {
|
||||
tc.client = NewClient(tc)
|
||||
tc.client.SetTileHeight(tc.tileHeight)
|
||||
}
|
||||
|
||||
// mustLookup does a lookup for path@vers and checks that the lines that come back match want.
|
||||
func (tc *testClient) mustLookup(path, vers, want string) {
|
||||
tc.t.Helper()
|
||||
lines, err := tc.client.Lookup(path, vers)
|
||||
if err != nil {
|
||||
tc.t.Fatal(err)
|
||||
}
|
||||
if strings.Join(lines, "\n") != want {
|
||||
tc.t.Fatalf("Lookup(%q, %q):\n\t%s\nwant:\n\t%s", path, vers, strings.Join(lines, "\n\t"), strings.Replace(want, "\n", "\n\t", -1))
|
||||
}
|
||||
}
|
||||
|
||||
// mustHaveLatest checks that the on-disk configuration
|
||||
// for latest is a tree of size n.
|
||||
func (tc *testClient) mustHaveLatest(n int64) {
|
||||
tc.t.Helper()
|
||||
|
||||
latest := tc.config[testName+"/latest"]
|
||||
lines := strings.Split(string(latest), "\n")
|
||||
if len(lines) < 2 || lines[1] != fmt.Sprint(n) {
|
||||
tc.t.Fatalf("/latest should have tree %d, but has:\n%s", n, latest)
|
||||
}
|
||||
}
|
||||
|
||||
// mustError checks that err's error string contains the text.
|
||||
func (tc *testClient) mustError(err error, text string) {
|
||||
tc.t.Helper()
|
||||
if err == nil || !strings.Contains(err.Error(), text) {
|
||||
tc.t.Fatalf("err = %v, want %q", err, text)
|
||||
}
|
||||
}
|
||||
|
||||
// fork returns a copy of tc.
|
||||
// Changes made to the new copy or to tc are not reflected in the other.
|
||||
func (tc *testClient) fork() *testClient {
|
||||
tc2 := &testClient{
|
||||
t: tc.t,
|
||||
getOK: tc.getOK,
|
||||
getTileOK: tc.getTileOK,
|
||||
tileHeight: tc.tileHeight,
|
||||
treeSize: tc.treeSize,
|
||||
hashes: append([]tlog.Hash{}, tc.hashes...),
|
||||
signer: tc.signer,
|
||||
config: copyMap(tc.config),
|
||||
cache: copyMap(tc.cache),
|
||||
remote: copyMap(tc.remote),
|
||||
}
|
||||
tc2.newClient()
|
||||
return tc2
|
||||
}
|
||||
|
||||
func copyMap(m map[string][]byte) map[string][]byte {
|
||||
m2 := make(map[string][]byte)
|
||||
for k, v := range m {
|
||||
m2[k] = v
|
||||
}
|
||||
return m2
|
||||
}
|
||||
|
||||
// ReadHashes is tc's implementation of tlog.HashReader, for use with
|
||||
// tlog.TreeHash and so on.
|
||||
func (tc *testClient) ReadHashes(indexes []int64) ([]tlog.Hash, error) {
|
||||
var list []tlog.Hash
|
||||
for _, id := range indexes {
|
||||
list = append(list, tc.hashes[id])
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// addRecord adds a log record using the given (!-encoded) key and data.
|
||||
func (tc *testClient) addRecord(key, data string) {
|
||||
tc.t.Helper()
|
||||
|
||||
// Create record, add hashes to log tree.
|
||||
id := tc.treeSize
|
||||
tc.treeSize++
|
||||
rec, err := tlog.FormatRecord(id, []byte(data))
|
||||
if err != nil {
|
||||
tc.t.Fatal(err)
|
||||
}
|
||||
hashes, err := tlog.StoredHashesForRecordHash(id, tlog.RecordHash([]byte(data)), tc)
|
||||
if err != nil {
|
||||
tc.t.Fatal(err)
|
||||
}
|
||||
tc.hashes = append(tc.hashes, hashes...)
|
||||
|
||||
// Create lookup result.
|
||||
tc.remote["/lookup/"+key] = append(rec, tc.signTree(tc.treeSize)...)
|
||||
|
||||
// Create new tiles.
|
||||
tiles := tlog.NewTiles(tc.tileHeight, id, tc.treeSize)
|
||||
for _, tile := range tiles {
|
||||
data, err := tlog.ReadTileData(tile, tc)
|
||||
if err != nil {
|
||||
tc.t.Fatal(err)
|
||||
}
|
||||
tc.remote["/"+tile.Path()] = data
|
||||
// TODO delete old partial tiles
|
||||
}
|
||||
}
|
||||
|
||||
// signTree returns the signed head for the tree of the given size.
|
||||
func (tc *testClient) signTree(size int64) []byte {
|
||||
h, err := tlog.TreeHash(size, tc)
|
||||
if err != nil {
|
||||
tc.t.Fatal(err)
|
||||
}
|
||||
text := tlog.FormatTree(tlog.Tree{N: size, Hash: h})
|
||||
data, err := note.Sign(¬e.Note{Text: string(text)}, tc.signer)
|
||||
if err != nil {
|
||||
tc.t.Fatal(err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadRemote is for tc's implementation of Client.
|
||||
func (tc *testClient) ReadRemote(path string) ([]byte, error) {
|
||||
// No mutex here because only the Client should be running
|
||||
// and the Client cannot change tc.get.
|
||||
if !tc.getOK {
|
||||
return nil, fmt.Errorf("disallowed remote read %s", path)
|
||||
}
|
||||
if strings.Contains(path, "/tile/") && !tc.getTileOK {
|
||||
return nil, fmt.Errorf("disallowed remote tile read %s", path)
|
||||
}
|
||||
|
||||
data, ok := tc.remote[path]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no remote path %s", path)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// ReadConfig is for tc's implementation of Client.
|
||||
func (tc *testClient) ReadConfig(file string) ([]byte, error) {
|
||||
tc.mu.Lock()
|
||||
defer tc.mu.Unlock()
|
||||
|
||||
data, ok := tc.config[file]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no config %s", file)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// WriteConfig is for tc's implementation of Client.
|
||||
func (tc *testClient) WriteConfig(file string, old, new []byte) error {
|
||||
tc.mu.Lock()
|
||||
defer tc.mu.Unlock()
|
||||
|
||||
data := tc.config[file]
|
||||
if !bytes.Equal(old, data) {
|
||||
return ErrWriteConflict
|
||||
}
|
||||
tc.config[file] = new
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadCache is for tc's implementation of Client.
|
||||
func (tc *testClient) ReadCache(file string) ([]byte, error) {
|
||||
tc.mu.Lock()
|
||||
defer tc.mu.Unlock()
|
||||
|
||||
data, ok := tc.cache[file]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no cache %s", file)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// WriteCache is for tc's implementation of Client.
|
||||
func (tc *testClient) WriteCache(file string, data []byte) {
|
||||
tc.mu.Lock()
|
||||
defer tc.mu.Unlock()
|
||||
|
||||
tc.cache[file] = data
|
||||
}
|
||||
|
||||
// Log is for tc's implementation of Client.
|
||||
func (tc *testClient) Log(msg string) {
|
||||
tc.t.Log(msg)
|
||||
}
|
||||
|
||||
// SecurityError is for tc's implementation of Client.
|
||||
func (tc *testClient) SecurityError(msg string) {
|
||||
tc.mu.Lock()
|
||||
defer tc.mu.Unlock()
|
||||
|
||||
fmt.Fprintf(&tc.security, "%s\n", strings.TrimRight(msg, "\n"))
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
// Copyright 2019 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 tlog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCertificateTransparency(t *testing.T) {
|
||||
// Test that we can verify actual Certificate Transparency proofs.
|
||||
// (The other tests check that we can verify our own proofs;
|
||||
// this is a test that the two are compatible.)
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("skipping in -short mode")
|
||||
}
|
||||
|
||||
var root ctTree
|
||||
httpGET(t, "http://ct.googleapis.com/logs/argon2020/ct/v1/get-sth", &root)
|
||||
|
||||
var leaf ctEntries
|
||||
httpGET(t, "http://ct.googleapis.com/logs/argon2020/ct/v1/get-entries?start=10000&end=10000", &leaf)
|
||||
hash := RecordHash(leaf.Entries[0].Data)
|
||||
|
||||
var rp ctRecordProof
|
||||
httpGET(t, "http://ct.googleapis.com/logs/argon2020/ct/v1/get-proof-by-hash?tree_size="+fmt.Sprint(root.Size)+"&hash="+url.QueryEscape(hash.String()), &rp)
|
||||
|
||||
err := CheckRecord(rp.Proof, root.Size, root.Hash, 10000, hash)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var tp ctTreeProof
|
||||
httpGET(t, "http://ct.googleapis.com/logs/argon2020/ct/v1/get-sth-consistency?first=3654490&second="+fmt.Sprint(root.Size), &tp)
|
||||
|
||||
oh, _ := ParseHash("AuIZ5V6sDUj1vn3Y1K85oOaQ7y+FJJKtyRTl1edIKBQ=")
|
||||
err = CheckTree(tp.Proof, root.Size, root.Hash, 3654490, oh)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type ctTree struct {
|
||||
Size int64 `json:"tree_size"`
|
||||
Hash Hash `json:"sha256_root_hash"`
|
||||
}
|
||||
|
||||
type ctEntries struct {
|
||||
Entries []*ctEntry
|
||||
}
|
||||
|
||||
type ctEntry struct {
|
||||
Data []byte `json:"leaf_input"`
|
||||
}
|
||||
|
||||
type ctRecordProof struct {
|
||||
Index int64 `json:"leaf_index"`
|
||||
Proof RecordProof `json:"audit_path"`
|
||||
}
|
||||
|
||||
type ctTreeProof struct {
|
||||
Proof TreeProof `json:"consistency"`
|
||||
}
|
||||
|
||||
func httpGET(t *testing.T, url string, targ interface{}) {
|
||||
if testing.Verbose() {
|
||||
println()
|
||||
println(url)
|
||||
}
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if testing.Verbose() {
|
||||
os.Stdout.Write(data)
|
||||
}
|
||||
err = json.Unmarshal(data, targ)
|
||||
if err != nil {
|
||||
println(url)
|
||||
os.Stdout.Write(data)
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
// Copyright 2019 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 tlog
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatTree(t *testing.T) {
|
||||
n := int64(123456789012)
|
||||
h := RecordHash([]byte("hello world"))
|
||||
golden := "go.sum database tree\n123456789012\nTszzRgjTG6xce+z2AG31kAXYKBgQVtCSCE40HmuwBb0=\n"
|
||||
b := FormatTree(Tree{n, h})
|
||||
if string(b) != golden {
|
||||
t.Errorf("FormatTree(...) = %q, want %q", b, golden)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTree(t *testing.T) {
|
||||
in := "go.sum database tree\n123456789012\nTszzRgjTG6xce+z2AG31kAXYKBgQVtCSCE40HmuwBb0=\n"
|
||||
goldH := RecordHash([]byte("hello world"))
|
||||
goldN := int64(123456789012)
|
||||
tree, err := ParseTree([]byte(in))
|
||||
if tree.N != goldN || tree.Hash != goldH || err != nil {
|
||||
t.Fatalf("ParseTree(...) = Tree{%d, %v}, %v, want Tree{%d, %v}, nil", tree.N, tree.Hash, err, goldN, goldH)
|
||||
}
|
||||
|
||||
// Check invalid trees.
|
||||
var badTrees = []string{
|
||||
"not-" + in,
|
||||
"go.sum database tree\n0xabcdef\nTszzRgjTG6xce+z2AG31kAXYKBgQVtCSCE40HmuwBb0=\n",
|
||||
"go.sum database tree\n123456789012\nTszzRgjTG6xce+z2AG31kAXYKBgQVtCSCE40HmuwBTOOBIG=\n",
|
||||
}
|
||||
for _, bad := range badTrees {
|
||||
_, err := ParseTree([]byte(bad))
|
||||
if err == nil {
|
||||
t.Fatalf("ParseTree(%q) succeeded, want failure", in)
|
||||
}
|
||||
}
|
||||
|
||||
// Check junk on end is ignored.
|
||||
var goodTrees = []string{
|
||||
in + "JOE",
|
||||
in + "JOE\n",
|
||||
in + strings.Repeat("JOE\n", 1000),
|
||||
}
|
||||
for _, good := range goodTrees {
|
||||
_, err := ParseTree([]byte(good))
|
||||
if tree.N != goldN || tree.Hash != goldH || err != nil {
|
||||
t.Fatalf("ParseTree(...+%q) = Tree{%d, %v}, %v, want Tree{%d, %v}, nil", good[len(in):], tree.N, tree.Hash, err, goldN, goldH)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatRecord(t *testing.T) {
|
||||
id := int64(123456789012)
|
||||
text := "hello, world\n"
|
||||
golden := "123456789012\nhello, world\n\n"
|
||||
msg, err := FormatRecord(id, []byte(text))
|
||||
if err != nil {
|
||||
t.Fatalf("FormatRecord: %v", err)
|
||||
}
|
||||
if string(msg) != golden {
|
||||
t.Fatalf("FormatRecord(...) = %q, want %q", msg, golden)
|
||||
}
|
||||
|
||||
var badTexts = []string{
|
||||
"",
|
||||
"hello\nworld",
|
||||
"hello\n\nworld\n",
|
||||
"hello\x01world\n",
|
||||
}
|
||||
for _, bad := range badTexts {
|
||||
msg, err := FormatRecord(id, []byte(bad))
|
||||
if err == nil {
|
||||
t.Errorf("FormatRecord(id, %q) = %q, want error", bad, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecord(t *testing.T) {
|
||||
in := "123456789012\nhello, world\n\njunk on end\x01\xff"
|
||||
goldID := int64(123456789012)
|
||||
goldText := "hello, world\n"
|
||||
goldRest := "junk on end\x01\xff"
|
||||
id, text, rest, err := ParseRecord([]byte(in))
|
||||
if id != goldID || string(text) != goldText || string(rest) != goldRest || err != nil {
|
||||
t.Fatalf("ParseRecord(%q) = %d, %q, %q, %v, want %d, %q, %q, nil", in, id, text, rest, err, goldID, goldText, goldRest)
|
||||
}
|
||||
|
||||
in = "123456789012\nhello, world\n\n"
|
||||
id, text, rest, err = ParseRecord([]byte(in))
|
||||
if id != goldID || string(text) != goldText || len(rest) != 0 || err != nil {
|
||||
t.Fatalf("ParseRecord(%q) = %d, %q, %q, %v, want %d, %q, %q, nil", in, id, text, rest, err, goldID, goldText, "")
|
||||
}
|
||||
if rest == nil {
|
||||
t.Fatalf("ParseRecord(%q): rest = []byte(nil), want []byte{}", in)
|
||||
}
|
||||
|
||||
// Check invalid records.
|
||||
var badRecords = []string{
|
||||
"not-" + in,
|
||||
"123\nhello\x01world\n\n",
|
||||
"123\nhello\xffworld\n\n",
|
||||
"123\nhello world\n",
|
||||
"0x123\nhello world\n\n",
|
||||
}
|
||||
for _, bad := range badRecords {
|
||||
id, text, rest, err := ParseRecord([]byte(bad))
|
||||
if err == nil {
|
||||
t.Fatalf("ParseRecord(%q) = %d, %q, %q, nil, want error", in, id, text, rest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,269 +0,0 @@
|
|||
// Copyright 2019 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 tlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testHashStorage []Hash
|
||||
|
||||
func (t testHashStorage) ReadHash(level int, n int64) (Hash, error) {
|
||||
return t[StoredHashIndex(level, n)], nil
|
||||
}
|
||||
|
||||
func (t testHashStorage) ReadHashes(index []int64) ([]Hash, error) {
|
||||
// It's not required by HashReader that indexes be in increasing order,
|
||||
// but check that the functions we are testing only ever ask for
|
||||
// indexes in increasing order.
|
||||
for i := 1; i < len(index); i++ {
|
||||
if index[i-1] >= index[i] {
|
||||
panic("indexes out of order")
|
||||
}
|
||||
}
|
||||
|
||||
out := make([]Hash, len(index))
|
||||
for i, x := range index {
|
||||
out[i] = t[x]
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type testTilesStorage struct {
|
||||
unsaved int
|
||||
m map[Tile][]byte
|
||||
}
|
||||
|
||||
func (t testTilesStorage) Height() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (t *testTilesStorage) SaveTiles(tiles []Tile, data [][]byte) {
|
||||
t.unsaved -= len(tiles)
|
||||
}
|
||||
|
||||
func (t *testTilesStorage) ReadTiles(tiles []Tile) ([][]byte, error) {
|
||||
out := make([][]byte, len(tiles))
|
||||
for i, tile := range tiles {
|
||||
out[i] = t.m[tile]
|
||||
}
|
||||
t.unsaved += len(tiles)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func TestTree(t *testing.T) {
|
||||
var trees []Hash
|
||||
var leafhashes []Hash
|
||||
var storage testHashStorage
|
||||
tiles := make(map[Tile][]byte)
|
||||
const testH = 2
|
||||
for i := int64(0); i < 100; i++ {
|
||||
data := []byte(fmt.Sprintf("leaf %d", i))
|
||||
hashes, err := StoredHashes(i, data, storage)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
leafhashes = append(leafhashes, RecordHash(data))
|
||||
oldStorage := len(storage)
|
||||
storage = append(storage, hashes...)
|
||||
if count := StoredHashCount(i + 1); count != int64(len(storage)) {
|
||||
t.Errorf("StoredHashCount(%d) = %d, have %d StoredHashes", i+1, count, len(storage))
|
||||
}
|
||||
th, err := TreeHash(i+1, storage)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tile := range NewTiles(testH, i, i+1) {
|
||||
data, err := ReadTileData(tile, storage)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
old := Tile{H: tile.H, L: tile.L, N: tile.N, W: tile.W - 1}
|
||||
oldData := tiles[old]
|
||||
if len(oldData) != len(data)-HashSize || !bytes.Equal(oldData, data[:len(oldData)]) {
|
||||
t.Fatalf("tile %v not extending earlier tile %v", tile.Path(), old.Path())
|
||||
}
|
||||
tiles[tile] = data
|
||||
}
|
||||
for _, tile := range NewTiles(testH, 0, i+1) {
|
||||
data, err := ReadTileData(tile, storage)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(tiles[tile], data) {
|
||||
t.Fatalf("mismatch at %+v", tile)
|
||||
}
|
||||
}
|
||||
for _, tile := range NewTiles(testH, i/2, i+1) {
|
||||
data, err := ReadTileData(tile, storage)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(tiles[tile], data) {
|
||||
t.Fatalf("mismatch at %+v", tile)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all the new hashes are readable from their tiles.
|
||||
for j := oldStorage; j < len(storage); j++ {
|
||||
tile := TileForIndex(testH, int64(j))
|
||||
data, ok := tiles[tile]
|
||||
if !ok {
|
||||
t.Log(NewTiles(testH, 0, i+1))
|
||||
t.Fatalf("TileForIndex(%d, %d) = %v, not yet stored (i=%d, stored %d)", testH, j, tile.Path(), i, len(storage))
|
||||
continue
|
||||
}
|
||||
h, err := HashFromTile(tile, data, int64(j))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if h != storage[j] {
|
||||
t.Errorf("HashFromTile(%v, %d) = %v, want %v", tile.Path(), int64(j), h, storage[j])
|
||||
}
|
||||
}
|
||||
|
||||
trees = append(trees, th)
|
||||
|
||||
// Check that leaf proofs work, for all trees and leaves so far.
|
||||
for j := int64(0); j <= i; j++ {
|
||||
p, err := ProveRecord(i+1, j, storage)
|
||||
if err != nil {
|
||||
t.Fatalf("ProveRecord(%d, %d): %v", i+1, j, err)
|
||||
}
|
||||
if err := CheckRecord(p, i+1, th, j, leafhashes[j]); err != nil {
|
||||
t.Fatalf("CheckRecord(%d, %d): %v", i+1, j, err)
|
||||
}
|
||||
for k := range p {
|
||||
p[k][0] ^= 1
|
||||
if err := CheckRecord(p, i+1, th, j, leafhashes[j]); err == nil {
|
||||
t.Fatalf("CheckRecord(%d, %d) succeeded with corrupt proof hash #%d!", i+1, j, k)
|
||||
}
|
||||
p[k][0] ^= 1
|
||||
}
|
||||
}
|
||||
|
||||
// Check that leaf proofs work using TileReader.
|
||||
// To prove a leaf that way, all you have to do is read and verify its hash.
|
||||
storage := &testTilesStorage{m: tiles}
|
||||
thr := TileHashReader(Tree{i + 1, th}, storage)
|
||||
for j := int64(0); j <= i; j++ {
|
||||
h, err := thr.ReadHashes([]int64{StoredHashIndex(0, j)})
|
||||
if err != nil {
|
||||
t.Fatalf("TileHashReader(%d).ReadHashes(%d): %v", i+1, j, err)
|
||||
}
|
||||
if h[0] != leafhashes[j] {
|
||||
t.Fatalf("TileHashReader(%d).ReadHashes(%d) returned wrong hash", i+1, j)
|
||||
}
|
||||
|
||||
// Even though reading the hash suffices,
|
||||
// check we can generate the proof too.
|
||||
p, err := ProveRecord(i+1, j, thr)
|
||||
if err != nil {
|
||||
t.Fatalf("ProveRecord(%d, %d, TileHashReader(%d)): %v", i+1, j, i+1, err)
|
||||
}
|
||||
if err := CheckRecord(p, i+1, th, j, leafhashes[j]); err != nil {
|
||||
t.Fatalf("CheckRecord(%d, %d, TileHashReader(%d)): %v", i+1, j, i+1, err)
|
||||
}
|
||||
}
|
||||
if storage.unsaved != 0 {
|
||||
t.Fatalf("TileHashReader(%d) did not save %d tiles", i+1, storage.unsaved)
|
||||
}
|
||||
|
||||
// Check that ReadHashes will give an error if the index is not in the tree.
|
||||
if _, err := thr.ReadHashes([]int64{(i + 1) * 2}); err == nil {
|
||||
t.Fatalf("TileHashReader(%d).ReadHashes(%d) for index not in tree <nil>, want err", i, i+1)
|
||||
}
|
||||
if storage.unsaved != 0 {
|
||||
t.Fatalf("TileHashReader(%d) did not save %d tiles", i+1, storage.unsaved)
|
||||
}
|
||||
|
||||
// Check that tree proofs work, for all trees so far, using TileReader.
|
||||
// To prove a tree that way, all you have to do is compute and verify its hash.
|
||||
for j := int64(0); j <= i; j++ {
|
||||
h, err := TreeHash(j+1, thr)
|
||||
if err != nil {
|
||||
t.Fatalf("TreeHash(%d, TileHashReader(%d)): %v", j, i+1, err)
|
||||
}
|
||||
if h != trees[j] {
|
||||
t.Fatalf("TreeHash(%d, TileHashReader(%d)) = %x, want %x (%v)", j, i+1, h[:], trees[j][:], trees[j])
|
||||
}
|
||||
|
||||
// Even though computing the subtree hash suffices,
|
||||
// check that we can generate the proof too.
|
||||
p, err := ProveTree(i+1, j+1, thr)
|
||||
if err != nil {
|
||||
t.Fatalf("ProveTree(%d, %d): %v", i+1, j+1, err)
|
||||
}
|
||||
if err := CheckTree(p, i+1, th, j+1, trees[j]); err != nil {
|
||||
t.Fatalf("CheckTree(%d, %d): %v [%v]", i+1, j+1, err, p)
|
||||
}
|
||||
for k := range p {
|
||||
p[k][0] ^= 1
|
||||
if err := CheckTree(p, i+1, th, j+1, trees[j]); err == nil {
|
||||
t.Fatalf("CheckTree(%d, %d) succeeded with corrupt proof hash #%d!", i+1, j+1, k)
|
||||
}
|
||||
p[k][0] ^= 1
|
||||
}
|
||||
}
|
||||
if storage.unsaved != 0 {
|
||||
t.Fatalf("TileHashReader(%d) did not save %d tiles", i+1, storage.unsaved)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitStoredHashIndex(t *testing.T) {
|
||||
for l := 0; l < 10; l++ {
|
||||
for n := int64(0); n < 100; n++ {
|
||||
x := StoredHashIndex(l, n)
|
||||
l1, n1 := SplitStoredHashIndex(x)
|
||||
if l1 != l || n1 != n {
|
||||
t.Fatalf("StoredHashIndex(%d, %d) = %d, but SplitStoredHashIndex(%d) = %d, %d", l, n, x, x, l1, n1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rsc): Test invalid paths too, like "tile/3/5/123/456/078".
|
||||
var tilePaths = []struct {
|
||||
path string
|
||||
tile Tile
|
||||
}{
|
||||
{"tile/4/0/001", Tile{4, 0, 1, 16}},
|
||||
{"tile/4/0/001.p/5", Tile{4, 0, 1, 5}},
|
||||
{"tile/3/5/x123/x456/078", Tile{3, 5, 123456078, 8}},
|
||||
{"tile/3/5/x123/x456/078.p/2", Tile{3, 5, 123456078, 2}},
|
||||
{"tile/1/0/x003/x057/500", Tile{1, 0, 3057500, 2}},
|
||||
{"tile/3/5/123/456/078", Tile{}},
|
||||
{"tile/3/-1/123/456/078", Tile{}},
|
||||
{"tile/1/data/x003/x057/500", Tile{1, -1, 3057500, 2}},
|
||||
}
|
||||
|
||||
func TestTilePath(t *testing.T) {
|
||||
for _, tt := range tilePaths {
|
||||
if tt.tile.H > 0 {
|
||||
p := tt.tile.Path()
|
||||
if p != tt.path {
|
||||
t.Errorf("%+v.Path() = %q, want %q", tt.tile, p, tt.path)
|
||||
}
|
||||
}
|
||||
tile, err := ParseTilePath(tt.path)
|
||||
if err != nil {
|
||||
if tt.tile.H == 0 {
|
||||
// Expected error.
|
||||
continue
|
||||
}
|
||||
t.Errorf("ParseTilePath(%q): %v", tt.path, err)
|
||||
} else if tile != tt.tile {
|
||||
if tt.tile.H == 0 {
|
||||
t.Errorf("ParseTilePath(%q): expected error, got %+v", tt.path, tt.tile)
|
||||
continue
|
||||
}
|
||||
t.Errorf("ParseTilePath(%q) = %+v, want %+v", tt.path, tile, tt.tile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,14 +23,15 @@ import (
|
|||
"sync"
|
||||
"testing"
|
||||
|
||||
"cmd/go/internal/dirhash"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/sumdb"
|
||||
"cmd/go/internal/txtar"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
"golang.org/x/mod/sumdb"
|
||||
"golang.org/x/mod/sumdb/dirhash"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,222 @@
|
|||
// Copyright 2016 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.
|
||||
|
||||
// In Go 1.13, the ed25519 package was promoted to the standard library as
|
||||
// crypto/ed25519, and this package became a wrapper for the standard library one.
|
||||
//
|
||||
// +build !go1.13
|
||||
|
||||
// Package ed25519 implements the Ed25519 signature algorithm. See
|
||||
// https://ed25519.cr.yp.to/.
|
||||
//
|
||||
// These functions are also compatible with the “Ed25519” function defined in
|
||||
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
|
||||
// representation includes a public key suffix to make multiple signing
|
||||
// operations with the same key more efficient. This package refers to the RFC
|
||||
// 8032 private key as the “seed”.
|
||||
package ed25519
|
||||
|
||||
// This code is a port of the public domain, “ref10” implementation of ed25519
|
||||
// from SUPERCOP.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/ed25519/internal/edwards25519"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeySize is the size, in bytes, of public keys as used in this package.
|
||||
PublicKeySize = 32
|
||||
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
|
||||
PrivateKeySize = 64
|
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = 64
|
||||
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
|
||||
SeedSize = 32
|
||||
)
|
||||
|
||||
// PublicKey is the type of Ed25519 public keys.
|
||||
type PublicKey []byte
|
||||
|
||||
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
|
||||
type PrivateKey []byte
|
||||
|
||||
// Public returns the PublicKey corresponding to priv.
|
||||
func (priv PrivateKey) Public() crypto.PublicKey {
|
||||
publicKey := make([]byte, PublicKeySize)
|
||||
copy(publicKey, priv[32:])
|
||||
return PublicKey(publicKey)
|
||||
}
|
||||
|
||||
// Seed returns the private key seed corresponding to priv. It is provided for
|
||||
// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
|
||||
// in this package.
|
||||
func (priv PrivateKey) Seed() []byte {
|
||||
seed := make([]byte, SeedSize)
|
||||
copy(seed, priv[:32])
|
||||
return seed
|
||||
}
|
||||
|
||||
// Sign signs the given message with priv.
|
||||
// Ed25519 performs two passes over messages to be signed and therefore cannot
|
||||
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
|
||||
// indicate the message hasn't been hashed. This can be achieved by passing
|
||||
// crypto.Hash(0) as the value for opts.
|
||||
func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
if opts.HashFunc() != crypto.Hash(0) {
|
||||
return nil, errors.New("ed25519: cannot sign hashed message")
|
||||
}
|
||||
|
||||
return Sign(priv, message), nil
|
||||
}
|
||||
|
||||
// GenerateKey generates a public/private key pair using entropy from rand.
|
||||
// If rand is nil, crypto/rand.Reader will be used.
|
||||
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
|
||||
if rand == nil {
|
||||
rand = cryptorand.Reader
|
||||
}
|
||||
|
||||
seed := make([]byte, SeedSize)
|
||||
if _, err := io.ReadFull(rand, seed); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
privateKey := NewKeyFromSeed(seed)
|
||||
publicKey := make([]byte, PublicKeySize)
|
||||
copy(publicKey, privateKey[32:])
|
||||
|
||||
return publicKey, privateKey, nil
|
||||
}
|
||||
|
||||
// NewKeyFromSeed calculates a private key from a seed. It will panic if
|
||||
// len(seed) is not SeedSize. This function is provided for interoperability
|
||||
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
|
||||
// package.
|
||||
func NewKeyFromSeed(seed []byte) PrivateKey {
|
||||
if l := len(seed); l != SeedSize {
|
||||
panic("ed25519: bad seed length: " + strconv.Itoa(l))
|
||||
}
|
||||
|
||||
digest := sha512.Sum512(seed)
|
||||
digest[0] &= 248
|
||||
digest[31] &= 127
|
||||
digest[31] |= 64
|
||||
|
||||
var A edwards25519.ExtendedGroupElement
|
||||
var hBytes [32]byte
|
||||
copy(hBytes[:], digest[:])
|
||||
edwards25519.GeScalarMultBase(&A, &hBytes)
|
||||
var publicKeyBytes [32]byte
|
||||
A.ToBytes(&publicKeyBytes)
|
||||
|
||||
privateKey := make([]byte, PrivateKeySize)
|
||||
copy(privateKey, seed)
|
||||
copy(privateKey[32:], publicKeyBytes[:])
|
||||
|
||||
return privateKey
|
||||
}
|
||||
|
||||
// Sign signs the message with privateKey and returns a signature. It will
|
||||
// panic if len(privateKey) is not PrivateKeySize.
|
||||
func Sign(privateKey PrivateKey, message []byte) []byte {
|
||||
if l := len(privateKey); l != PrivateKeySize {
|
||||
panic("ed25519: bad private key length: " + strconv.Itoa(l))
|
||||
}
|
||||
|
||||
h := sha512.New()
|
||||
h.Write(privateKey[:32])
|
||||
|
||||
var digest1, messageDigest, hramDigest [64]byte
|
||||
var expandedSecretKey [32]byte
|
||||
h.Sum(digest1[:0])
|
||||
copy(expandedSecretKey[:], digest1[:])
|
||||
expandedSecretKey[0] &= 248
|
||||
expandedSecretKey[31] &= 63
|
||||
expandedSecretKey[31] |= 64
|
||||
|
||||
h.Reset()
|
||||
h.Write(digest1[32:])
|
||||
h.Write(message)
|
||||
h.Sum(messageDigest[:0])
|
||||
|
||||
var messageDigestReduced [32]byte
|
||||
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
|
||||
var R edwards25519.ExtendedGroupElement
|
||||
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
|
||||
|
||||
var encodedR [32]byte
|
||||
R.ToBytes(&encodedR)
|
||||
|
||||
h.Reset()
|
||||
h.Write(encodedR[:])
|
||||
h.Write(privateKey[32:])
|
||||
h.Write(message)
|
||||
h.Sum(hramDigest[:0])
|
||||
var hramDigestReduced [32]byte
|
||||
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
|
||||
|
||||
var s [32]byte
|
||||
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
|
||||
|
||||
signature := make([]byte, SignatureSize)
|
||||
copy(signature[:], encodedR[:])
|
||||
copy(signature[32:], s[:])
|
||||
|
||||
return signature
|
||||
}
|
||||
|
||||
// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||
// will panic if len(publicKey) is not PublicKeySize.
|
||||
func Verify(publicKey PublicKey, message, sig []byte) bool {
|
||||
if l := len(publicKey); l != PublicKeySize {
|
||||
panic("ed25519: bad public key length: " + strconv.Itoa(l))
|
||||
}
|
||||
|
||||
if len(sig) != SignatureSize || sig[63]&224 != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var A edwards25519.ExtendedGroupElement
|
||||
var publicKeyBytes [32]byte
|
||||
copy(publicKeyBytes[:], publicKey)
|
||||
if !A.FromBytes(&publicKeyBytes) {
|
||||
return false
|
||||
}
|
||||
edwards25519.FeNeg(&A.X, &A.X)
|
||||
edwards25519.FeNeg(&A.T, &A.T)
|
||||
|
||||
h := sha512.New()
|
||||
h.Write(sig[:32])
|
||||
h.Write(publicKey[:])
|
||||
h.Write(message)
|
||||
var digest [64]byte
|
||||
h.Sum(digest[:0])
|
||||
|
||||
var hReduced [32]byte
|
||||
edwards25519.ScReduce(&hReduced, &digest)
|
||||
|
||||
var R edwards25519.ProjectiveGroupElement
|
||||
var s [32]byte
|
||||
copy(s[:], sig[32:])
|
||||
|
||||
// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
|
||||
// the range [0, order) in order to prevent signature malleability.
|
||||
if !edwards25519.ScMinimal(&s) {
|
||||
return false
|
||||
}
|
||||
|
||||
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
|
||||
|
||||
var checkR [32]byte
|
||||
R.ToBytes(&checkR)
|
||||
return bytes.Equal(sig[:32], checkR[:])
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2019 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.
|
||||
|
||||
// +build go1.13
|
||||
|
||||
// Package ed25519 implements the Ed25519 signature algorithm. See
|
||||
// https://ed25519.cr.yp.to/.
|
||||
//
|
||||
// These functions are also compatible with the “Ed25519” function defined in
|
||||
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
|
||||
// representation includes a public key suffix to make multiple signing
|
||||
// operations with the same key more efficient. This package refers to the RFC
|
||||
// 8032 private key as the “seed”.
|
||||
//
|
||||
// Beginning with Go 1.13, the functionality of this package was moved to the
|
||||
// standard library as crypto/ed25519. This package only acts as a compatibility
|
||||
// wrapper.
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeySize is the size, in bytes, of public keys as used in this package.
|
||||
PublicKeySize = 32
|
||||
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
|
||||
PrivateKeySize = 64
|
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = 64
|
||||
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
|
||||
SeedSize = 32
|
||||
)
|
||||
|
||||
// PublicKey is the type of Ed25519 public keys.
|
||||
//
|
||||
// This type is an alias for crypto/ed25519's PublicKey type.
|
||||
// See the crypto/ed25519 package for the methods on this type.
|
||||
type PublicKey = ed25519.PublicKey
|
||||
|
||||
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
|
||||
//
|
||||
// This type is an alias for crypto/ed25519's PrivateKey type.
|
||||
// See the crypto/ed25519 package for the methods on this type.
|
||||
type PrivateKey = ed25519.PrivateKey
|
||||
|
||||
// GenerateKey generates a public/private key pair using entropy from rand.
|
||||
// If rand is nil, crypto/rand.Reader will be used.
|
||||
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
|
||||
return ed25519.GenerateKey(rand)
|
||||
}
|
||||
|
||||
// NewKeyFromSeed calculates a private key from a seed. It will panic if
|
||||
// len(seed) is not SeedSize. This function is provided for interoperability
|
||||
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
|
||||
// package.
|
||||
func NewKeyFromSeed(seed []byte) PrivateKey {
|
||||
return ed25519.NewKeyFromSeed(seed)
|
||||
}
|
||||
|
||||
// Sign signs the message with privateKey and returns a signature. It will
|
||||
// panic if len(privateKey) is not PrivateKeySize.
|
||||
func Sign(privateKey PrivateKey, message []byte) []byte {
|
||||
return ed25519.Sign(privateKey, message)
|
||||
}
|
||||
|
||||
// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||
// will panic if len(publicKey) is not PublicKeySize.
|
||||
func Verify(publicKey PublicKey, message, sig []byte) bool {
|
||||
return ed25519.Verify(publicKey, message, sig)
|
||||
}
|
||||
1422
src/cmd/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
generated
vendored
Normal file
1422
src/cmd/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1793
src/cmd/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
generated
vendored
Normal file
1793
src/cmd/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// 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 lazyregexp is a thin wrapper over regexp, allowing the use of global
|
||||
// regexp variables without forcing them to be compiled at init.
|
||||
package lazyregexp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Regexp is a wrapper around regexp.Regexp, where the underlying regexp will be
|
||||
// compiled the first time it is needed.
|
||||
type Regexp struct {
|
||||
str string
|
||||
once sync.Once
|
||||
rx *regexp.Regexp
|
||||
}
|
||||
|
||||
func (r *Regexp) re() *regexp.Regexp {
|
||||
r.once.Do(r.build)
|
||||
return r.rx
|
||||
}
|
||||
|
||||
func (r *Regexp) build() {
|
||||
r.rx = regexp.MustCompile(r.str)
|
||||
r.str = ""
|
||||
}
|
||||
|
||||
func (r *Regexp) FindSubmatch(s []byte) [][]byte {
|
||||
return r.re().FindSubmatch(s)
|
||||
}
|
||||
|
||||
func (r *Regexp) FindStringSubmatch(s string) []string {
|
||||
return r.re().FindStringSubmatch(s)
|
||||
}
|
||||
|
||||
func (r *Regexp) FindStringSubmatchIndex(s string) []int {
|
||||
return r.re().FindStringSubmatchIndex(s)
|
||||
}
|
||||
|
||||
func (r *Regexp) ReplaceAllString(src, repl string) string {
|
||||
return r.re().ReplaceAllString(src, repl)
|
||||
}
|
||||
|
||||
func (r *Regexp) FindString(s string) string {
|
||||
return r.re().FindString(s)
|
||||
}
|
||||
|
||||
func (r *Regexp) FindAllString(s string, n int) []string {
|
||||
return r.re().FindAllString(s, n)
|
||||
}
|
||||
|
||||
func (r *Regexp) MatchString(s string) bool {
|
||||
return r.re().MatchString(s)
|
||||
}
|
||||
|
||||
func (r *Regexp) SubexpNames() []string {
|
||||
return r.re().SubexpNames()
|
||||
}
|
||||
|
||||
var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test")
|
||||
|
||||
// New creates a new lazy regexp, delaying the compiling work until it is first
|
||||
// needed. If the code is being run as part of tests, the regexp compiling will
|
||||
// happen immediately.
|
||||
func New(str string) *Regexp {
|
||||
lr := &Regexp{str: str}
|
||||
if inTest {
|
||||
// In tests, always compile the regexps early.
|
||||
lr.re()
|
||||
}
|
||||
return lr
|
||||
}
|
||||
|
|
@ -8,14 +8,14 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/lazyregexp"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"cmd/go/internal/module"
|
||||
"golang.org/x/mod/internal/lazyregexp"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
// A File is the parsed, interpreted form of a go.mod file.
|
||||
|
|
@ -96,14 +96,14 @@ package module
|
|||
// Changes to the semantics in this file require approval from rsc.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"cmd/go/internal/semver"
|
||||
"golang.org/x/mod/semver"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// A Version (for clients, a module.Version) is defined by a module path and version pair.
|
||||
0
src/cmd/go/internal/sumdb/cache.go → src/cmd/vendor/golang.org/x/mod/sumdb/cache.go
generated
vendored
0
src/cmd/go/internal/sumdb/cache.go → src/cmd/vendor/golang.org/x/mod/sumdb/cache.go
generated
vendored
|
|
@ -13,9 +13,9 @@ import (
|
|||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/note"
|
||||
"cmd/go/internal/tlog"
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/sumdb/note"
|
||||
"golang.org/x/mod/sumdb/tlog"
|
||||
)
|
||||
|
||||
// A ClientOps provides the external operations
|
||||
|
|
@ -183,7 +183,6 @@ package note
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
|
|
@ -194,6 +193,8 @@ import (
|
|||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// A Verifier verifies messages signed with a specific key.
|
||||
|
|
@ -7,13 +7,13 @@ package sumdb
|
|||
|
||||
import (
|
||||
"context"
|
||||
"internal/lazyregexp"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/tlog"
|
||||
"golang.org/x/mod/internal/lazyregexp"
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/sumdb/tlog"
|
||||
)
|
||||
|
||||
// A ServerOps provides the external operations
|
||||
6
src/cmd/go/internal/sumdb/test.go → src/cmd/vendor/golang.org/x/mod/sumdb/test.go
generated
vendored
6
src/cmd/go/internal/sumdb/test.go → src/cmd/vendor/golang.org/x/mod/sumdb/test.go
generated
vendored
|
|
@ -9,9 +9,9 @@ import (
|
|||
"fmt"
|
||||
"sync"
|
||||
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/note"
|
||||
"cmd/go/internal/tlog"
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/sumdb/note"
|
||||
"golang.org/x/mod/sumdb/tlog"
|
||||
)
|
||||
|
||||
// NewTestServer constructs a new TestServer
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
This repository holds the transition packages for the new Go 1.13 error values.
|
||||
See golang.org/design/29934-error-values.
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
// 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 xerrors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// FormatError calls the FormatError method of f with an errors.Printer
|
||||
// configured according to s and verb, and writes the result to s.
|
||||
func FormatError(f Formatter, s fmt.State, verb rune) {
|
||||
// Assuming this function is only called from the Format method, and given
|
||||
// that FormatError takes precedence over Format, it cannot be called from
|
||||
// any package that supports errors.Formatter. It is therefore safe to
|
||||
// disregard that State may be a specific printer implementation and use one
|
||||
// of our choice instead.
|
||||
|
||||
// limitations: does not support printing error as Go struct.
|
||||
|
||||
var (
|
||||
sep = " " // separator before next error
|
||||
p = &state{State: s}
|
||||
direct = true
|
||||
)
|
||||
|
||||
var err error = f
|
||||
|
||||
switch verb {
|
||||
// Note that this switch must match the preference order
|
||||
// for ordinary string printing (%#v before %+v, and so on).
|
||||
|
||||
case 'v':
|
||||
if s.Flag('#') {
|
||||
if stringer, ok := err.(fmt.GoStringer); ok {
|
||||
io.WriteString(&p.buf, stringer.GoString())
|
||||
goto exit
|
||||
}
|
||||
// proceed as if it were %v
|
||||
} else if s.Flag('+') {
|
||||
p.printDetail = true
|
||||
sep = "\n - "
|
||||
}
|
||||
case 's':
|
||||
case 'q', 'x', 'X':
|
||||
// Use an intermediate buffer in the rare cases that precision,
|
||||
// truncation, or one of the alternative verbs (q, x, and X) are
|
||||
// specified.
|
||||
direct = false
|
||||
|
||||
default:
|
||||
p.buf.WriteString("%!")
|
||||
p.buf.WriteRune(verb)
|
||||
p.buf.WriteByte('(')
|
||||
switch {
|
||||
case err != nil:
|
||||
p.buf.WriteString(reflect.TypeOf(f).String())
|
||||
default:
|
||||
p.buf.WriteString("<nil>")
|
||||
}
|
||||
p.buf.WriteByte(')')
|
||||
io.Copy(s, &p.buf)
|
||||
return
|
||||
}
|
||||
|
||||
loop:
|
||||
for {
|
||||
switch v := err.(type) {
|
||||
case Formatter:
|
||||
err = v.FormatError((*printer)(p))
|
||||
case fmt.Formatter:
|
||||
v.Format(p, 'v')
|
||||
break loop
|
||||
default:
|
||||
io.WriteString(&p.buf, v.Error())
|
||||
break loop
|
||||
}
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if p.needColon || !p.printDetail {
|
||||
p.buf.WriteByte(':')
|
||||
p.needColon = false
|
||||
}
|
||||
p.buf.WriteString(sep)
|
||||
p.inDetail = false
|
||||
p.needNewline = false
|
||||
}
|
||||
|
||||
exit:
|
||||
width, okW := s.Width()
|
||||
prec, okP := s.Precision()
|
||||
|
||||
if !direct || (okW && width > 0) || okP {
|
||||
// Construct format string from State s.
|
||||
format := []byte{'%'}
|
||||
if s.Flag('-') {
|
||||
format = append(format, '-')
|
||||
}
|
||||
if s.Flag('+') {
|
||||
format = append(format, '+')
|
||||
}
|
||||
if s.Flag(' ') {
|
||||
format = append(format, ' ')
|
||||
}
|
||||
if okW {
|
||||
format = strconv.AppendInt(format, int64(width), 10)
|
||||
}
|
||||
if okP {
|
||||
format = append(format, '.')
|
||||
format = strconv.AppendInt(format, int64(prec), 10)
|
||||
}
|
||||
format = append(format, string(verb)...)
|
||||
fmt.Fprintf(s, string(format), p.buf.String())
|
||||
} else {
|
||||
io.Copy(s, &p.buf)
|
||||
}
|
||||
}
|
||||
|
||||
var detailSep = []byte("\n ")
|
||||
|
||||
// state tracks error printing state. It implements fmt.State.
|
||||
type state struct {
|
||||
fmt.State
|
||||
buf bytes.Buffer
|
||||
|
||||
printDetail bool
|
||||
inDetail bool
|
||||
needColon bool
|
||||
needNewline bool
|
||||
}
|
||||
|
||||
func (s *state) Write(b []byte) (n int, err error) {
|
||||
if s.printDetail {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if s.inDetail && s.needColon {
|
||||
s.needNewline = true
|
||||
if b[0] == '\n' {
|
||||
b = b[1:]
|
||||
}
|
||||
}
|
||||
k := 0
|
||||
for i, c := range b {
|
||||
if s.needNewline {
|
||||
if s.inDetail && s.needColon {
|
||||
s.buf.WriteByte(':')
|
||||
s.needColon = false
|
||||
}
|
||||
s.buf.Write(detailSep)
|
||||
s.needNewline = false
|
||||
}
|
||||
if c == '\n' {
|
||||
s.buf.Write(b[k:i])
|
||||
k = i + 1
|
||||
s.needNewline = true
|
||||
}
|
||||
}
|
||||
s.buf.Write(b[k:])
|
||||
if !s.inDetail {
|
||||
s.needColon = true
|
||||
}
|
||||
} else if !s.inDetail {
|
||||
s.buf.Write(b)
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// printer wraps a state to implement an xerrors.Printer.
|
||||
type printer state
|
||||
|
||||
func (s *printer) Print(args ...interface{}) {
|
||||
if !s.inDetail || s.printDetail {
|
||||
fmt.Fprint((*state)(s), args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *printer) Printf(format string, args ...interface{}) {
|
||||
if !s.inDetail || s.printDetail {
|
||||
fmt.Fprintf((*state)(s), format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *printer) Detail() bool {
|
||||
s.inDetail = true
|
||||
return s.printDetail
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
issuerepo: golang/go
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2019 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 xerrors implements functions to manipulate errors.
|
||||
//
|
||||
// This package is based on the Go 2 proposal for error values:
|
||||
// https://golang.org/design/29934-error-values
|
||||
//
|
||||
// These functions were incorporated into the standard library's errors package
|
||||
// in Go 1.13:
|
||||
// - Is
|
||||
// - As
|
||||
// - Unwrap
|
||||
//
|
||||
// Also, Errorf's %w verb was incorporated into fmt.Errorf.
|
||||
//
|
||||
// Use this package to get equivalent behavior in all supported Go versions.
|
||||
//
|
||||
// No other features of this package were included in Go 1.13, and at present
|
||||
// there are no plans to include any of them.
|
||||
package xerrors // import "golang.org/x/xerrors"
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2011 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 xerrors
|
||||
|
||||
import "fmt"
|
||||
|
||||
// errorString is a trivial implementation of error.
|
||||
type errorString struct {
|
||||
s string
|
||||
frame Frame
|
||||
}
|
||||
|
||||
// New returns an error that formats as the given text.
|
||||
//
|
||||
// The returned error contains a Frame set to the caller's location and
|
||||
// implements Formatter to show this information when printed with details.
|
||||
func New(text string) error {
|
||||
return &errorString{text, Caller(1)}
|
||||
}
|
||||
|
||||
func (e *errorString) Error() string {
|
||||
return e.s
|
||||
}
|
||||
|
||||
func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) }
|
||||
|
||||
func (e *errorString) FormatError(p Printer) (next error) {
|
||||
p.Print(e.s)
|
||||
e.frame.Format(p)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
// 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 xerrors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/xerrors/internal"
|
||||
)
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string as a
|
||||
// value that satisfies error.
|
||||
//
|
||||
// The returned error includes the file and line number of the caller when
|
||||
// formatted with additional detail enabled. If the last argument is an error
|
||||
// the returned error's Format method will return it if the format string ends
|
||||
// with ": %s", ": %v", or ": %w". If the last argument is an error and the
|
||||
// format string ends with ": %w", the returned error implements Wrapper
|
||||
// with an Unwrap method returning it.
|
||||
func Errorf(format string, a ...interface{}) error {
|
||||
err, wrap := lastError(format, a)
|
||||
format = formatPlusW(format)
|
||||
if err == nil {
|
||||
return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)}
|
||||
}
|
||||
|
||||
// TODO: this is not entirely correct. The error value could be
|
||||
// printed elsewhere in format if it mixes numbered with unnumbered
|
||||
// substitutions. With relatively small changes to doPrintf we can
|
||||
// have it optionally ignore extra arguments and pass the argument
|
||||
// list in its entirety.
|
||||
msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...)
|
||||
frame := Frame{}
|
||||
if internal.EnableTrace {
|
||||
frame = Caller(1)
|
||||
}
|
||||
if wrap {
|
||||
return &wrapError{msg, err, frame}
|
||||
}
|
||||
return &noWrapError{msg, err, frame}
|
||||
}
|
||||
|
||||
// formatPlusW is used to avoid the vet check that will barf at %w.
|
||||
func formatPlusW(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
func lastError(format string, a []interface{}) (err error, wrap bool) {
|
||||
wrap = strings.HasSuffix(format, ": %w")
|
||||
if !wrap &&
|
||||
!strings.HasSuffix(format, ": %s") &&
|
||||
!strings.HasSuffix(format, ": %v") {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if len(a) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
err, ok := a[len(a)-1].(error)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return err, wrap
|
||||
}
|
||||
|
||||
type noWrapError struct {
|
||||
msg string
|
||||
err error
|
||||
frame Frame
|
||||
}
|
||||
|
||||
func (e *noWrapError) Error() string {
|
||||
return fmt.Sprint(e)
|
||||
}
|
||||
|
||||
func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
|
||||
|
||||
func (e *noWrapError) FormatError(p Printer) (next error) {
|
||||
p.Print(e.msg)
|
||||
e.frame.Format(p)
|
||||
return e.err
|
||||
}
|
||||
|
||||
type wrapError struct {
|
||||
msg string
|
||||
err error
|
||||
frame Frame
|
||||
}
|
||||
|
||||
func (e *wrapError) Error() string {
|
||||
return fmt.Sprint(e)
|
||||
}
|
||||
|
||||
func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
|
||||
|
||||
func (e *wrapError) FormatError(p Printer) (next error) {
|
||||
p.Print(e.msg)
|
||||
e.frame.Format(p)
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e *wrapError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// 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 xerrors
|
||||
|
||||
// A Formatter formats error messages.
|
||||
type Formatter interface {
|
||||
error
|
||||
|
||||
// FormatError prints the receiver's first error and returns the next error in
|
||||
// the error chain, if any.
|
||||
FormatError(p Printer) (next error)
|
||||
}
|
||||
|
||||
// A Printer formats error messages.
|
||||
//
|
||||
// The most common implementation of Printer is the one provided by package fmt
|
||||
// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message
|
||||
// typically provide their own implementations.
|
||||
type Printer interface {
|
||||
// Print appends args to the message output.
|
||||
Print(args ...interface{})
|
||||
|
||||
// Printf writes a formatted string.
|
||||
Printf(format string, args ...interface{})
|
||||
|
||||
// Detail reports whether error detail is requested.
|
||||
// After the first call to Detail, all text written to the Printer
|
||||
// is formatted as additional detail, or ignored when
|
||||
// detail has not been requested.
|
||||
// If Detail returns false, the caller can avoid printing the detail at all.
|
||||
Detail() bool
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// 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 xerrors
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// A Frame contains part of a call stack.
|
||||
type Frame struct {
|
||||
// Make room for three PCs: the one we were asked for, what it called,
|
||||
// and possibly a PC for skipPleaseUseCallersFrames. See:
|
||||
// https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169
|
||||
frames [3]uintptr
|
||||
}
|
||||
|
||||
// Caller returns a Frame that describes a frame on the caller's stack.
|
||||
// The argument skip is the number of frames to skip over.
|
||||
// Caller(0) returns the frame for the caller of Caller.
|
||||
func Caller(skip int) Frame {
|
||||
var s Frame
|
||||
runtime.Callers(skip+1, s.frames[:])
|
||||
return s
|
||||
}
|
||||
|
||||
// location reports the file, line, and function of a frame.
|
||||
//
|
||||
// The returned function may be "" even if file and line are not.
|
||||
func (f Frame) location() (function, file string, line int) {
|
||||
frames := runtime.CallersFrames(f.frames[:])
|
||||
if _, ok := frames.Next(); !ok {
|
||||
return "", "", 0
|
||||
}
|
||||
fr, ok := frames.Next()
|
||||
if !ok {
|
||||
return "", "", 0
|
||||
}
|
||||
return fr.Function, fr.File, fr.Line
|
||||
}
|
||||
|
||||
// Format prints the stack as error detail.
|
||||
// It should be called from an error's Format implementation
|
||||
// after printing any other error detail.
|
||||
func (f Frame) Format(p Printer) {
|
||||
if p.Detail() {
|
||||
function, file, line := f.location()
|
||||
if function != "" {
|
||||
p.Printf("%s\n ", function)
|
||||
}
|
||||
if file != "" {
|
||||
p.Printf("%s:%d\n", file, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue