blob: d55a42ec0c70b78e94c742ca057b256617000c3a [file] [log] [blame]
package imports
import (
"context"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"go.skia.org/infra/go/testutils"
"go.skia.org/infra/go/util"
)
// Any package named testutil(s) and the packages in the following list are
// allowed to import other test packages, but non-test packages are not allowed
// to import them.
var testPackages = []string{
"github.com/stretchr/testify/assert",
"github.com/stretchr/testify/require",
"go.skia.org/infra/go/deepequal/assertdeep",
"go.skia.org/infra/go/git/testutils/mem_git",
"go.skia.org/infra/go/mockhttpclient",
"go.skia.org/infra/golden/go/mocks",
"go.skia.org/infra/golden/go/ingestion/mocks",
"go.skia.org/infra/task_scheduler/go/scheduling/perftest",
"testing",
"go.skia.org/infra/perf/go/shortcut/shortcuttest",
"go.skia.org/infra/perf/go/sql/sqltest",
"go.skia.org/infra/perf/go/alerts/alertstest",
"go.skia.org/infra/perf/go/regression/regressiontest",
"go.skia.org/infra/perf/go/git/gittest",
"go.skia.org/infra/golden/go/sql/sqltest",
"go.skia.org/infra/go/gcs/test_gcsclient",
}
// TODO(borenet): this list should be empty.
// items should not be added to this list, only removed.
var legacyTestImportExceptions = map[string][]string{
"go.skia.org/infra/task_driver/go/td": {
"github.com/stretchr/testify/require",
},
"go.skia.org/infra/task_scheduler/go/db": {
"github.com/stretchr/testify/require",
"go.skia.org/infra/go/deepequal/assertdeep",
"go.skia.org/infra/go/git/testutils",
"go.skia.org/infra/go/testutils",
},
"go.skia.org/infra/task_scheduler/go/task_scheduler": {
"go.skia.org/infra/task_scheduler/go/testutils",
},
"go.skia.org/infra/task_scheduler/go/tryjobs": {
"github.com/stretchr/testify/require",
"go.skia.org/infra/go/depot_tools/testutils",
"go.skia.org/infra/go/git/testutils",
"go.skia.org/infra/go/mockhttpclient",
"go.skia.org/infra/go/testutils",
"go.skia.org/infra/task_scheduler/go/task_cfg_cache/testutils",
"go.skia.org/infra/go/buildbucket/mocks",
"go.skia.org/infra/go/cas/mocks",
},
}
// isTestPackage returns true if the given package should be considered a test
// package, ie. it is allowed to import other test packages and non-test
// packages are not allowed to import it.
func isTestPackage(pkg string) bool {
return strings.HasSuffix(pkg, "shared_tests") ||
strings.HasSuffix(pkg, "testutil") ||
strings.HasSuffix(pkg, "testutils") ||
strings.HasSuffix(pkg, "mocks") ||
util.In(pkg, testPackages)
}
// testImportAllowed returns true if the given non-test package is allowed
// to import the given test package.
func testImportAllowed(importer, importee string) bool {
return util.In(importee, legacyTestImportExceptions[importer])
}
// TestImports verifies that we follow some basic rules for imports:
// 1. No non-test package imports a test package, with the exception of those
// listed in legacyTestImportExceptions.
// 2. No packages use CGO.
//
// These checks are combined into one test because LoadAllPackageData is slow.
func TestImports(t *testing.T) {
// For compatibility with Bazel: the "go" command fails if HOME is not set.
testutils.SetUpFakeHomeDir(t, "imports_test")
// Find all packages, categorize as test and non-test.
pkgs, err := LoadAllPackageData(context.Background())
assert.NoError(t, err)
testPkgs := make(map[string]*Package, len(pkgs))
nonTestPkgs := make(map[string]*Package, len(pkgs))
for name, pkg := range pkgs {
if isTestPackage(name) {
testPkgs[name] = pkg
} else {
nonTestPkgs[name] = pkg
}
}
for _, name := range testPackages {
testPkgs[name] = &Package{} // Not actually used.
}
// 1. Ensure that no non-test package imports a test package.
for name, pkg := range nonTestPkgs {
for _, imported := range pkg.Imports {
_, importsTestPkg := testPkgs[imported]
// TODO: Remove this once legacyTestImportExceptions is empty.
if importsTestPkg && testImportAllowed(name, imported) {
importsTestPkg = false
}
assert.Falsef(t, importsTestPkg, "Non-test package %s imports test package %s", name, imported)
}
// Verify that legacyTestImportExceptions doesn't contain more
// entries than it should.
// TODO: Remove this once legacyTestImportExceptions is empty.
for _, allowedTestImport := range legacyTestImportExceptions[name] {
assert.Truef(t, util.In(allowedTestImport, pkg.Imports), "Non-test package %s is allowed to import %s but does not.", name, allowedTestImport)
}
}
// 2. Ensure that no package uses CGO.
for name, pkg := range pkgs {
for _, imp := range pkg.Imports {
assert.NotEqual(t, "C", imp, "Package %s uses CGO.", name)
}
}
}