mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-14 23:34:01 -04:00
101 lines
3.2 KiB
Go
101 lines
3.2 KiB
Go
package systemd
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
"os"
|
|
"testing"
|
|
"testing/synctest"
|
|
"time"
|
|
|
|
"github.com/godbus/dbus/v5"
|
|
"github.com/godbus/dbus/v5/prop"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
type readinessSystemdManager struct {
|
|
pid uint32
|
|
unit dbus.ObjectPath
|
|
}
|
|
|
|
func (m *readinessSystemdManager) GetUnitByPID(pid uint32) (dbus.ObjectPath, *dbus.Error) {
|
|
if pid != m.pid {
|
|
return "", dbus.NewError("org.freedesktop.systemd1.NoSuchUnit", nil)
|
|
}
|
|
return m.unit, nil
|
|
}
|
|
|
|
func readinessSystemdBus(t *testing.T, timeout time.Duration) *dbus.Conn {
|
|
t.Helper()
|
|
bus := shellReadyBus(t)
|
|
reply, err := bus.RequestName("org.freedesktop.systemd1", dbus.NameFlagDoNotQueue)
|
|
require.NoError(t, err)
|
|
require.Equal(t, dbus.RequestNameReplyPrimaryOwner, reply)
|
|
manager := &readinessSystemdManager{pid: uint32(os.Getpid()), unit: "/org/freedesktop/systemd1/unit/custom_2eservice"}
|
|
require.NoError(t, bus.Export(manager, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager"))
|
|
var now unix.Timespec
|
|
require.NoError(t, unix.ClockGettime(unix.CLOCK_MONOTONIC, &now))
|
|
_, err = prop.Export(bus, manager.unit, prop.Map{
|
|
"org.freedesktop.systemd1.Service": {
|
|
"TimeoutStartUSec": {Value: uint64(timeout.Microseconds())},
|
|
"ExecMainStartTimestampMonotonic": {Value: uint64(now.Nano() / 1000)},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
return bus
|
|
}
|
|
|
|
func TestReadinessUsesServiceStartupTimeout(t *testing.T) {
|
|
readinessSystemdBus(t, 150*time.Millisecond)
|
|
socket := notifySocket(t)
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
NewReadiness().Wait(ctx)
|
|
require.NoError(t, ctx.Err(), "must use the service timeout, not wait for parent cancellation")
|
|
require.Equal(t, "READY=1\nSTATUS=Shell started with a readiness warning: shell readiness timed out: UI did not report readiness", readNotification(t, socket))
|
|
}
|
|
|
|
func TestRemainingStartupTimeout(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
timeoutUS uint64
|
|
elapsedUS uint64
|
|
want time.Duration
|
|
}{
|
|
{"unit_timeout", 90_000_000, 0, 85 * time.Second},
|
|
{"override", 180_000_000, 0, 175 * time.Second},
|
|
{"elapsed_startup", 90_000_000, 30_000_000, 55 * time.Second},
|
|
{"short_timeout", 2_000_000, 500_000, 1300 * time.Millisecond},
|
|
{"expired", 90_000_000, 85_000_000, 0},
|
|
{"past_deadline", 90_000_000, 100_000_000, 0},
|
|
{"infinity", math.MaxUint64, 100_000_000, -1},
|
|
{"disabled", 0, 100_000_000, -1},
|
|
{"overflow", math.MaxUint64 - 1, 0, time.Duration(math.MaxInt64/int64(time.Microsecond)) * time.Microsecond},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require.Equal(t, tc.want, remainingStartupTimeout(tc.timeoutUS, tc.elapsedUS))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReadinessUnlimitedTimeout(t *testing.T) {
|
|
for _, uiReady := range []bool{false, true} {
|
|
synctest.Test(t, func(t *testing.T) {
|
|
r := NewReadiness()
|
|
if uiReady {
|
|
r.ShellReady(42)
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go func() {
|
|
time.Sleep(2 * time.Minute)
|
|
cancel()
|
|
}()
|
|
start := time.Now()
|
|
err := r.wait(ctx, -1, func(context.Context, uint32) (bool, error) { return false, nil })
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2*time.Minute, time.Since(start))
|
|
})
|
|
}
|
|
}
|