| package dkms | |
| import ( | |
| "context" | |
| "os" | |
| "path" | |
| "sort" | |
| "strings" | |
| "testing" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/fakes" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/fs" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/gcs" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/kernel" | |
| "github.com/google/go-cmp/cmp" | |
| ) | |
| func TestBuild(t *testing.T) { | |
| options := &Options{ | |
| Force: false, | |
| MakeVariables: "", | |
| InstallBuildDependencies: false, | |
| } | |
| trees := &Trees{ | |
| Dkms: t.TempDir(), | |
| Source: "testdata/source-tree", | |
| Kernel: "testdata/kernel-source/lib/modules/6.1.100/build", | |
| } | |
| pkg := &Package{ | |
| Name: "mymodule", | |
| Version: "1.0", | |
| KernelVersion: "6.1.100", | |
| Arch: "x86_64", | |
| BuildId: "18244.151.14", | |
| Trees: trees, | |
| } | |
| err := Build(pkg, options) | |
| if err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| makefilePath := path.Join(trees.Dkms, "mymodule", "1.0", "6.1.100", "x86_64", "18244.151.14", "source", "Makefile") | |
| if !fs.IsFile(makefilePath) { | |
| t.Fatalf("expected to find file %s after adding sources", makefilePath) | |
| } | |
| buildMakefilePath := path.Join(trees.Dkms, "mymodule", "1.0", "6.1.100", "x86_64", "18244.151.14", "build", "Makefile") | |
| if !fs.IsFile(buildMakefilePath) { | |
| t.Fatalf("expected to find file %s after building", buildMakefilePath) | |
| } | |
| builtModulePath := path.Join(trees.Dkms, "mymodule", "1.0", "6.1.100", "x86_64", "18244.151.14", "build", "mymodule.ko") | |
| if !fs.IsFile(builtModulePath) { | |
| t.Fatalf("expected to find file %s after building", builtModulePath) | |
| } | |
| } | |
| func TestBuildModuleSigning(t *testing.T) { | |
| signingDir := t.TempDir() | |
| options := &Options{ | |
| Force: false, | |
| MakeVariables: "", | |
| InstallBuildDependencies: false, | |
| Hash: "sha256", | |
| PrivateKeyPath: path.Join(signingDir, "rsaKey.pem"), | |
| CertificatePath: path.Join(signingDir, "cert.der"), | |
| } | |
| trees := &Trees{ | |
| Dkms: t.TempDir(), | |
| Source: "testdata/source-tree", | |
| Kernel: "testdata/kernel-source/lib/modules/6.1.100/build", | |
| } | |
| pkg := &Package{ | |
| Name: "mymodule", | |
| Version: "1.0", | |
| KernelVersion: "6.1.100", | |
| Arch: "x86_64", | |
| BuildId: "18244.151.14", | |
| Trees: trees, | |
| } | |
| key, keyBytes, err := generateRSAPrivateKey() | |
| if err != nil { | |
| t.Fatalf("failed to generate rsa key: %v", err) | |
| } | |
| if err = os.WriteFile(options.PrivateKeyPath, keyBytes, 0600); err != nil { | |
| t.Fatalf("failed to write rsa key to file (%s): %v", options.PrivateKeyPath, err) | |
| } | |
| certBytes, err := generateCertificate(key) | |
| if err != nil { | |
| t.Fatalf("failed to generate certificate: %v", err) | |
| } | |
| if err = os.WriteFile(options.CertificatePath, certBytes, 0600); err != nil { | |
| t.Fatalf("failed to write certificate to file (%s): %v", options.CertificatePath, err) | |
| } | |
| err = Build(pkg, options) | |
| if err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| builtModulePath := path.Join(trees.Dkms, "mymodule", "1.0", "6.1.100", "x86_64", "18244.151.14", "build", "mymodule.ko") | |
| if !fs.IsFile(builtModulePath) { | |
| t.Fatalf("expected to find file %s after building", builtModulePath) | |
| } | |
| moduleBytes, err := os.ReadFile(builtModulePath) | |
| if err != nil { | |
| t.Fatalf("failed to read module: %s", builtModulePath) | |
| } | |
| expectedMessage := []byte("~Module signature appended~\n") | |
| diff := cmp.Diff(expectedMessage, moduleBytes[len(moduleBytes)-len(expectedMessage):]) | |
| if diff != "" { | |
| t.Fatalf("expected signature did not match\ndiff: %s", diff) | |
| } | |
| } | |
| func TestCachedBuild(t *testing.T) { | |
| options := &Options{ | |
| Force: false, | |
| MakeVariables: "", | |
| InstallBuildDependencies: false, | |
| Upload: true, | |
| } | |
| trees := &Trees{ | |
| Dkms: t.TempDir(), | |
| Source: "testdata/source-tree", | |
| Kernel: "testdata/kernel-source/lib/modules/6.1.100/build", | |
| } | |
| pkg := &Package{ | |
| Name: "mymodule", | |
| Version: "1.0", | |
| KernelVersion: "6.1.100", | |
| Arch: "x86_64", | |
| BuildId: "18244.151.14", | |
| Trees: trees, | |
| } | |
| ctx := context.Background() | |
| fakeGCS := fakes.GCSForTest(t) | |
| fakeGCS.LogMissing = false | |
| bucket := gcs.NewGCSBucket(fakeGCS.Client, "test", "dkms") | |
| err := CachedBuild(ctx, pkg, bucket, options) | |
| if err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| cacheMakefilePath := path.Join("mymodule", "1.0", "6.1.100", "x86_64", "18244.151.14", "source", "Makefile") | |
| makefilePath := path.Join(trees.Dkms, cacheMakefilePath) | |
| if !fs.IsFile(makefilePath) { | |
| t.Fatalf("expected to find file %s after adding sources", makefilePath) | |
| } | |
| buildMakefilePath := path.Join(trees.Dkms, "mymodule", "1.0", "6.1.100", "x86_64", "18244.151.14", "build", "Makefile") | |
| if !fs.IsFile(buildMakefilePath) { | |
| t.Fatalf("expected to find file %s after building", makefilePath) | |
| } | |
| cacheBuiltModulePath := path.Join("mymodule", "1.0", "6.1.100", "x86_64", "18244.151.14", "build", "mymodule.ko") | |
| builtModulePath := path.Join(trees.Dkms, cacheBuiltModulePath) | |
| if !fs.IsFile(builtModulePath) { | |
| t.Fatalf("expected to find file %s after building", makefilePath) | |
| } | |
| cachedMakefileExists, err := bucket.Exists(ctx, cacheMakefilePath) | |
| if err != nil { | |
| t.Fatalf("failed to check if %s exists", cacheMakefilePath) | |
| } | |
| if !cachedMakefileExists { | |
| t.Fatalf("expected to find file %s in cache after adding sources", cacheMakefilePath) | |
| } | |
| cachedModuleExists, err := bucket.Exists(ctx, cacheBuiltModulePath) | |
| if err != nil { | |
| t.Fatalf("failed to check if %s exists", cacheBuiltModulePath) | |
| } | |
| if !cachedModuleExists { | |
| t.Fatalf("expected to find file %s in cache after building modules", cacheBuiltModulePath) | |
| } | |
| } | |
| func TestDefaultMakeVariables(t *testing.T) { | |
| trees := &Trees{ | |
| Kernel: "testdata/kernel-sources", | |
| } | |
| pkg := &Package{ | |
| Arch: "abc", | |
| Trees: trees, | |
| } | |
| variables, err := DefaultMakeVariables(pkg) | |
| if err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| expectedList := []string{ | |
| "ARCH=abc", | |
| // because CC and CXX are loaded from toolchain_env, the arch will be | |
| // arch from the file and not the argument to DefaultMakeVariables | |
| "CC=toolchain/bin/x86_64-cros-linux-gnu-clang", | |
| "CXX=toolchain/bin/x86_64-cros-linux-gnu-clang++", | |
| "LD=toolchain/bin/abc-cros-linux-gnu-ld.lld", | |
| "STRIP=toolchain/bin/llvm-strip", | |
| "OBJCOPY=toolchain/bin/llvm-objcopy", | |
| "HOSTCC=abc-pc-linux-gnu-clang", | |
| "HOSTCXX=abc-pc-linux-gnu-clang++", | |
| "HOSTLD=abc-pc-linux-gnu-clang", | |
| } | |
| sort.Strings(expectedList) | |
| expected := strings.Join(expectedList, " ") | |
| diff := cmp.Diff(expected, variables) | |
| if diff != "" { | |
| t.Fatalf("default variables did not match expected\ndiff: %s", diff) | |
| } | |
| } | |
| func TestApplyPatch(t *testing.T) { | |
| buildDir := t.TempDir() | |
| if err := fs.CopyDir("testdata/source-tree/mymodule-1.0", buildDir, 0777); err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| patchPath := path.Join(buildDir, "patches", "a.patch") | |
| if err := ApplyPatch(buildDir, patchPath); err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| contents, err := os.ReadFile(path.Join(buildDir, "mymodule.c")) | |
| if err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| text := string(contents) | |
| if !strings.Contains(text, "patched") { | |
| t.Fatalf("expected mymodule.c to have comment `//patched` after patch is applied") | |
| } | |
| } | |
| func TestInstallBuildDependencies(t *testing.T) { | |
| downloader := kernel.FakeDownloader{} | |
| ctx := context.Background() | |
| targetDir := t.TempDir() | |
| defer os.RemoveAll(targetDir) | |
| err := installBuildDependencies(ctx, &downloader, targetDir) | |
| if err != nil { | |
| t.Errorf("failed to install build dependencies headers: %v", err) | |
| } | |
| if !kernel.KernelHeadersInstalled(targetDir) { | |
| t.Errorf("expected headers to be installed") | |
| } | |
| if !kernel.CompilerToolchainInstalled(targetDir) { | |
| t.Errorf("expected compiler toolchain to be installed") | |
| } | |
| } |