Files
hakurei/internal/uevent/coldboot_test.go
Ophestra f03c0fb249
All checks were successful
Test / Create distribution (push) Successful in 1m15s
Test / Sandbox (push) Successful in 3m5s
Test / Hakurei (push) Successful in 4m12s
Test / ShareFS (push) Successful in 4m16s
Test / Sandbox (race detector) (push) Successful in 5m35s
Test / Hakurei (race detector) (push) Successful in 6m39s
Test / Flake checks (push) Successful in 1m24s
internal/uevent: synthetic events for coldboot
This causes the kernel to regenerate events that happened before earlyinit started.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-03-30 23:01:08 +09:00

228 lines
5.2 KiB
Go

package uevent_test
import (
"context"
"os"
"path/filepath"
"reflect"
"slices"
"sync"
"syscall"
"testing"
"hakurei.app/check"
"hakurei.app/internal/pkg"
"hakurei.app/internal/uevent"
)
func TestColdboot(t *testing.T) {
t.Parallel()
d := t.TempDir()
if err := os.Chmod(d, 0700); err != nil {
t.Fatal(err)
}
for _, s := range []string{
"devices",
"devices/sub",
"devices/empty",
"block",
} {
if err := os.MkdirAll(filepath.Join(d, s), 0700); err != nil {
t.Fatal(err)
}
}
for _, f := range [][2]string{
{"devices/uevent", ""},
{"devices/sub/uevent", ""},
{"block/uevent", ""},
} {
if err := os.WriteFile(
filepath.Join(d, f[0]),
[]byte(f[1]),
0600,
); err != nil {
t.Fatal(err)
}
}
var wg sync.WaitGroup
defer wg.Wait()
visited := make(chan string)
var got []string
wg.Go(func() {
for path := range visited {
got = append(got, path)
}
})
err := uevent.Coldboot(t.Context(), d, visited, func(err error) error {
t.Errorf("handleWalkErr: %v", err)
return err
})
close(visited)
if err != nil {
t.Fatalf("Coldboot: error = %v", err)
}
wg.Wait()
want := []string{
"devices/sub/uevent",
"devices/uevent",
}
for i, rel := range want {
want[i] = filepath.Join(d, rel)
}
if !slices.Equal(got, want) {
t.Errorf("Coldboot: %#v, want %#v", got, want)
}
var checksum pkg.Checksum
if err = pkg.HashDir(&checksum, check.MustAbs(d)); err != nil {
t.Fatalf("HashDir: error = %v", err)
}
wantChecksum := pkg.MustDecode("mEy_Lf5KotThm7OwMx7yTKZh5HCCyaB41pVAvI9uDMgVQFM91iosBLYsRm8bDsX8")
if checksum != wantChecksum {
t.Errorf(
"Coldboot: checksum = %s, want %s",
pkg.Encode(checksum),
pkg.Encode(wantChecksum),
)
}
}
func TestColdbootError(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
dF func(t *testing.T, d string) (wantErr error)
vF func(<-chan string, context.Context, context.CancelFunc)
hF func(d string, err error) error
}{
{"walk", func(t *testing.T, d string) (wantErr error) {
wantErr = &os.PathError{
Op: "open",
Path: filepath.Join(d, "devices"),
Err: syscall.EACCES,
}
if err := os.Mkdir(filepath.Join(d, "devices"), 0); err != nil {
t.Fatal(err)
}
return
}, nil, nil},
{"write", func(t *testing.T, d string) (wantErr error) {
wantErr = &os.PathError{
Op: "open",
Path: filepath.Join(d, "devices/uevent"),
Err: syscall.EACCES,
}
if err := os.Mkdir(filepath.Join(d, "devices"), 0700); err != nil {
t.Fatal(err)
} else if err = os.WriteFile(filepath.Join(d, "devices/uevent"), nil, 0); err != nil {
t.Fatal(err)
}
return
}, nil, nil},
{"deref", func(t *testing.T, d string) (wantErr error) {
if err := os.Mkdir(filepath.Join(d, "devices"), 0700); err != nil {
t.Fatal(err)
} else if err = os.Symlink("/proc/nonexistent", filepath.Join(d, "devices/uevent")); err != nil {
t.Fatal(err)
}
return
}, nil, nil},
{"deref handle", func(t *testing.T, d string) (wantErr error) {
if err := os.Mkdir(filepath.Join(d, "devices"), 0700); err != nil {
t.Fatal(err)
} else if err = os.Symlink("/proc/nonexistent", filepath.Join(d, "devices/uevent")); err != nil {
t.Fatal(err)
}
return
}, nil, func(d string, err error) error {
if reflect.DeepEqual(err, &os.PathError{
Op: "open",
Path: filepath.Join(d, "devices/uevent"),
Err: syscall.ENOENT,
}) {
return nil
}
return err
}},
{"cancel early", func(t *testing.T, d string) (wantErr error) {
wantErr = context.Canceled
if err := os.Mkdir(filepath.Join(d, "devices"), 0700); err != nil {
t.Fatal(err)
}
return
}, func(visited <-chan string, ctx context.Context, cancel context.CancelFunc) {
if visited == nil {
cancel()
}
return
}, nil},
{"cancel", func(t *testing.T, d string) (wantErr error) {
wantErr = context.Canceled
if err := os.Mkdir(filepath.Join(d, "devices"), 0700); err != nil {
t.Fatal(err)
} else if err = os.WriteFile(filepath.Join(d, "devices/uevent"), nil, 0600); err != nil {
t.Fatal(err)
} else if err = os.Mkdir(filepath.Join(d, "devices/sub"), 0700); err != nil {
t.Fatal(err)
} else if err = os.WriteFile(filepath.Join(d, "devices/sub/uevent"), nil, 0600); err != nil {
t.Fatal(err)
}
return
}, func(visited <-chan string, ctx context.Context, cancel context.CancelFunc) {
if visited == nil {
return
}
<-visited
cancel()
return
}, nil},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
d := t.TempDir()
wantErr := tc.dF(t, d)
var wg sync.WaitGroup
defer wg.Wait()
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
var visited chan string
if tc.vF != nil {
tc.vF(nil, ctx, cancel)
visited = make(chan string)
defer close(visited)
wg.Go(func() { tc.vF(visited, ctx, cancel) })
}
var handleWalkErr func(error) error
if tc.hF != nil {
handleWalkErr = func(err error) error {
return tc.hF(d, err)
}
}
if err := uevent.Coldboot(ctx, d, visited, handleWalkErr); !reflect.DeepEqual(err, wantErr) {
t.Errorf("Coldboot: error = %v, want %v", err, wantErr)
}
})
}
}