Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: easy parsing of PropertiesChanged signals #371

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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")
}
}