package main // The DKMS gate (novox/hq ADR 0508 ยง3; acceptance 1841/AC4). The META is captured from a machine whose ZFS // module declares Linux-Maximum 7.2; the BUILD_EXCLUSIVE_KERNEL case is a made-up module's pattern. import ( "os" "strings" "testing" ) func TestTheNewKernelsReleaseIsDerivedAsTheInstalledOnesIs(t *testing.T) { for _, c := range [][4]string{ {"7.2.8.arch1-2", "7.2.8-arch1-2", "7.3.1.arch1-1", "7.3.1-arch1-1"}, {"6.18.54-2", "6.18.54-2-lts", "6.18.60-1", "6.18.60-1-lts"}, } { if got := NewRelease(c[0], c[1], c[2]); got != c[3] { t.Errorf("%v: %s", c, got) } } for release, past := range map[string]bool{"7.3.1-arch1-1": true, "7.2.9-arch1-1": false, "7.2-arch1-1": false, "8.0.1": true, "6.18.60-1-lts": false} { if pastMaximum(release, "7.2") != past { t.Errorf("%s past 7.2: want %v", release, past) } } } // gateMachine is a machine with the linux and LTS kernels and these DKMS modules' sources. func gateMachine(t *testing.T, dkmsStatus string, sources map[string]string) *Machine { files := func(paths ...string) Ran { return Ran{Stdout: strings.Join(paths, "\n") + "\n"} } m := machine(byLine(map[string]Ran{ "pacman -Qlq -- linux": files("/usr/lib/modules/7.2.8-arch1-2/", "/usr/lib/modules/7.2.8-arch1-2/vmlinuz"), "pacman -Qlq -- linux-headers": files("/usr/lib/modules/7.2.8-arch1-2/build/Makefile"), "pacman -Qlq -- linux-lts": files("/usr/lib/modules/6.18.54-2-lts/vmlinuz"), "pacman -Qlq -- linux-firmware": files("/usr/lib/firmware/amdgpu/"), "dkms status": {Stdout: dkmsStatus}, }, nil), 1000) m.ReadFile = func(path string) ([]byte, error) { if s, ok := sources[path]; ok { return []byte(s), nil } return nil, errNoFile } return m } func capturedZFSSource(t *testing.T) map[string]string { out := map[string]string{} for _, f := range []string{"META", "dkms.conf"} { raw, err := os.ReadFile("testdata/dkms-zfs-linux-maximum-7.2/" + f) if err != nil { t.Fatal(err) } out["/usr/src/zfs-2.4.4/"+f] = string(raw) } return out } func gateUpgrades(t *testing.T, m *Machine, linuxTo string, reachable bool) *Upgrades { updates := newBucket() if _, err := updates.Put("node-a", UpdatesValue{Reachable: reachable, Packages: []VersionChange{ {Name: "bash", From: "5.3.15-1", To: "5.3.20-1"}, {Name: "linux", From: "7.2.8.arch1-2", To: linuxTo}, {Name: "linux-firmware", From: "20261001-1", To: "20261015-1"}, {Name: "linux-headers", From: "7.2.8.arch1-2", To: linuxTo}, {Name: "linux-lts", From: "6.18.54-2", To: "6.18.60-1"}, }}); err != nil { t.Fatal(err) } return &Upgrades{Node: "node-a", Machine: m, Updates: updates} } const zfsStatus = "zfs/2.4.4, 7.2.8-arch1-2, x86_64: installed\nzfs/2.4.4, 6.18.54-2-lts, x86_64: installed\n" func TestAKernelPastAModulesLinuxMaximumIsHeldWithItsHeaders(t *testing.T) { u := gateUpgrades(t, gateMachine(t, zfsStatus, capturedZFSSource(t)), "7.3.1.arch1-1", true) held, ignore, why := u.Gate() if why != "" { t.Fatal(why) } if len(held) != 1 || held[0] != (HeldKernel{Package: "linux", Version: "7.3.1.arch1-1", Module: "zfs/2.4.4", Bound: "Linux-Maximum: 7.2"}) { t.Errorf("held: %+v", held) } if strings.Join(ignore, " ") != "linux linux-headers" { t.Errorf("left out: %v (the LTS kernel stays within the bound and is upgraded)", ignore) } } func TestAKernelWithinTheBoundIsNotHeld(t *testing.T) { u := gateUpgrades(t, gateMachine(t, zfsStatus, capturedZFSSource(t)), "7.2.9.arch1-1", true) if held, ignore, why := u.Gate(); why != "" || len(held) != 0 || len(ignore) != 0 { t.Errorf("%+v %v %q", held, ignore, why) } } func TestABuildExclusivePatternHoldsTheKernelsItDoesNotMatch(t *testing.T) { src := map[string]string{"/usr/src/made-up-driver-1.0/dkms.conf": "PACKAGE_NAME=\"made-up-driver\"\nBUILD_EXCLUSIVE_KERNEL=\"^(6\\.|7\\.[0-2]\\.)\"\n"} u := gateUpgrades(t, gateMachine(t, "made-up-driver/1.0, 7.2.8-arch1-2, x86_64: installed\n", src), "7.3.1.arch1-1", true) held, ignore, why := u.Gate() if why != "" || len(held) != 1 || held[0].Package != "linux" || held[0].Bound != `BUILD_EXCLUSIVE_KERNEL=^(6\.|7\.[0-2]\.)` || strings.Join(ignore, " ") != "linux linux-headers" { t.Errorf("%+v %v %q", held, ignore, why) } } func TestNoRunWhenWhatTheUpgradeWouldChangeIsNotKnown(t *testing.T) { u := gateUpgrades(t, gateMachine(t, zfsStatus, capturedZFSSource(t)), "7.3.1.arch1-1", false) if _, _, why := u.Gate(); !strings.Contains(why, "could not reach its mirrors") { t.Errorf("unreachable: %q", why) } u.Updates = newBucket() if _, _, why := u.Gate(); !strings.Contains(why, "not on the bus") { t.Errorf("no updates key: %q", why) } }