blob: 6081e0c63a3fec1de0b029f618b997cd2cdf6b00 [file]
package dkms
import (
"os"
"path"
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
)
func TestFindKernelModules(t *testing.T) {
installDir := t.TempDir()
paths := []string{
"a.ko",
"b.txt",
"x/c.ko",
"x/d.ko",
"x/e.txt",
"x/y/f.ko",
"x/y/g.txt",
}
for _, p := range paths {
installPath := path.Join(installDir, p)
if err := os.MkdirAll(filepath.Dir(installPath), 0777); err != nil {
t.Fatalf("%v", err)
}
if _, err := os.Create(path.Join(installDir, p)); err != nil {
t.Fatalf("%v", err)
}
}
kernelModules, err := FindKernelModules(installDir)
if err != nil {
t.Fatalf("could not find kernel modules: %v", err)
}
expected := map[string]string{
"a.ko": path.Join(installDir, "a.ko"),
"c.ko": path.Join(installDir, "x/c.ko"),
"d.ko": path.Join(installDir, "x/d.ko"),
"f.ko": path.Join(installDir, "x/y/f.ko"),
}
diff := cmp.Diff(expected, kernelModules)
if diff != "" {
t.Errorf("found kernel modules did not match expected:\ndiff: %s", diff)
}
}
func TestModuleDependencies(t *testing.T) {
deps, err := ModuleDependencies("ext4")
if err != nil {
t.Fatalf("error getting dependencies for module ext4: %v", err)
}
if len(deps) == 0 {
t.Fatalf("expected to find at least one dependency for module ext4; found none")
}
t.Logf("ext4 dependencies: %v", deps)
}
func TestModuleDependencyGraph(t *testing.T) {
insertedModules, err := InsertedModules()
if err != nil {
t.Fatalf("failed to get inserted modules: %v", err)
}
var moduleNames []string
for module := range insertedModules {
moduleNames = append(moduleNames, module)
}
// Technically, this wouldn't be fully correct if there are multiple kernels
// installed, but any nontrivial dependency graph suffices for this test.
modulePaths, err := FindKernelModules("/lib/modules/")
if err != nil {
t.Fatalf("failed to get kernel module paths")
}
graph, err := ModuleDependencyGraph(moduleNames, modulePaths)
if err != nil {
t.Fatalf("failed to build module dependency graph: %v", err)
}
any_non_empty_deps := false
for _, deps := range graph {
if len(deps) > 0 {
any_non_empty_deps = true
}
}
t.Logf("module dependency graph: %v", graph)
if !any_non_empty_deps {
t.Fatalf("expected to find at least one module with dependencies; found none")
}
}
func TestModuleInsertionOrder(t *testing.T) {
testCases := []struct {
modules []string
graph map[string][]string
expected []string
expectedErr bool
}{
{
modules: []string{"a", "b", "c", "d"},
graph: map[string][]string{
"a": {},
"b": {"d"},
"c": {"b", "a"},
"d": {},
},
expected: []string{"a", "d", "b", "c"},
},
// This test case is the Lustre kernel module dependency graph, which has
// a non-trivial structure and important tie-breaking.
{
modules: []string{
"ksocklnd",
"in-kernel-ko2iblnd",
"ec",
"fid",
"fld",
"libcfs",
"lmv",
"lnet",
"lov",
"lustre",
"mdc",
"mgc",
"obdclass",
"obdecho",
"osc",
"ptlrpc",
},
graph: map[string][]string{
"ec": {},
"fid": {"obdclass", "ptlrpc", "libcfs"},
"fld": {"obdclass", "ptlrpc", "libcfs"},
"in-kernel-ko2iblnd": {"ib_core", "rdma_cm", "libcfs", "lnet"},
"ksocklnd": {"lnet", "libcfs"},
"libcfs": {},
"lmv": {"fld", "obdclass", "fid", "ptlrpc", "libcfs", "lnet"},
"lnet": {"sunrpc", "libcfs"},
"lov": {"obdclass", "ptlrpc", "fld", "libcfs"},
"lustre": {"lnet", "obdclass", "ptlrpc", "libcfs", "lov", "mdc", "lmv", "fid"},
"mdc": {"ptlrpc", "libcfs", "osc", "obdclass", "lov", "fid"},
"mgc": {"ptlrpc", "obdclass", "libcfs", "lnet"},
"obdclass": {"lnet", "libcfs"},
"obdecho": {"obdclass", "libcfs"},
"osc": {"libcfs", "obdclass", "ptlrpc", "lnet"},
"ptlrpc": {"lnet", "libcfs", "obdclass"},
},
expected: []string{
"libcfs",
"sunrpc",
"lnet",
"ksocklnd",
"ib_core",
"rdma_cm",
"in-kernel-ko2iblnd",
"ec",
"obdclass",
"ptlrpc",
"fid",
"fld",
"lmv",
"lov",
"osc",
"mdc",
"lustre",
"mgc",
"obdecho",
},
},
{
modules: []string{"nfs"},
graph: map[string][]string{
"nfs": {"nfs"},
},
expected: nil,
expectedErr: true,
},
{
modules: []string{
"sunrpc",
"auth_rpcgss",
"rpcsec_gss_krb5",
"rpcrdma",
"nfsv4",
"nfs",
"nfs_layout_nfsv41_files",
"blocklayoutdriver",
"nfs_layout_flexfiles",
"nfsd",
"nfs_acl",
"compat_nfs_ssc",
"lockd",
},
// Nontrivial graph with a circular dependency:
// ( nfs -> lockd -> grace -> blocklayoutdriver -> nfs )
graph: map[string][]string{
"sunrpc": {},
"auth_rpcgss": {"sunrpc"},
"rpcsec_gss_krb5": {"sunrpc", "auth_rpcgss"},
"rpcrdma": {"sunrpc", "rdma_cm", "ib_core"},
"nfs": {"sunrpc", "lockd"},
"nfsv4": {"nfs", "sunrpc", "dns_resolver"},
"nfs_layout_nfsv41_files": {"nfsv4", "nfs", "sunrpc"},
"blocklayoutdriver": {"nfsv4", "nfs", "sunrpc"},
"nfs_layout_flexfiles": {"nfsv4", "nfs", "sunrpc"},
"nfsd": {"sunrpc", "lockd", "nfs_acl", "grace", "auth_rpcgss"},
"nfs_acl": {"sunrpc"},
"compat_nfs_ssc": {},
"lockd": {"grace", "sunrpc"},
"grace": {"blocklayoutdriver"},
},
expected: nil,
expectedErr: true,
},
}
for _, testCase := range testCases {
order, err := ModuleInsertionOrder(testCase.modules, testCase.graph)
if err != nil {
if testCase.expectedErr {
t.Logf("got expected error: %v", err)
} else {
t.Errorf("got unexpected error: %v", err)
}
} else if testCase.expectedErr {
t.Errorf("expected test case to error, but didn't")
}
diff := cmp.Diff(order, testCase.expected)
if len(diff) > 0 {
t.Fatalf("order differs from expected: %s", diff)
}
}
}