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
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:
@@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TargetError is reported by [State.Consume] for an event on a nonexistent
|
const (
|
||||||
// entry. This is generally only possible before coldboot completes.
|
// EUnexpectedColdboot is reported for a coldboot event with action other
|
||||||
type TargetError Event
|
// 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 TargetError) Error() string {
|
func (e EventError) Error() string {
|
||||||
return "unexpected " + e.Action.String() +
|
switch e.Kind {
|
||||||
" event on devpath " + strconv.Quote(e.DevPath)
|
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) +
|
||||||
|
" in state " + strconv.Itoa(e.O.State)
|
||||||
|
case EUnexpectedOffline:
|
||||||
|
if e.O == nil {
|
||||||
|
return "invalid unexpected offline error"
|
||||||
|
}
|
||||||
|
if e.O.Offline {
|
||||||
|
return "offline event targeting devpath " + strconv.Quote(e.E.DevPath)
|
||||||
|
}
|
||||||
|
return "online event targeting devpath " + strconv.Quote(e.E.DevPath)
|
||||||
|
case EBindState:
|
||||||
|
if e.O == nil {
|
||||||
|
return "invalid bind state error"
|
||||||
|
}
|
||||||
|
return "bind event targeting devpath " + strconv.Quote(e.E.DevPath) +
|
||||||
|
" in state " + strconv.Itoa(e.O.State)
|
||||||
|
case EUnbindState:
|
||||||
|
if e.O == nil {
|
||||||
|
return "invalid unbind state error"
|
||||||
|
}
|
||||||
|
return "unbind event targeting devpath " + strconv.Quote(e.E.DevPath) +
|
||||||
|
" in state " + strconv.Itoa(e.O.State)
|
||||||
|
case EMalformedMove:
|
||||||
|
return "move event targeting devpath " + strconv.Quote(e.E.DevPath) +
|
||||||
|
" missing DEVPATH_OLD"
|
||||||
|
|
||||||
// RemoveStateError is reported by [State.Consume] for a [uevent.KOBJ_REMOVE]
|
default:
|
||||||
// event targeting an entry in a state other than [StateColdboot] and [StateNew].
|
return "invalid event error kind " + strconv.Itoa(e.Kind)
|
||||||
type RemoveStateError Object
|
|
||||||
|
|
||||||
func (e RemoveStateError) Error() string {
|
|
||||||
return "remove event targeting devpath " + strconv.Quote(e.DevPath) +
|
|
||||||
" in state " + strconv.Itoa(e.State)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BindStateError is reported by [State.Consume] for a [uevent.KOBJ_BIND] event
|
|
||||||
// targeting an entry in a state other than [StateColdboot] and [StateNew].
|
|
||||||
type BindStateError Object
|
|
||||||
|
|
||||||
func (e BindStateError) Error() string {
|
|
||||||
return "bind event targeting devpath " + strconv.Quote(e.DevPath) +
|
|
||||||
" in state " + strconv.Itoa(e.State)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnbindStateError is reported by [State.Consume] for a [uevent.KOBJ_UNBIND]
|
|
||||||
// event targeting an entry in a state other than [StateBound].
|
|
||||||
type UnbindStateError Object
|
|
||||||
|
|
||||||
func (e UnbindStateError) Error() string {
|
|
||||||
return "unbind event targeting devpath " + strconv.Quote(e.DevPath) +
|
|
||||||
" in state " + strconv.Itoa(e.State)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MalformedMoveError is reported by [State.Consume] for a [uevent.KOBJ_MOVE]
|
|
||||||
// event missing the DEVPATH_OLD environment variable.
|
|
||||||
type MalformedMoveError Event
|
|
||||||
|
|
||||||
func (e MalformedMoveError) Error() string {
|
|
||||||
return "move event targeting devpath " + strconv.Quote(e.DevPath) +
|
|
||||||
" missing DEVPATH_OLD"
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnexpectedOfflineError is reported by [State.Consume] for a
|
|
||||||
// [uevent.KOBJ_OFFLINE] or [uevent.KOBJ_ONLINE] event targeting an already
|
|
||||||
// offline or online object.
|
|
||||||
type UnexpectedOfflineError Object
|
|
||||||
|
|
||||||
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)
|
}
|
||||||
|
|
||||||
|
// NewError returns a new [EventError] for e and o.
|
||||||
|
func (e *Event) NewError(kind int, o *Object) error {
|
||||||
|
if o != nil {
|
||||||
|
o = o.Clone()
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
UnexpectedOfflineError{
|
}).NewError(EUnexpectedOffline, &Object{
|
||||||
|
DevPath: "\xfd",
|
||||||
|
Env: map[string]string{
|
||||||
|
"V": "\xff",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
(&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{
|
||||||
|
State: StateBound,
|
||||||
|
}), `remove event targeting devpath "\x00" in state 2`},
|
||||||
|
{"ERemoveState invalid", (&Event{
|
||||||
|
DevPath: "\x00",
|
||||||
|
}).NewError(ERemoveState, nil),
|
||||||
|
"invalid remove event error"},
|
||||||
|
|
||||||
{"BindStateError", BindStateError{
|
{"EBindState", (&Event{
|
||||||
State: StateBound,
|
|
||||||
DevPath: "\x00",
|
DevPath: "\x00",
|
||||||
}, `bind event targeting devpath "\x00" in state 2`},
|
}).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"},
|
||||||
|
|
||||||
{"UnbindStateError", UnbindStateError{
|
{"EUnbindState", (&Event{
|
||||||
State: StateNew,
|
|
||||||
DevPath: "\x00",
|
DevPath: "\x00",
|
||||||
}, `unbind event targeting devpath "\x00" in state 1`},
|
}).NewError(EUnbindState, &Object{
|
||||||
|
State: StateNew,
|
||||||
|
}), `unbind event targeting devpath "\x00" in state 1`},
|
||||||
|
{"EUnbindState invalid", (&Event{
|
||||||
|
DevPath: "\x00",
|
||||||
|
}).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) {
|
||||||
|
|||||||
@@ -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{
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user