| 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 |
| }{ |
| { |
| 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", |
| }, |
| }, |
| } |
| |
| for _, testCase := range testCases { |
| order := ModuleInsertionOrder(testCase.modules, testCase.graph) |
| |
| diff := cmp.Diff(order, testCase.expected) |
| if len(diff) > 0 { |
| t.Fatalf("order differs from expected: %s", diff) |
| } |
| } |
| } |