Skip to content

Commit

Permalink
feat: easy parsing of PropertiesChanged signals
Browse files Browse the repository at this point in the history
Introducing a new type PropertiesChanged that conforms to the
PropertiesChanged signal body fields. The Signal type will now
have a func that parsed the Signal body and return the new type.
Included some supporting functionality for PropertiesChanged to
check for and get changed properties.

Addresses godbus#201
  • Loading branch information
Damian Myers committed Jun 17, 2023
1 parent 7623695 commit 4d61938
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 0 deletions.
67 changes: 67 additions & 0 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,73 @@ type Signal struct {
Sequence Sequence
}

// PropertiesChanged is a representation of a signal parsed from
// org.freedesktop.DBus.Properties.PropertiesChanged
type PropertiesChanged struct {
// STRING interface_name
Iface string
// ARRAY of DICT_ENTRY<STRING,VARIANT> changed_properties
Changes map[string]Variant
// ARRAY<STRING> invalidated_properties
InvalidatedProperties []string
}

// Uses a received signal to parse PropertiesChanged. Based off:
//
// org.freedesktop.DBus.Properties.PropertiesChanged (STRING interface_name,
// ARRAY of DICT_ENTRY<STRING,VARIANT> changed_properties,
// ARRAY<STRING> invalidated_properties);
func (s *Signal) ParsePropertiesChanged() (pc PropertiesChanged, err error) {
pc = PropertiesChanged{}

if len(s.Body) != 3 {
err = errors.New("dbus: invalid body length for PropertiesChanged")
return
}

var ok bool
pc.Iface, ok = s.Body[0].(string)
if !ok {
err = errors.New("dbus: unable to parse interface_name from PropertiesChanged signal")
return
}

pc.Changes, ok = s.Body[1].(map[string]Variant)
if !ok {
err = errors.New("dbus: unable to parse changed_properties from PropertiesChanged signal")
return
}

pc.InvalidatedProperties, ok = s.Body[2].([]string)
if !ok {
err = errors.New("dbus: unable to parse invalidated_properties from PropertiesChanged signal")
return
}

return
}

// Checks the changed_properties map for a given property, if it is present we know it
// has changed and will return true.
func (pc *PropertiesChanged) IsPropertyChanged(property string) bool {
_, changed := pc.Changes[property]
return changed
}

// Returns the new value for a given property provided by changed_properties. If the property
// is not found, returns false.
func (pc *PropertiesChanged) GetChangedProperty(property string) (val interface{}, ok bool) {
var variant Variant
variant, ok = pc.Changes[property]
if !ok {
val = nil
return
}

val = variant.value
return
}

// transport is a D-Bus transport.
type transport interface {
// Read and Write raw data (for example, for the authentication protocol).
Expand Down
139 changes: 139 additions & 0 deletions conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -826,3 +826,142 @@ func TestTimeoutContextClosesConnection(t *testing.T) {
t.Errorf("expected connection to be closed, but got: %v", err)
}
}

var (
// some test vars we'll use to simplify result validation in PropertiesChanged testing
pciface = "totally.real.interface"
changedprop = "iChanged"
changedval = "12345"
changes = map[string]Variant{
changedprop: Variant{value: changedval},
}
invalidated = []string{"imInvalidNow"}
)

func TestParsePropertiesChanged(t *testing.T) {
// Case 1 - Expected pass, coverage on parsing accuracy

testSignal := Signal{
Body: []interface{}{
// all three are here and happy
pciface,
changes,
invalidated,
},
}

got, err := testSignal.ParsePropertiesChanged()
if err != nil {
t.Fatalf("case 1 err - %v", err)
}

if got.Iface != pciface {
t.Fatalf("case 1 - iface mismatch expected %s got %s", pciface, got.Iface)
}

if v, found := got.GetChangedProperty(changedprop); !found || v != changedval {
t.Fatalf("case 1 - changed prop mismatch expected %v got %v", changes, got.Changes)
}

if len(got.InvalidatedProperties) != 1 || got.InvalidatedProperties[0] != "imInvalidNow" {
t.Fatalf("case 1 - invalidated props mismatch expected %v got %v", invalidated, got.InvalidatedProperties)
}

// Case 2 - Expected fail, coverage on Body length validation

testSignal = Signal{
Body: []interface{}{
// only have one entry in the body, oh no!
invalidated,
},
}

_, err = testSignal.ParsePropertiesChanged()
if err == nil {
t.Fatal("case 2 - expected err but err is nil")
}

// Case 3 - Expected fail, coverage on the parsing of interface name

testSignal = Signal{
Body: []interface{}{
// all three are present but iface is the wrong type
changes,
changes,
invalidated,
},
}

_, err = testSignal.ParsePropertiesChanged()
if err == nil {
t.Fatal("case 3 - expected err but err is nil")
}

// Case 4 - Expected fail, coverage on the properties_changed parsing

testSignal = Signal{
Body: []interface{}{
// all three are present but changes is the wrong type
pciface,
pciface,
invalidated,
},
}

_, err = testSignal.ParsePropertiesChanged()
if err == nil {
t.Fatal("case 4 - expected err but err is nil")
}

// Case 5 - Expected fail, coverage on the the invalidated_properties parsing

testSignal = Signal{
Body: []interface{}{
// all three are present but invalidated props are the wrong type
pciface,
changes,
changes,
},
}

_, err = testSignal.ParsePropertiesChanged()
if err == nil {
t.Fatal("case 5 - expected err but err is nil")
}
}

func TestIsPropertyChanged(t *testing.T) {
testPC := PropertiesChanged{
Changes: changes,
}

// Case 1 - Expected pass, changed property found

if !testPC.IsPropertyChanged(changedprop) {
t.Fatal("case 1 - IsPropertyChanged mismatch")
}

// Case 2 - Expected fail, changed property not found

if testPC.IsPropertyChanged("did I change? no") {
t.Fatal("case 2 - IsPropertyChanged mismatch")
}
}

func TestGetChangedProperty(t *testing.T) {
testPC := PropertiesChanged{
Changes: changes,
}

// Case 1 - Expected pass, able to find property and we get the correct value

if val, found := testPC.GetChangedProperty(changedprop); !found || val != changedval {
t.Fatalf("case 1 - GetChangedProperty expected %s:true got %v:%v", changedval, val, found)
}

// Case 2 - expected fail, unable to find property

if _, found := testPC.GetChangedProperty("If you read this, hello"); found {
t.Fatal("case 2 - GetChangedProperty found property when property shouldn't exist")
}
}

0 comments on commit 4d61938

Please sign in to comment.