internal/kobject: improve error JSON representation
All checks were successful
Test / Create distribution (push) Successful in 1m4s
Test / Sandbox (push) Successful in 2m55s
Test / Hakurei (push) Successful in 3m52s
Test / ShareFS (push) Successful in 3m44s
Test / Sandbox (race detector) (push) Successful in 5m25s
Test / Hakurei (race detector) (push) Successful in 6m35s
Test / Flake checks (push) Successful in 1m22s

This is now usable by internal/report.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-05-26 18:06:07 +09:00
parent 12b9f51128
commit caf3e0db4b
3 changed files with 238 additions and 143 deletions

View File

@@ -9,6 +9,7 @@ import (
"strconv" "strconv"
"sync" "sync"
"hakurei.app/internal/report"
"hakurei.app/internal/uevent" "hakurei.app/internal/uevent"
) )
@@ -29,9 +30,9 @@ const (
// Object represents a kernel object. // Object represents a kernel object.
type Object struct { type Object struct {
// Origin of the object. // Origin of the object.
State int `json:"-"` State int `json:"state,omitempty"`
// Set by [uevent.KOBJ_OFFLINE] and [uevent.KOBJ_ONLINE]. // Set by [uevent.KOBJ_OFFLINE] and [uevent.KOBJ_ONLINE].
Offline bool Offline bool `json:"offline,omitempty"`
// alloc_uevent_skb: devpath // alloc_uevent_skb: devpath
DevPath string `json:"devpath"` DevPath string `json:"devpath"`
@@ -48,11 +49,11 @@ type Object struct {
Env map[string]string `json:"env"` Env map[string]string `json:"env"`
} }
// Clone returns a copy of o. // Clone returns the address of a copy of o.
func (o *Object) Clone() Object { func (o *Object) Clone() *Object {
v := *o v := *o
v.Env = maps.Clone(o.Env) v.Env = maps.Clone(o.Env)
return v return &v
} }
// GoString returns compound literal for the underlying value. // GoString returns compound literal for the underlying value.
@@ -194,77 +195,94 @@ func (s *State) Range(ctx context.Context, f func(o *Object) bool) {
} }
} }
// UnexpectedColdbootError is reported by [State.Consume] for a coldboot event // An EventError describes a malformed or inconsistent [Event].
// with action other than the expected [uevent.KOBJ_ADD]. type EventError struct {
type UnexpectedColdbootError Event Kind int `json:"fault"`
E Event `json:"event"`
func (e UnexpectedColdbootError) Error() string { O *Object `json:"object,omitempty"`
return "unexpected " + e.Action.String() + " coldboot event"
} }
// DuplicateAddError is reported by [State.Consume] for a [uevent.KOBJ_ADD] var _ report.RepresentableError = EventError{}
// event on a still-existing entry that was not the result of a coldboot.
type DuplicateAddError Event
func (e DuplicateAddError) Error() string { func (EventError) Representable() {}
return "duplicate add event on devpath " + strconv.Quote(e.DevPath)
const (
// EUnexpectedColdboot is reported for a coldboot event with action other
// than the expected [uevent.KOBJ_ADD].
EUnexpectedColdboot = iota
// EDuplicateAdd is reported for a [uevent.KOBJ_ADD] event on a
// still-existing entry that was not the result of a coldboot.
EDuplicateAdd
// EBadTarget is reported for an event on a nonexistent [Object]. This is
// generally only possible before coldboot completes.
EBadTarget
// ERemoveState is reported for a [uevent.KOBJ_REMOVE] event targeting an
// entry in a state other than [StateColdboot] and [StateNew].
ERemoveState
// EUnexpectedOffline is reported for a [uevent.KOBJ_OFFLINE] or
// [uevent.KOBJ_ONLINE] event targeting an already offline or online object.
EUnexpectedOffline
// EBindState is reported for a [uevent.KOBJ_BIND] event targeting an entry
// in a state other than [StateColdboot] and [StateNew].
EBindState
// EUnbindState is reported for a [uevent.KOBJ_UNBIND] event targeting an
// entry in a state other than [StateBound].
EUnbindState
// EMalformedMove is reported for a [uevent.KOBJ_MOVE] event missing the
// DEVPATH_OLD environment variable.
EMalformedMove
)
func (e EventError) Error() string {
switch e.Kind {
case EUnexpectedColdboot:
return "unexpected " + e.E.Action.String() + " coldboot event"
case EDuplicateAdd:
return "duplicate add event on devpath " + strconv.Quote(e.E.DevPath)
case EBadTarget:
return "unexpected " + e.E.Action.String() + " event on devpath " +
strconv.Quote(e.E.DevPath)
case ERemoveState:
if e.O == nil {
return "invalid remove event error"
} }
return "remove event targeting devpath " + strconv.Quote(e.E.DevPath) +
// TargetError is reported by [State.Consume] for an event on a nonexistent " in state " + strconv.Itoa(e.O.State)
// entry. This is generally only possible before coldboot completes. case EUnexpectedOffline:
type TargetError Event if e.O == nil {
return "invalid unexpected offline error"
func (e TargetError) Error() string {
return "unexpected " + e.Action.String() +
" event on devpath " + strconv.Quote(e.DevPath)
} }
if e.O.Offline {
// RemoveStateError is reported by [State.Consume] for a [uevent.KOBJ_REMOVE] return "offline event targeting devpath " + strconv.Quote(e.E.DevPath)
// event targeting an entry in a state other than [StateColdboot] and [StateNew].
type RemoveStateError Object
func (e RemoveStateError) Error() string {
return "remove event targeting devpath " + strconv.Quote(e.DevPath) +
" in state " + strconv.Itoa(e.State)
} }
return "online event targeting devpath " + strconv.Quote(e.E.DevPath)
// BindStateError is reported by [State.Consume] for a [uevent.KOBJ_BIND] event case EBindState:
// targeting an entry in a state other than [StateColdboot] and [StateNew]. if e.O == nil {
type BindStateError Object return "invalid bind state error"
func (e BindStateError) Error() string {
return "bind event targeting devpath " + strconv.Quote(e.DevPath) +
" in state " + strconv.Itoa(e.State)
} }
return "bind event targeting devpath " + strconv.Quote(e.E.DevPath) +
// UnbindStateError is reported by [State.Consume] for a [uevent.KOBJ_UNBIND] " in state " + strconv.Itoa(e.O.State)
// event targeting an entry in a state other than [StateBound]. case EUnbindState:
type UnbindStateError Object if e.O == nil {
return "invalid unbind state error"
func (e UnbindStateError) Error() string {
return "unbind event targeting devpath " + strconv.Quote(e.DevPath) +
" in state " + strconv.Itoa(e.State)
} }
return "unbind event targeting devpath " + strconv.Quote(e.E.DevPath) +
// MalformedMoveError is reported by [State.Consume] for a [uevent.KOBJ_MOVE] " in state " + strconv.Itoa(e.O.State)
// event missing the DEVPATH_OLD environment variable. case EMalformedMove:
type MalformedMoveError Event return "move event targeting devpath " + strconv.Quote(e.E.DevPath) +
func (e MalformedMoveError) Error() string {
return "move event targeting devpath " + strconv.Quote(e.DevPath) +
" missing DEVPATH_OLD" " missing DEVPATH_OLD"
default:
return "invalid event error kind " + strconv.Itoa(e.Kind)
}
} }
// UnexpectedOfflineError is reported by [State.Consume] for a // NewError returns a new [EventError] for e and o.
// [uevent.KOBJ_OFFLINE] or [uevent.KOBJ_ONLINE] event targeting an already func (e *Event) NewError(kind int, o *Object) error {
// offline or online object. if o != nil {
type UnexpectedOfflineError Object o = o.Clone()
func (e UnexpectedOfflineError) Error() string {
if e.Offline {
return "offline event targeting devpath " + strconv.Quote(e.DevPath)
} }
return "online event targeting devpath " + strconv.Quote(e.DevPath) return EventError{kind, e.Clone(), o}
} }
// processEvent merges an event into s. // processEvent merges an event into s.
@@ -274,7 +292,7 @@ func (s *State) processEvent(e *Event) {
coldboot := e.Synth != nil coldboot := e.Synth != nil
if e.Action != uevent.KOBJ_ADD && coldboot { if e.Action != uevent.KOBJ_ADD && coldboot {
s.reportErr(UnexpectedColdbootError(e.Clone())) s.reportErr(e.NewError(EUnexpectedColdboot, nil))
return return
} }
@@ -282,7 +300,7 @@ func (s *State) processEvent(e *Event) {
case uevent.KOBJ_ADD: case uevent.KOBJ_ADD:
if e.Synth == nil { if e.Synth == nil {
if o, ok := s.uevent[e.DevPath]; ok { if o, ok := s.uevent[e.DevPath]; ok {
s.reportErr(DuplicateAddError(e.Clone())) s.reportErr(e.NewError(EDuplicateAdd, o))
o.merge(e.Env) o.merge(e.Env)
s.dispatchIter(o) s.dispatchIter(o)
return return
@@ -299,10 +317,10 @@ func (s *State) processEvent(e *Event) {
case uevent.KOBJ_REMOVE: case uevent.KOBJ_REMOVE:
if o, ok := s.uevent[e.DevPath]; !ok { if o, ok := s.uevent[e.DevPath]; !ok {
s.reportErr(TargetError(e.Clone())) s.reportErr(e.NewError(EBadTarget, nil))
return return
} else if o.State != StateColdboot && o.State != StateNew { } else if o.State != StateColdboot && o.State != StateNew {
s.reportErr(RemoveStateError(o.Clone())) s.reportErr(e.NewError(ERemoveState, o))
} }
delete(s.uevent, e.DevPath) delete(s.uevent, e.DevPath)
return return
@@ -310,7 +328,7 @@ func (s *State) processEvent(e *Event) {
case uevent.KOBJ_CHANGE: case uevent.KOBJ_CHANGE:
o, ok := s.uevent[e.DevPath] o, ok := s.uevent[e.DevPath]
if !ok { if !ok {
s.reportErr(TargetError(e.Clone())) s.reportErr(e.NewError(EBadTarget, nil))
// this suffers from the coldboot race window similar to KOBJ_MOVE, // this suffers from the coldboot race window similar to KOBJ_MOVE,
// however this action combines driver-specific and change-specific // however this action combines driver-specific and change-specific
// environment variables and combines them with environment // environment variables and combines them with environment
@@ -333,11 +351,11 @@ func (s *State) processEvent(e *Event) {
case uevent.KOBJ_MOVE: case uevent.KOBJ_MOVE:
var o *Object var o *Object
if old, ok := e.Env["DEVPATH_OLD"]; !ok { if old, ok := e.Env["DEVPATH_OLD"]; !ok {
s.reportErr(MalformedMoveError(e.Clone())) s.reportErr(e.NewError(EMalformedMove, nil))
// not reached // not reached
o = e.makeColdboot() o = e.makeColdboot()
} else if o, ok = s.uevent[old]; !ok { } else if o, ok = s.uevent[old]; !ok {
s.reportErr(TargetError(e.Clone())) s.reportErr(e.NewError(EBadTarget, nil))
// this generally happens during coldboot, dropping the event here // this generally happens during coldboot, dropping the event here
// may cause inconsistent state if the coldboot event for this // may cause inconsistent state if the coldboot event for this
// object was generated before the bind event // object was generated before the bind event
@@ -356,14 +374,14 @@ func (s *State) processEvent(e *Event) {
case uevent.KOBJ_ONLINE: case uevent.KOBJ_ONLINE:
o, ok := s.uevent[e.DevPath] o, ok := s.uevent[e.DevPath]
if !ok { if !ok {
s.reportErr(TargetError(e.Clone())) s.reportErr(e.NewError(EBadTarget, nil))
// coldboot race window similar to an unexpected KOBJ_MOVE // coldboot race window similar to an unexpected KOBJ_MOVE
o = e.makeColdboot() o = e.makeColdboot()
s.uevent[e.DevPath] = o s.uevent[e.DevPath] = o
o.merge(e.Env) o.merge(e.Env)
} }
if !o.Offline { if !o.Offline {
s.reportErr(UnexpectedOfflineError(o.Clone())) s.reportErr(e.NewError(EUnexpectedOffline, o))
} }
o.Offline = false o.Offline = false
s.dispatchIter(o) s.dispatchIter(o)
@@ -372,14 +390,14 @@ func (s *State) processEvent(e *Event) {
case uevent.KOBJ_OFFLINE: case uevent.KOBJ_OFFLINE:
o, ok := s.uevent[e.DevPath] o, ok := s.uevent[e.DevPath]
if !ok { if !ok {
s.reportErr(TargetError(e.Clone())) s.reportErr(e.NewError(EBadTarget, nil))
// coldboot race window similar to an unexpected KOBJ_MOVE // coldboot race window similar to an unexpected KOBJ_MOVE
o = e.makeColdboot() o = e.makeColdboot()
s.uevent[e.DevPath] = o s.uevent[e.DevPath] = o
o.merge(e.Env) o.merge(e.Env)
} }
if o.Offline { if o.Offline {
s.reportErr(UnexpectedOfflineError(o.Clone())) s.reportErr(e.NewError(EUnexpectedOffline, o))
} }
o.Offline = true o.Offline = true
s.dispatchIter(o) s.dispatchIter(o)
@@ -388,13 +406,13 @@ func (s *State) processEvent(e *Event) {
case uevent.KOBJ_BIND: case uevent.KOBJ_BIND:
o, ok := s.uevent[e.DevPath] o, ok := s.uevent[e.DevPath]
if !ok { if !ok {
s.reportErr(TargetError(e.Clone())) s.reportErr(e.NewError(EBadTarget, nil))
// coldboot race window similar to an unexpected KOBJ_MOVE // coldboot race window similar to an unexpected KOBJ_MOVE
o = e.makeColdboot() o = e.makeColdboot()
s.uevent[e.DevPath] = o s.uevent[e.DevPath] = o
} }
if o.State != StateColdboot && o.State != StateNew { if o.State != StateColdboot && o.State != StateNew {
s.reportErr(BindStateError(o.Clone())) s.reportErr(e.NewError(EBindState, o))
} }
o.State = StateBound o.State = StateBound
o.merge(e.Env) o.merge(e.Env)
@@ -404,13 +422,13 @@ func (s *State) processEvent(e *Event) {
case uevent.KOBJ_UNBIND: case uevent.KOBJ_UNBIND:
o, ok := s.uevent[e.DevPath] o, ok := s.uevent[e.DevPath]
if !ok { if !ok {
s.reportErr(TargetError(e.Clone())) s.reportErr(e.NewError(EBadTarget, nil))
// coldboot race window similar to an unexpected KOBJ_MOVE, but does // coldboot race window similar to an unexpected KOBJ_MOVE, but does
// not result in inconsistent state if dropped // not result in inconsistent state if dropped
return return
} }
if o.State != StateBound { if o.State != StateBound {
s.reportErr(UnbindStateError(o.Clone())) s.reportErr(e.NewError(EUnbindState, o))
} }
o.State = StateNew o.State = StateNew
o.Driver = "" o.Driver = ""
@@ -423,7 +441,7 @@ func (s *State) processEvent(e *Event) {
} }
} }
// BadSequenceError is reported by [State.Consume] for an unexpected SEQNUM. // BadSequenceError is reported for an unexpected SEQNUM.
type BadSequenceError struct{ Got, Want uint64 } type BadSequenceError struct{ Got, Want uint64 }
func (e BadSequenceError) Error() string { func (e BadSequenceError) Error() string {

View File

@@ -42,12 +42,12 @@ func TestConsume(t *testing.T) {
"SYNTH_UUID=fdfdfdfd-fdfd-fdfd-fdfd-fdfdfdfdfdfd", "SYNTH_UUID=fdfdfdfd-fdfd-fdfd-fdfd-fdfdfdfdfdfd",
}}, }},
}, map[string]*Object{}, nil, []error{ }, map[string]*Object{}, nil, []error{
UnexpectedColdbootError{ (&Event{
Action: uevent.KOBJ_BIND, Action: uevent.KOBJ_BIND,
DevPath: "\x00", DevPath: "\x00",
Env: map[string]string{}, Env: map[string]string{},
Synth: &coldboot, Synth: &coldboot,
}, }).NewError(EUnexpectedColdboot, nil),
}}, }},
{"sequence", []*uevent.Message{ {"sequence", []*uevent.Message{
@@ -85,12 +85,16 @@ func TestConsume(t *testing.T) {
Env: map[string]string{"V": "\xfd"}, Env: map[string]string{"V": "\xfd"},
}, },
}, nil, []error{ }, nil, []error{
DuplicateAddError{ (&Event{
Action: uevent.KOBJ_ADD, Action: uevent.KOBJ_ADD,
DevPath: "\x00", DevPath: "\x00",
Env: map[string]string{"V": "\xfd"}, Env: map[string]string{"V": "\xfd"},
Sequence: 2, Sequence: 2,
}, }).NewError(EDuplicateAdd, &Object{
State: StateNew,
DevPath: "\x00",
Env: map[string]string{"V": "\xff"},
}),
}}, }},
{"remove", []*uevent.Message{ {"remove", []*uevent.Message{
@@ -109,16 +113,21 @@ func TestConsume(t *testing.T) {
"SEQNUM=3", "SEQNUM=3",
}}, }},
}, map[string]*Object{}, nil, []error{ }, map[string]*Object{}, nil, []error{
TargetError{ (&Event{
Action: uevent.KOBJ_REMOVE, Action: uevent.KOBJ_REMOVE,
DevPath: "\x00", DevPath: "\x00",
Env: map[string]string{}, Env: map[string]string{},
}, }).NewError(EBadTarget, nil),
RemoveStateError{ (&Event{
Action: uevent.KOBJ_REMOVE,
DevPath: "\x00",
Env: map[string]string{},
Sequence: 3,
}).NewError(ERemoveState, &Object{
State: StateBound, State: StateBound,
DevPath: "\x00", DevPath: "\x00",
Driver: "\xfd", Driver: "\xfd",
}, }),
}}, }},
{"change", []*uevent.Message{ {"change", []*uevent.Message{
@@ -133,14 +142,14 @@ func TestConsume(t *testing.T) {
Env: map[string]string{"V": "\xff"}, Env: map[string]string{"V": "\xff"},
}, },
}, nil, []error{ }, nil, []error{
TargetError{ (&Event{
Action: uevent.KOBJ_CHANGE, Action: uevent.KOBJ_CHANGE,
DevPath: "\x00", DevPath: "\x00",
Sequence: 9, Sequence: 9,
Env: map[string]string{ Env: map[string]string{
"V": "\xff", "V": "\xff",
}, },
}, }).NewError(EBadTarget, nil),
}}, }},
{"move", []*uevent.Message{ {"move", []*uevent.Message{
@@ -157,14 +166,14 @@ func TestConsume(t *testing.T) {
"\x0f": {DevPath: "\x0f", Env: map[string]string{"V": "\xfc"}}, "\x0f": {DevPath: "\x0f", Env: map[string]string{"V": "\xfc"}},
"\x0e": {DevPath: "\x0e", Env: map[string]string{"V": "\xfd"}}, "\x0e": {DevPath: "\x0e", Env: map[string]string{"V": "\xfd"}},
}, nil, []error{ }, nil, []error{
MalformedMoveError{ (&Event{
Action: uevent.KOBJ_MOVE, Action: uevent.KOBJ_MOVE,
DevPath: "\x0f", DevPath: "\x0f",
Env: map[string]string{ Env: map[string]string{
"V": "\xfc", "V": "\xfc",
}, },
}, }).NewError(EMalformedMove, nil),
TargetError{ (&Event{
Action: uevent.KOBJ_MOVE, Action: uevent.KOBJ_MOVE,
DevPath: "\x0e", DevPath: "\x0e",
Env: map[string]string{ Env: map[string]string{
@@ -172,7 +181,7 @@ func TestConsume(t *testing.T) {
"DEVPATH_OLD": "\xff", "DEVPATH_OLD": "\xff",
}, },
Sequence: 1, Sequence: 1,
}, }).NewError(EBadTarget, nil),
}}, }},
{"offline", []*uevent.Message{ {"offline", []*uevent.Message{
@@ -203,31 +212,43 @@ func TestConsume(t *testing.T) {
Env: map[string]string{"V": "\xf0"}, Env: map[string]string{"V": "\xf0"},
}, },
}, nil, []error{ }, nil, []error{
TargetError{ (&Event{
Action: uevent.KOBJ_ONLINE, Action: uevent.KOBJ_ONLINE,
DevPath: "\xfd", DevPath: "\xfd",
Env: map[string]string{ Env: map[string]string{
"V": "\xff", "V": "\xff",
}, },
Sequence: 9, Sequence: 9,
}, }).NewError(EBadTarget, nil),
UnexpectedOfflineError{ (&Event{
Action: uevent.KOBJ_ONLINE,
DevPath: "\xfd", DevPath: "\xfd",
Env: map[string]string{ Env: map[string]string{
"V": "\xff", "V": "\xff",
}, },
Sequence: 9,
}).NewError(EUnexpectedOffline, &Object{
DevPath: "\xfd",
Env: map[string]string{
"V": "\xff",
}, },
UnexpectedOfflineError{ }),
(&Event{
Action: uevent.KOBJ_OFFLINE,
DevPath: "\xfd",
Env: map[string]string{},
Sequence: 11,
}).NewError(EUnexpectedOffline, &Object{
Offline: true, Offline: true,
DevPath: "\xfd", DevPath: "\xfd",
Env: map[string]string{"V": "\xff"}, Env: map[string]string{"V": "\xff"},
}, }),
TargetError{ (&Event{
Action: uevent.KOBJ_OFFLINE, Action: uevent.KOBJ_OFFLINE,
DevPath: "\xfc", DevPath: "\xfc",
Env: map[string]string{"V": "\xf0"}, Env: map[string]string{"V": "\xf0"},
Sequence: 12, Sequence: 12,
}, }).NewError(EBadTarget, nil),
}}, }},
{"bind", []*uevent.Message{ {"bind", []*uevent.Message{
@@ -272,22 +293,32 @@ func TestConsume(t *testing.T) {
"V": "\xf0", "V": "\xf0",
}, },
}}, nil, []error{ }}, nil, []error{
UnbindStateError{ (&Event{
Action: uevent.KOBJ_UNBIND,
DevPath: "\x00",
Env: map[string]string{"_V": "\xfd"},
Sequence: 2,
}).NewError(EUnbindState, &Object{
State: StateNew, State: StateNew,
DevPath: "\x00", DevPath: "\x00",
Env: map[string]string{"V": "\xff"}, Env: map[string]string{"V": "\xff"},
}, }),
BindStateError{ (&Event{
Action: uevent.KOBJ_BIND,
DevPath: "\x00",
Env: map[string]string{"___V": "\xfb"},
Sequence: 4,
}).NewError(EBindState, &Object{
State: StateBound, State: StateBound,
DevPath: "\x00", DevPath: "\x00",
Env: map[string]string{"V": "\xff", "__V": "\xfc"}, Env: map[string]string{"V": "\xff", "__V": "\xfc"},
}, }),
TargetError{ (&Event{
Action: uevent.KOBJ_BIND, Action: uevent.KOBJ_BIND,
DevPath: "\f", DevPath: "\f",
Env: map[string]string{"V": "\xf0", "DRIVER": "\x01"}, Env: map[string]string{"V": "\xf0", "DRIVER": "\x01"},
Sequence: 5, Sequence: 5,
}, }).NewError(EBadTarget, nil),
}}, }},
{"unbind", []*uevent.Message{ {"unbind", []*uevent.Message{
@@ -295,12 +326,12 @@ func TestConsume(t *testing.T) {
"SEQNUM=9", "SEQNUM=9",
}}, }},
}, map[string]*Object{}, nil, []error{ }, map[string]*Object{}, nil, []error{
TargetError{ (&Event{
Action: uevent.KOBJ_UNBIND, Action: uevent.KOBJ_UNBIND,
DevPath: "\xfd", DevPath: "\xfd",
Env: map[string]string{}, Env: map[string]string{},
Sequence: 9, Sequence: 9,
}, }).NewError(EBadTarget, nil),
}}, }},
} }
for _, tc := range testCases { for _, tc := range testCases {
@@ -375,7 +406,7 @@ func TestIter(t *testing.T) {
var done bool var done bool
wg.Go(func() { wg.Go(func() {
s.Range(ctx, func(o *Object) bool { s.Range(ctx, func(o *Object) bool {
got = append(got, new(o.Clone())) got = append(got, o.Clone())
return !done return !done
}) })
}) })
@@ -435,46 +466,75 @@ func TestErrors(t *testing.T) {
Want: 0xbabe, Want: 0xbabe,
}, "SEQNUM=51966, want 47806"}, }, "SEQNUM=51966, want 47806"},
{"UnexpectedColdbootError", UnexpectedColdbootError{ {"EUnexpectedColdboot", (&Event{
Action: 0xbeef, Action: 0xbeef,
}, "unexpected unsupported kobject_action 48879 coldboot event"}, }).NewError(EUnexpectedColdboot, nil),
"unexpected unsupported kobject_action 48879 coldboot event"},
{"DuplicateAddError", DuplicateAddError{ {"EDuplicateAdd", (&Event{
Action: uevent.KOBJ_ADD, Action: uevent.KOBJ_ADD,
DevPath: "\x00", DevPath: "\x00",
}, `duplicate add event on devpath "\x00"`}, }).NewError(EDuplicateAdd, nil),
`duplicate add event on devpath "\x00"`},
{"TargetError", TargetError{ {"EBadTarget", (&Event{
Action: uevent.KOBJ_UNBIND, Action: uevent.KOBJ_UNBIND,
DevPath: "\x00", DevPath: "\x00",
}, `unexpected unbind event on devpath "\x00"`}, }).NewError(EBadTarget, nil),
`unexpected unbind event on devpath "\x00"`},
{"RemoveStateError", RemoveStateError{ {"ERemoveState", (&Event{
State: StateBound,
DevPath: "\x00", DevPath: "\x00",
}, `remove event targeting devpath "\x00" in state 2`}, }).NewError(ERemoveState, &Object{
{"BindStateError", BindStateError{
State: StateBound, State: StateBound,
}), `remove event targeting devpath "\x00" in state 2`},
{"ERemoveState invalid", (&Event{
DevPath: "\x00", DevPath: "\x00",
}, `bind event targeting devpath "\x00" in state 2`}, }).NewError(ERemoveState, nil),
"invalid remove event error"},
{"UnbindStateError", UnbindStateError{ {"EBindState", (&Event{
DevPath: "\x00",
}).NewError(EBindState, &Object{
State: StateBound,
}), `bind event targeting devpath "\x00" in state 2`},
{"EBindState invalid", (&Event{
DevPath: "\x00",
}).NewError(EBindState, nil),
"invalid bind state error"},
{"EUnbindState", (&Event{
DevPath: "\x00",
}).NewError(EUnbindState, &Object{
State: StateNew, State: StateNew,
}), `unbind event targeting devpath "\x00" in state 1`},
{"EUnbindState invalid", (&Event{
DevPath: "\x00", DevPath: "\x00",
}, `unbind event targeting devpath "\x00" in state 1`}, }).NewError(EUnbindState, nil),
"invalid unbind state error"},
{"MalformedMoveError", MalformedMoveError{ {"EMalformedMove", (&Event{
DevPath: "\x00", DevPath: "\x00",
}, `move event targeting devpath "\x00" missing DEVPATH_OLD`}, }).NewError(EMalformedMove, nil),
`move event targeting devpath "\x00" missing DEVPATH_OLD`},
{"UnexpectedOfflineError", UnexpectedOfflineError{ {"EUnexpectedOffline", (&Event{
DevPath: "\x00", DevPath: "\x00",
}, `online event targeting devpath "\x00"`}, }).NewError(EUnexpectedOffline, &Object{
{"UnexpectedOfflineError offline", UnexpectedOfflineError{ Offline: false,
}), `online event targeting devpath "\x00"`},
{"EUnexpectedOffline offline", (&Event{
DevPath: "\x00", DevPath: "\x00",
}).NewError(EUnexpectedOffline, &Object{
Offline: true, Offline: true,
}, `offline event targeting devpath "\x00"`}, }), `offline event targeting devpath "\x00"`},
{"EUnexpectedOffline invalid", (&Event{
DevPath: "\x00",
}).NewError(EUnexpectedOffline, nil),
"invalid unexpected offline error"},
{"EventError invalid", &EventError{Kind: 0xbad},
"invalid event error kind 2989"},
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
@@ -1648,13 +1708,13 @@ func TestConsumeSample(t *testing.T) {
delete(want, "/devices/virtual/net/lo") delete(want, "/devices/virtual/net/lo")
o.DevPath = "/devices/virtual/net/_lo" o.DevPath = "/devices/virtual/net/_lo"
o.Env["INTERFACE"] = "_lo" o.Env["INTERFACE"] = "_lo"
want["/devices/virtual/net/_lo"] = &o want["/devices/virtual/net/_lo"] = o
}, nil}, }, nil},
{"cpu", testdata02, func(want map[string]*Object) { {"cpu", testdata02, func(want map[string]*Object) {
o := want["/devices/system/machinecheck/machinecheck0"].Clone() o := want["/devices/system/machinecheck/machinecheck0"].Clone()
o.State = StateNew o.State = StateNew
want["/devices/system/machinecheck/machinecheck0"] = &o want["/devices/system/machinecheck/machinecheck0"] = o
}, nil}, }, nil},
{"loop", testdata03, func(want map[string]*Object) { {"loop", testdata03, func(want map[string]*Object) {

View File

@@ -11,8 +11,10 @@ import (
"unsafe" "unsafe"
"hakurei.app/check" "hakurei.app/check"
"hakurei.app/internal/kobject"
"hakurei.app/internal/report" "hakurei.app/internal/report"
"hakurei.app/internal/stub" "hakurei.app/internal/stub"
"hakurei.app/internal/uevent"
) )
type representableUE struct{ stub.UniqueError } type representableUE struct{ stub.UniqueError }
@@ -38,10 +40,25 @@ func TestDispatch(t *testing.T) {
Message: "rejecting coldboot loop", Message: "rejecting coldboot loop",
Err: stub.UniqueError(0xcafe), Err: stub.UniqueError(0xcafe),
}, },
}, true, []string{ {
`{"kind":"fatal","message":"rejecting coldboot loop","error":"unique error 51966 injected by the test suite"} Severity: report.Inconsistent,
`, Message: "processed inconsistent uevent",
}, "dispatch: rejecting coldboot loop: unique error 51966 injected by the test suite\n", nil}, Err: (&kobject.Event{
Action: uevent.KOBJ_ADD,
DevPath: "\x00",
Env: map[string]string{"V": "\xfd"},
Sequence: 2,
}).NewError(kobject.EDuplicateAdd, &kobject.Object{
State: kobject.StateNew,
DevPath: "\x00",
Env: map[string]string{"V": "\xff"},
}),
},
}, true, []string{`{"kind":"fatal","message":"rejecting coldboot loop","error":"unique error 51966 injected by the test suite"}
`, `{"kind":"inconsistent","message":"processed inconsistent uevent","error":{"fault":1,"event":{"action":"add","devpath":"\u0000","env":{"V":"\ufffd"},"seqnum":2,"subsystem":""},"object":{"state":1,"devpath":"\u0000","subsystem":"","env":{"V":"\ufffd"}}}}
`}, `dispatch: rejecting coldboot loop: unique error 51966 injected by the test suite
dispatch: processed inconsistent uevent: duplicate add event on devpath "\x00"
`, nil},
{"strict", report.DStrict, []report.Error{ {"strict", report.DStrict, []report.Error{
{ {