tests/thirdparty: multiple packages per project (#262)
This commit is contained in:
committed by
GitHub
parent
96c4d85995
commit
84071ae4f9
230
tests/thirdparty/config.go
vendored
230
tests/thirdparty/config.go
vendored
@@ -1,4 +1,4 @@
|
||||
// Package thirdparty executes integration tests based on third-party packages that use avo.
|
||||
// Package thirdparty executes integration tests based on third-party projects that use avo.
|
||||
package thirdparty
|
||||
|
||||
import (
|
||||
@@ -45,6 +45,71 @@ type Metadata struct {
|
||||
Stars int `json:"stars,omitempty"`
|
||||
}
|
||||
|
||||
// Project defines an integration test based on a third-party project using avo.
|
||||
type Project struct {
|
||||
// Repository for the project. At the moment, all projects are available on
|
||||
// github.
|
||||
Repository GithubRepository `json:"repository"`
|
||||
|
||||
// Repository metadata.
|
||||
Metadata Metadata `json:"metadata"`
|
||||
|
||||
// Default git branch. This is used when testing against the latest version.
|
||||
DefaultBranch string `json:"default_branch,omitempty"`
|
||||
|
||||
// Version as a git sha, tag or branch.
|
||||
Version string `json:"version"`
|
||||
|
||||
// If the project test has a known problem, record it by setting this to a
|
||||
// non-zero avo issue number. If set, the project will be skipped in
|
||||
// testing.
|
||||
KnownIssue int `json:"known_issue,omitempty"`
|
||||
|
||||
// Packages within the project to test.
|
||||
Packages []*Package `json:"packages"`
|
||||
}
|
||||
|
||||
func (p *Project) defaults(set bool) {
|
||||
for _, pkg := range p.Packages {
|
||||
pkg.defaults(set)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate project definition.
|
||||
func (p *Project) Validate() error {
|
||||
if p.DefaultBranch == "" {
|
||||
return errors.New("missing default branch")
|
||||
}
|
||||
if p.Version == "" {
|
||||
return errors.New("missing version")
|
||||
}
|
||||
if len(p.Packages) == 0 {
|
||||
return errors.New("missing packages")
|
||||
}
|
||||
for _, pkg := range p.Packages {
|
||||
if err := pkg.Validate(); err != nil {
|
||||
return fmt.Errorf("package %s: %w", pkg.Name(), err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID returns an identifier for the project.
|
||||
func (p *Project) ID() string {
|
||||
return strings.ReplaceAll(p.Repository.String(), "/", "-")
|
||||
}
|
||||
|
||||
// Skip reports whether the project test should be skipped. If skipped, a known
|
||||
// issue will be set.
|
||||
func (p *Project) Skip() bool {
|
||||
return p.KnownIssue != 0
|
||||
}
|
||||
|
||||
// Reason returns the reason why the test is skipped.
|
||||
func (p *Project) Reason() string {
|
||||
return fmt.Sprintf("https://github.com/mmcloughlin/avo/issues/%d", p.KnownIssue)
|
||||
}
|
||||
|
||||
// Step represents a set of commands to run as part of the testing plan for a
|
||||
// third-party package.
|
||||
type Step struct {
|
||||
@@ -64,24 +129,11 @@ func (s *Step) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Package defines an integration test based on a third-party package using avo.
|
||||
// Package defines an integration test for a package within a project.
|
||||
type Package struct {
|
||||
// Repository the package belongs to. At the moment, all packages are
|
||||
// available on github.
|
||||
Repository GithubRepository `json:"repository"`
|
||||
|
||||
// Repository metadata.
|
||||
Metadata Metadata `json:"metadata"`
|
||||
|
||||
// Default git branch. This is used when testing against the latest version.
|
||||
DefaultBranch string `json:"default_branch,omitempty"`
|
||||
|
||||
// Version as a git sha, tag or branch.
|
||||
Version string `json:"version"`
|
||||
|
||||
// Sub-package within the repository under test. All file path references
|
||||
// will be relative to this directory. If empty the root of the repository
|
||||
// is used.
|
||||
// Sub-package within the project under test. All file path references will
|
||||
// be relative to this directory. If empty the root of the repository is
|
||||
// used.
|
||||
SubPackage string `json:"pkg,omitempty"`
|
||||
|
||||
// Path to the module file for the avo generator package. This is necessary
|
||||
@@ -95,32 +147,10 @@ type Package struct {
|
||||
Setup []*Step `json:"setup,omitempty"`
|
||||
|
||||
// Steps to run the avo code generator.
|
||||
Generate []*Step `json:"generate"` // generate commands to run
|
||||
Generate []*Step `json:"generate"`
|
||||
|
||||
// Test steps. If empty, defaults to "go test ./...".
|
||||
Test []*Step `json:"test,omitempty"`
|
||||
|
||||
// If the package test has a known problem, record it by setting this to a
|
||||
// non-zero avo issue number. If set, the package will be skipped in
|
||||
// testing.
|
||||
KnownIssue int `json:"known_issue,omitempty"`
|
||||
}
|
||||
|
||||
// ID returns an identifier for the package.
|
||||
func (p *Package) ID() string {
|
||||
pkgpath := path.Join(p.Repository.String(), p.SubPackage)
|
||||
return strings.ReplaceAll(pkgpath, "/", "-")
|
||||
}
|
||||
|
||||
// Skip reports whether the package test should be skipped. If skipped, a known
|
||||
// issue will be set.
|
||||
func (p *Package) Skip() bool {
|
||||
return p.KnownIssue != 0
|
||||
}
|
||||
|
||||
// Reason returns the reason why the test is skipped.
|
||||
func (p *Package) Reason() string {
|
||||
return fmt.Sprintf("https://github.com/mmcloughlin/avo/issues/%d", p.KnownIssue)
|
||||
}
|
||||
|
||||
// defaults sets or removes default field values.
|
||||
@@ -152,12 +182,6 @@ func applydefault(set bool, s, def string) string {
|
||||
|
||||
// Validate package definition.
|
||||
func (p *Package) Validate() error {
|
||||
if p.DefaultBranch == "" {
|
||||
return errors.New("missing default branch")
|
||||
}
|
||||
if p.Version == "" {
|
||||
return errors.New("missing version")
|
||||
}
|
||||
if p.Module == "" {
|
||||
return errors.New("missing module")
|
||||
}
|
||||
@@ -181,6 +205,19 @@ func (p *Package) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name of the package.
|
||||
func (p *Package) Name() string {
|
||||
if p.IsRoot() {
|
||||
return "root"
|
||||
}
|
||||
return p.SubPackage
|
||||
}
|
||||
|
||||
// IsRoot reports whether the package is the root of the containing project.
|
||||
func (p *Package) IsRoot() bool {
|
||||
return p.SubPackage == ""
|
||||
}
|
||||
|
||||
// Context specifies execution environment parameters for a third-party test.
|
||||
type Context struct {
|
||||
// Path to the avo version under test.
|
||||
@@ -243,37 +280,72 @@ func (p *Package) Steps(c *Context) []*Step {
|
||||
return steps
|
||||
}
|
||||
|
||||
// Packages is a collection of third-party integration tests.
|
||||
type Packages []*Package
|
||||
// Test case for a given package within a project.
|
||||
type Test struct {
|
||||
Project *Project
|
||||
Package *Package
|
||||
}
|
||||
|
||||
func (p Packages) defaults(set bool) {
|
||||
for _, pkg := range p {
|
||||
pkg.defaults(set)
|
||||
// ID returns an identifier for the test case.
|
||||
func (t *Test) ID() string {
|
||||
pkgpath := path.Join(t.Project.Repository.String(), t.Package.SubPackage)
|
||||
return strings.ReplaceAll(pkgpath, "/", "-")
|
||||
}
|
||||
|
||||
// Projects is a collection of third-party integration tests.
|
||||
type Projects []*Project
|
||||
|
||||
func (p Projects) defaults(set bool) {
|
||||
for _, prj := range p {
|
||||
prj.defaults(set)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the package collection.
|
||||
func (p Packages) Validate() error {
|
||||
for _, pkg := range p {
|
||||
if err := pkg.Validate(); err != nil {
|
||||
return fmt.Errorf("package %s: %w", pkg.ID(), err)
|
||||
// Validate the project collection.
|
||||
func (p Projects) Validate() error {
|
||||
seen := map[string]bool{}
|
||||
for _, prj := range p {
|
||||
// Project is valid.
|
||||
if err := prj.Validate(); err != nil {
|
||||
return fmt.Errorf("project %s: %w", prj.ID(), err)
|
||||
}
|
||||
|
||||
// No duplicate entries.
|
||||
id := prj.ID()
|
||||
if seen[id] {
|
||||
return fmt.Errorf("duplicate project %q", id)
|
||||
}
|
||||
seen[id] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ranked returns a copy of the packages list ranked in desending order of
|
||||
// Tests returns all test cases for the projects collection.
|
||||
func (p Projects) Tests() []*Test {
|
||||
var ts []*Test
|
||||
for _, prj := range p {
|
||||
for _, pkg := range prj.Packages {
|
||||
ts = append(ts, &Test{
|
||||
Project: prj,
|
||||
Package: pkg,
|
||||
})
|
||||
}
|
||||
}
|
||||
return ts
|
||||
}
|
||||
|
||||
// Ranked returns a copy of the projects list ranked in desending order of
|
||||
// popularity.
|
||||
func (p Packages) Ranked() Packages {
|
||||
ranked := append(Packages(nil), p...)
|
||||
func (p Projects) Ranked() Projects {
|
||||
ranked := append(Projects(nil), p...)
|
||||
sort.SliceStable(ranked, func(i, j int) bool {
|
||||
return ranked[i].Metadata.Stars > ranked[j].Metadata.Stars
|
||||
})
|
||||
return ranked
|
||||
}
|
||||
|
||||
// Top returns the top n most popular packages.
|
||||
func (p Packages) Top(n int) Packages {
|
||||
// Top returns the top n most popular projects.
|
||||
func (p Projects) Top(n int) Projects {
|
||||
top := p.Ranked()
|
||||
if len(top) > n {
|
||||
top = top[:n]
|
||||
@@ -281,44 +353,44 @@ func (p Packages) Top(n int) Packages {
|
||||
return top
|
||||
}
|
||||
|
||||
// LoadPackages loads a list of package configurations from JSON format.
|
||||
func LoadPackages(r io.Reader) (Packages, error) {
|
||||
var pkgs Packages
|
||||
// LoadProjects loads a list of project configurations from JSON format.
|
||||
func LoadProjects(r io.Reader) (Projects, error) {
|
||||
var prjs Projects
|
||||
d := json.NewDecoder(r)
|
||||
d.DisallowUnknownFields()
|
||||
if err := d.Decode(&pkgs); err != nil {
|
||||
if err := d.Decode(&prjs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkgs.defaults(true)
|
||||
return pkgs, nil
|
||||
prjs.defaults(true)
|
||||
return prjs, nil
|
||||
}
|
||||
|
||||
// LoadPackagesFile loads a list of package configurations from a JSON file.
|
||||
func LoadPackagesFile(filename string) (Packages, error) {
|
||||
// LoadProjectsFile loads a list of project configurations from a JSON file.
|
||||
func LoadProjectsFile(filename string) (Projects, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return LoadPackages(f)
|
||||
return LoadProjects(f)
|
||||
}
|
||||
|
||||
// StorePackages writes a list of package configurations in JSON format.
|
||||
func StorePackages(w io.Writer, pkgs Packages) error {
|
||||
// StoreProjects writes a list of project configurations in JSON format.
|
||||
func StoreProjects(w io.Writer, prjs Projects) error {
|
||||
e := json.NewEncoder(w)
|
||||
e.SetIndent("", " ")
|
||||
pkgs.defaults(false)
|
||||
err := e.Encode(pkgs)
|
||||
pkgs.defaults(true)
|
||||
prjs.defaults(false)
|
||||
err := e.Encode(prjs)
|
||||
prjs.defaults(true)
|
||||
return err
|
||||
}
|
||||
|
||||
// StorePackagesFile writes a list of package configurations to a JSON file.
|
||||
func StorePackagesFile(filename string, pkgs Packages) error {
|
||||
// StoreProjectsFile writes a list of project configurations to a JSON file.
|
||||
func StoreProjectsFile(filename string, prjs Projects) error {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
return StorePackages(f, pkgs)
|
||||
return StoreProjects(f, prjs)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user