blob: 590b2f62f0d11aad410da351b564a8654132147e [file] [log] [blame]
// 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 fetch
import (
"archive/zip"
"context"
"net/http"
"sort"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/safehtml"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/licenses"
"golang.org/x/pkgsite/internal/log"
"golang.org/x/pkgsite/internal/proxy"
"golang.org/x/pkgsite/internal/stdlib"
)
var testProxyCommitTime = time.Date(2019, 1, 30, 0, 0, 0, 0, time.UTC)
func cleanFetchResult(fr *FetchResult, detector *licenses.Detector) *FetchResult {
fr.ModulePath = fr.Module.ModulePath
if fr.GoModPath == "" {
fr.GoModPath = fr.ModulePath
}
if fr.Status == 0 {
fr.Status = 200
}
if fr.Module.Version == "" {
fr.Module.Version = "v1.0.0"
}
if fr.RequestedVersion == "" {
fr.RequestedVersion = fr.Module.Version
}
fr.ResolvedVersion = fr.Module.Version
if fr.Module.CommitTime.IsZero() {
fr.Module.CommitTime = testProxyCommitTime
}
allLicenses := detector.AllLicenses()
if len(allLicenses) > 0 {
fr.Module.Licenses = allLicenses
fr.Module.IsRedistributable = true
for _, d := range fr.Module.Units {
isRedist, lics := detector.PackageInfo(internal.Suffix(d.Path, fr.ModulePath))
for _, l := range lics {
d.Licenses = append(d.Licenses, l.Metadata)
}
d.IsRedistributable = isRedist
}
}
shouldSetPVS := (fr.PackageVersionStates == nil)
for _, u := range fr.Module.Units {
u.UnitMeta = internal.UnitMeta{
ModulePath: fr.Module.ModulePath,
Version: fr.Module.Version,
Path: u.Path,
Name: u.Name,
IsRedistributable: u.IsRedistributable,
Licenses: u.Licenses,
}
if u.Documentation != nil {
if u.Documentation.GOOS == "" {
u.Documentation.GOOS = "linux"
u.Documentation.GOARCH = "amd64"
}
}
if u.IsPackage() {
fr.Module.LegacyPackages = append(fr.Module.LegacyPackages, &internal.LegacyPackage{
Path: u.Path,
Licenses: u.Licenses,
V1Path: internal.V1Path(u.Path, u.ModulePath),
Name: u.Name,
Synopsis: u.Documentation.Synopsis,
DocumentationHTML: u.Documentation.HTML,
Imports: u.Imports,
GOOS: u.Documentation.GOOS,
GOARCH: u.Documentation.GOARCH,
IsRedistributable: u.IsRedistributable,
})
if shouldSetPVS {
fr.PackageVersionStates = append(
fr.PackageVersionStates, &internal.PackageVersionState{
PackagePath: u.Path,
ModulePath: fr.Module.ModulePath,
Version: fr.Module.Version,
Status: http.StatusOK,
},
)
}
}
}
return fr
}
func licenseDetector(ctx context.Context, t *testing.T, modulePath, version string, proxyClient *proxy.Client) *licenses.Detector {
t.Helper()
var (
zipReader *zip.Reader
err error
)
if modulePath == stdlib.ModulePath {
zipReader, _, err = stdlib.Zip(version)
if err != nil {
t.Fatal(err)
}
} else {
zipReader, err = proxyClient.GetZip(ctx, modulePath, version)
if err != nil {
t.Fatal(err)
}
}
logf := func(format string, args ...interface{}) {
log.Infof(ctx, format, args...)
}
return licenses.NewDetector(modulePath, version, zipReader, logf)
}
func sortFetchResult(fr *FetchResult) {
sort.Slice(fr.Module.LegacyPackages, func(i, j int) bool {
return fr.Module.LegacyPackages[i].Path < fr.Module.LegacyPackages[j].Path
})
sort.Slice(fr.Module.Units, func(i, j int) bool {
return fr.Module.Units[i].Path < fr.Module.Units[j].Path
})
sort.Slice(fr.Module.Licenses, func(i, j int) bool {
return fr.Module.Licenses[i].FilePath < fr.Module.Licenses[j].FilePath
})
sort.Slice(fr.PackageVersionStates, func(i, j int) bool {
return fr.PackageVersionStates[i].PackagePath < fr.PackageVersionStates[j].PackagePath
})
for _, dir := range fr.Module.Units {
sort.Slice(dir.Licenses, func(i, j int) bool {
return dir.Licenses[i].FilePath < dir.Licenses[j].FilePath
})
}
for _, pkg := range fr.Module.LegacyPackages {
sort.Slice(pkg.Licenses, func(i, j int) bool {
return pkg.Licenses[i].FilePath < pkg.Licenses[j].FilePath
})
}
}
// validateDocumentationHTML checks that the doc HTML contains a set of
// substrings. The desired strings consist of the "want" module's documentation
// fields, with multiple substrings separated by '~'.
func validateDocumentationHTML(t *testing.T, got, want *internal.Module) {
t.Helper()
defer func() {
if r := recover(); r != nil {
t.Fatalf("Recovered in checkDocumentationHTML: %v \n; diff = %s", r, cmp.Diff(want, got))
}
}()
checkHTML := func(msg string, i int, hGot, hWant safehtml.HTML) {
t.Helper()
got := hGot.String()
// Treat the wanted DocumentationHTML as a set of substrings to look for, separated by ~.
for _, want := range strings.Split(hWant.String(), "~") {
want = strings.TrimSpace(want)
if !strings.Contains(got, want) {
t.Errorf("doc for %s[%d]:\nmissing %q; got\n%q", msg, i, want, got)
}
}
}
for i := 0; i < len(want.LegacyPackages); i++ {
checkHTML("LegacyPackages", i, got.LegacyPackages[i].DocumentationHTML, want.LegacyPackages[i].DocumentationHTML)
}
for i := 0; i < len(want.Units); i++ {
if !want.Units[i].IsPackage() {
continue
}
checkHTML("Directories", i, got.Units[i].Documentation.HTML, want.Units[i].Documentation.HTML)
}
}