Merge pull request #408 from runcom/update-go-systemd
godeps: update go-systemd to v4 and godbus/dbus to v3
This commit is contained in:
commit
56d4e8eb3e
|
@ -14,13 +14,13 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-systemd/dbus",
|
||||
"Comment": "v3",
|
||||
"Rev": "be94bc700879ae8217780e9d141789a2defa302b"
|
||||
"Comment": "v4",
|
||||
"Rev": "b4a58d95188dd092ae20072bac14cece0e67c388"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-systemd/util",
|
||||
"Comment": "v3",
|
||||
"Rev": "be94bc700879ae8217780e9d141789a2defa302b"
|
||||
"Comment": "v4",
|
||||
"Rev": "b4a58d95188dd092ae20072bac14cece0e67c388"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/docker/pkg/mount",
|
||||
|
@ -44,8 +44,8 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/godbus/dbus",
|
||||
"Comment": "v2",
|
||||
"Rev": "88765d85c0fdadcd98a54e30694fa4e4f5b51133"
|
||||
"Comment": "v3",
|
||||
"Rev": "c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -64,11 +64,11 @@ func PathBusEscape(path string) string {
|
|||
type Conn struct {
|
||||
// sysconn/sysobj are only used to call dbus methods
|
||||
sysconn *dbus.Conn
|
||||
sysobj *dbus.Object
|
||||
sysobj dbus.BusObject
|
||||
|
||||
// sigconn/sigobj are only used to receive dbus signals
|
||||
sigconn *dbus.Conn
|
||||
sigobj *dbus.Object
|
||||
sigobj dbus.BusObject
|
||||
|
||||
jobListener struct {
|
||||
jobs map[dbus.ObjectPath]chan<- string
|
||||
|
@ -86,14 +86,30 @@ type Conn struct {
|
|||
// New establishes a connection to the system bus and authenticates.
|
||||
// Callers should call Close() when done with the connection.
|
||||
func New() (*Conn, error) {
|
||||
return newConnection(dbus.SystemBusPrivate)
|
||||
return newConnection(func() (*dbus.Conn, error) {
|
||||
return dbusAuthHelloConnection(dbus.SystemBusPrivate)
|
||||
})
|
||||
}
|
||||
|
||||
// NewUserConnection establishes a connection to the session bus and
|
||||
// authenticates. This can be used to connect to systemd user instances.
|
||||
// Callers should call Close() when done with the connection.
|
||||
func NewUserConnection() (*Conn, error) {
|
||||
return newConnection(dbus.SessionBusPrivate)
|
||||
return newConnection(func() (*dbus.Conn, error) {
|
||||
return dbusAuthHelloConnection(dbus.SessionBusPrivate)
|
||||
})
|
||||
}
|
||||
|
||||
// NewSystemdConnection establishes a private, direct connection to systemd.
|
||||
// This can be used for communicating with systemd without a dbus daemon.
|
||||
// Callers should call Close() when done with the connection.
|
||||
func NewSystemdConnection() (*Conn, error) {
|
||||
return newConnection(func() (*dbus.Conn, error) {
|
||||
// We skip Hello when talking directly to systemd.
|
||||
return dbusAuthConnection(func() (*dbus.Conn, error) {
|
||||
return dbus.Dial("unix:path=/run/systemd/private")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Close closes an established connection
|
||||
|
@ -103,12 +119,12 @@ func (c *Conn) Close() {
|
|||
}
|
||||
|
||||
func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
|
||||
sysconn, err := dbusConnection(createBus)
|
||||
sysconn, err := createBus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sigconn, err := dbusConnection(createBus)
|
||||
sigconn, err := createBus()
|
||||
if err != nil {
|
||||
sysconn.Close()
|
||||
return nil, err
|
||||
|
@ -132,7 +148,7 @@ func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
|
|||
return c, nil
|
||||
}
|
||||
|
||||
func dbusConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
|
||||
func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
|
||||
conn, err := createBus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -149,8 +165,16 @@ func dbusConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = conn.Hello()
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
|
||||
conn, err := dbusAuthConnection(createBus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = conn.Hello(); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
@ -158,6 +182,6 @@ func dbusConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
|
|||
return conn, nil
|
||||
}
|
||||
|
||||
func systemdObject(conn *dbus.Conn) *dbus.Object {
|
||||
func systemdObject(conn *dbus.Conn) dbus.BusObject {
|
||||
return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
|
||||
}
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNeedsEscape(t *testing.T) {
|
||||
// Anything not 0-9a-zA-Z should always be escaped
|
||||
for want, vals := range map[bool][]byte{
|
||||
false: []byte{'a', 'b', 'z', 'A', 'Q', '1', '4', '9'},
|
||||
true: []byte{'#', '%', '$', '!', '.', '_', '-', '%', '\\'},
|
||||
} {
|
||||
for i := 1; i < 10; i++ {
|
||||
for _, b := range vals {
|
||||
got := needsEscape(i, b)
|
||||
if got != want {
|
||||
t.Errorf("needsEscape(%d, %c) returned %t, want %t", i, b, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0-9 in position 0 should be escaped
|
||||
for want, vals := range map[bool][]byte{
|
||||
false: []byte{'A', 'a', 'e', 'x', 'Q', 'Z'},
|
||||
true: []byte{'0', '4', '5', '9'},
|
||||
} {
|
||||
for _, b := range vals {
|
||||
got := needsEscape(0, b)
|
||||
if got != want {
|
||||
t.Errorf("needsEscape(0, %c) returned %t, want %t", b, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPathBusEscape(t *testing.T) {
|
||||
for in, want := range map[string]string{
|
||||
"": "_",
|
||||
"foo.service": "foo_2eservice",
|
||||
"foobar": "foobar",
|
||||
"woof@woof.service": "woof_40woof_2eservice",
|
||||
"0123456": "_30123456",
|
||||
"account_db.service": "account_5fdb_2eservice",
|
||||
"got-dashes": "got_2ddashes",
|
||||
} {
|
||||
got := PathBusEscape(in)
|
||||
if got != want {
|
||||
t.Errorf("bad result for PathBusEscape(%s): got %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestNew ensures that New() works without errors.
|
||||
func TestNew(t *testing.T) {
|
||||
_, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -1,345 +0,0 @@
|
|||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
func setupConn(t *testing.T) *Conn {
|
||||
conn, err := New()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func findFixture(target string, t *testing.T) string {
|
||||
abs, err := filepath.Abs("../fixtures/" + target)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return abs
|
||||
}
|
||||
|
||||
func setupUnit(target string, conn *Conn, t *testing.T) {
|
||||
// Blindly stop the unit in case it is running
|
||||
conn.StopUnit(target, "replace", nil)
|
||||
|
||||
// Blindly remove the symlink in case it exists
|
||||
targetRun := filepath.Join("/run/systemd/system/", target)
|
||||
os.Remove(targetRun)
|
||||
}
|
||||
|
||||
func linkUnit(target string, conn *Conn, t *testing.T) {
|
||||
abs := findFixture(target, t)
|
||||
fixture := []string{abs}
|
||||
|
||||
changes, err := conn.LinkUnitFiles(fixture, true, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(changes) < 1 {
|
||||
t.Fatalf("Expected one change, got %v", changes)
|
||||
}
|
||||
|
||||
runPath := filepath.Join("/run/systemd/system/", target)
|
||||
if changes[0].Filename != runPath {
|
||||
t.Fatal("Unexpected target filename")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that basic unit starting and stopping works.
|
||||
func TestStartStopUnit(t *testing.T) {
|
||||
target := "start-stop.service"
|
||||
conn := setupConn(t)
|
||||
|
||||
setupUnit(target, conn, t)
|
||||
linkUnit(target, conn, t)
|
||||
|
||||
// 2. Start the unit
|
||||
reschan := make(chan string)
|
||||
_, err := conn.StartUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
job := <-reschan
|
||||
if job != "done" {
|
||||
t.Fatal("Job is not done:", job)
|
||||
}
|
||||
|
||||
units, err := conn.ListUnits()
|
||||
|
||||
var unit *UnitStatus
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit == nil {
|
||||
t.Fatalf("Test unit not found in list")
|
||||
}
|
||||
|
||||
if unit.ActiveState != "active" {
|
||||
t.Fatalf("Test unit not active")
|
||||
}
|
||||
|
||||
// 3. Stop the unit
|
||||
_, err = conn.StopUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for StopUnit job to complete
|
||||
<-reschan
|
||||
|
||||
units, err = conn.ListUnits()
|
||||
|
||||
unit = nil
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit != nil {
|
||||
t.Fatalf("Test unit found in list, should be stopped")
|
||||
}
|
||||
}
|
||||
|
||||
// Enables a unit and then immediately tears it down
|
||||
func TestEnableDisableUnit(t *testing.T) {
|
||||
target := "enable-disable.service"
|
||||
conn := setupConn(t)
|
||||
|
||||
setupUnit(target, conn, t)
|
||||
abs := findFixture(target, t)
|
||||
runPath := filepath.Join("/run/systemd/system/", target)
|
||||
|
||||
// 1. Enable the unit
|
||||
install, changes, err := conn.EnableUnitFiles([]string{abs}, true, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if install != false {
|
||||
t.Fatal("Install was true")
|
||||
}
|
||||
|
||||
if len(changes) < 1 {
|
||||
t.Fatalf("Expected one change, got %v", changes)
|
||||
}
|
||||
|
||||
if changes[0].Filename != runPath {
|
||||
t.Fatal("Unexpected target filename")
|
||||
}
|
||||
|
||||
// 2. Disable the unit
|
||||
dChanges, err := conn.DisableUnitFiles([]string{abs}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(dChanges) != 1 {
|
||||
t.Fatalf("Changes should include the path, %v", dChanges)
|
||||
}
|
||||
if dChanges[0].Filename != runPath {
|
||||
t.Fatalf("Change should include correct filename, %+v", dChanges[0])
|
||||
}
|
||||
if dChanges[0].Destination != "" {
|
||||
t.Fatalf("Change destination should be empty, %+v", dChanges[0])
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetUnitProperties reads the `-.mount` which should exist on all systemd
|
||||
// systems and ensures that one of its properties is valid.
|
||||
func TestGetUnitProperties(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
unit := "-.mount"
|
||||
|
||||
info, err := conn.GetUnitProperties(unit)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
names := info["Wants"].([]string)
|
||||
|
||||
if len(names) < 1 {
|
||||
t.Fatal("/ is unwanted")
|
||||
}
|
||||
|
||||
if names[0] != "system.slice" {
|
||||
t.Fatal("unexpected wants for /")
|
||||
}
|
||||
|
||||
prop, err := conn.GetUnitProperty(unit, "Wants")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if prop.Name != "Wants" {
|
||||
t.Fatal("unexpected property name")
|
||||
}
|
||||
|
||||
val := prop.Value.Value().([]string)
|
||||
if !reflect.DeepEqual(val, names) {
|
||||
t.Fatal("unexpected property value")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetUnitPropertiesRejectsInvalidName attempts to get the properties for a
|
||||
// unit with an invalid name. This test should be run with --test.timeout set,
|
||||
// as a fail will manifest as GetUnitProperties hanging indefinitely.
|
||||
func TestGetUnitPropertiesRejectsInvalidName(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
unit := "//invalid#$^/"
|
||||
|
||||
_, err := conn.GetUnitProperties(unit)
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error, got nil")
|
||||
}
|
||||
|
||||
_, err = conn.GetUnitProperty(unit, "Wants")
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
// TestSetUnitProperties changes a cgroup setting on the `tmp.mount`
|
||||
// which should exist on all systemd systems and ensures that the
|
||||
// property was set.
|
||||
func TestSetUnitProperties(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
unit := "tmp.mount"
|
||||
|
||||
if err := conn.SetUnitProperties(unit, true, Property{"CPUShares", dbus.MakeVariant(uint64(1023))}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
info, err := conn.GetUnitTypeProperties(unit, "Mount")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
value := info["CPUShares"].(uint64)
|
||||
if value != 1023 {
|
||||
t.Fatal("CPUShares of unit is not 1023:", value)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that basic transient unit starting and stopping works.
|
||||
func TestStartStopTransientUnit(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
props := []Property{
|
||||
PropExecStart([]string{"/bin/sleep", "400"}, false),
|
||||
}
|
||||
target := fmt.Sprintf("testing-transient-%d.service", rand.Int())
|
||||
|
||||
// Start the unit
|
||||
reschan := make(chan string)
|
||||
_, err := conn.StartTransientUnit(target, "replace", props, reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
job := <-reschan
|
||||
if job != "done" {
|
||||
t.Fatal("Job is not done:", job)
|
||||
}
|
||||
|
||||
units, err := conn.ListUnits()
|
||||
|
||||
var unit *UnitStatus
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit == nil {
|
||||
t.Fatalf("Test unit not found in list")
|
||||
}
|
||||
|
||||
if unit.ActiveState != "active" {
|
||||
t.Fatalf("Test unit not active")
|
||||
}
|
||||
|
||||
// 3. Stop the unit
|
||||
_, err = conn.StopUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for StopUnit job to complete
|
||||
<-reschan
|
||||
|
||||
units, err = conn.ListUnits()
|
||||
|
||||
unit = nil
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit != nil {
|
||||
t.Fatalf("Test unit found in list, should be stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnJobListener(t *testing.T) {
|
||||
target := "start-stop.service"
|
||||
conn := setupConn(t)
|
||||
|
||||
setupUnit(target, conn, t)
|
||||
linkUnit(target, conn, t)
|
||||
|
||||
jobSize := len(conn.jobListener.jobs)
|
||||
|
||||
reschan := make(chan string)
|
||||
_, err := conn.StartUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
<-reschan
|
||||
|
||||
_, err = conn.StopUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
<-reschan
|
||||
|
||||
currentJobSize := len(conn.jobListener.jobs)
|
||||
if jobSize != currentJobSize {
|
||||
t.Fatal("JobListener jobs leaked")
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestBasicSetActions asserts that Add & Remove behavior is correct
|
||||
func TestBasicSetActions(t *testing.T) {
|
||||
s := newSet()
|
||||
|
||||
if s.Contains("foo") {
|
||||
t.Fatal("set should not contain 'foo'")
|
||||
}
|
||||
|
||||
s.Add("foo")
|
||||
|
||||
if !s.Contains("foo") {
|
||||
t.Fatal("set should contain 'foo'")
|
||||
}
|
||||
|
||||
v := s.Values()
|
||||
if len(v) != 1 {
|
||||
t.Fatal("set.Values did not report correct number of values")
|
||||
}
|
||||
if v[0] != "foo" {
|
||||
t.Fatal("set.Values did not report value")
|
||||
}
|
||||
|
||||
s.Remove("foo")
|
||||
|
||||
if s.Contains("foo") {
|
||||
t.Fatal("set should not contain 'foo'")
|
||||
}
|
||||
|
||||
v = s.Values()
|
||||
if len(v) != 0 {
|
||||
t.Fatal("set.Values did not report correct number of values")
|
||||
}
|
||||
}
|
82
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go
generated
vendored
82
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go
generated
vendored
|
@ -1,82 +0,0 @@
|
|||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestSubscribeUnit exercises the basics of subscription of a particular unit.
|
||||
func TestSubscriptionSetUnit(t *testing.T) {
|
||||
target := "subscribe-events-set.service"
|
||||
|
||||
conn, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
subSet := conn.NewSubscriptionSet()
|
||||
evChan, errChan := subSet.Subscribe()
|
||||
|
||||
subSet.Add(target)
|
||||
setupUnit(target, conn, t)
|
||||
linkUnit(target, conn, t)
|
||||
|
||||
reschan := make(chan string)
|
||||
_, err = conn.StartUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
job := <-reschan
|
||||
if job != "done" {
|
||||
t.Fatal("Couldn't start", target)
|
||||
}
|
||||
|
||||
timeout := make(chan bool, 1)
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
close(timeout)
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case changes := <-evChan:
|
||||
tCh, ok := changes[target]
|
||||
|
||||
if !ok {
|
||||
t.Fatal("Unexpected event:", changes)
|
||||
}
|
||||
|
||||
if tCh.ActiveState == "active" && tCh.Name == target {
|
||||
goto success
|
||||
}
|
||||
case err = <-errChan:
|
||||
t.Fatal(err)
|
||||
case <-timeout:
|
||||
t.Fatal("Reached timeout")
|
||||
}
|
||||
}
|
||||
|
||||
success:
|
||||
return
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestSubscribe exercises the basics of subscription
|
||||
func TestSubscribe(t *testing.T) {
|
||||
conn, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Unsubscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSubscribeUnit exercises the basics of subscription of a particular unit.
|
||||
func TestSubscribeUnit(t *testing.T) {
|
||||
target := "subscribe-events.service"
|
||||
|
||||
conn, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Unsubscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
evChan, errChan := conn.SubscribeUnits(time.Second)
|
||||
|
||||
setupUnit(target, conn, t)
|
||||
linkUnit(target, conn, t)
|
||||
|
||||
reschan := make(chan string)
|
||||
_, err = conn.StartUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
job := <-reschan
|
||||
if job != "done" {
|
||||
t.Fatal("Couldn't start", target)
|
||||
}
|
||||
|
||||
timeout := make(chan bool, 1)
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
close(timeout)
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case changes := <-evChan:
|
||||
tCh, ok := changes[target]
|
||||
|
||||
// Just continue until we see our event.
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if tCh.ActiveState == "active" && tCh.Name == target {
|
||||
goto success
|
||||
}
|
||||
case err = <-errChan:
|
||||
t.Fatal(err)
|
||||
case <-timeout:
|
||||
t.Fatal("Reached timeout")
|
||||
}
|
||||
}
|
||||
|
||||
success:
|
||||
return
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>)
|
||||
Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>), Google
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -27,6 +27,9 @@ The complete package documentation and some simple examples are available at
|
|||
[_examples](https://github.com/godbus/dbus/tree/master/_examples) directory
|
||||
gives a short overview over the basic usage.
|
||||
|
||||
#### Projects using godbus
|
||||
- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
|
||||
|
||||
Please note that the API is considered unstable for now and may change without
|
||||
further notice.
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package dbus
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Call represents a pending or completed method call.
|
||||
|
@ -35,113 +34,3 @@ func (c *Call) Store(retvalues ...interface{}) error {
|
|||
|
||||
return Store(c.Body, retvalues...)
|
||||
}
|
||||
|
||||
// Object represents a remote object on which methods can be invoked.
|
||||
type Object struct {
|
||||
conn *Conn
|
||||
dest string
|
||||
path ObjectPath
|
||||
}
|
||||
|
||||
// Call calls a method with (*Object).Go and waits for its reply.
|
||||
func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
|
||||
return <-o.Go(method, flags, make(chan *Call, 1), args...).Done
|
||||
}
|
||||
|
||||
// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
|
||||
// object. The property name must be given in interface.member notation.
|
||||
func (o *Object) GetProperty(p string) (Variant, error) {
|
||||
idx := strings.LastIndex(p, ".")
|
||||
if idx == -1 || idx+1 == len(p) {
|
||||
return Variant{}, errors.New("dbus: invalid property " + p)
|
||||
}
|
||||
|
||||
iface := p[:idx]
|
||||
prop := p[idx+1:]
|
||||
|
||||
result := Variant{}
|
||||
err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
|
||||
|
||||
if err != nil {
|
||||
return Variant{}, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Go calls a method with the given arguments asynchronously. It returns a
|
||||
// Call structure representing this method call. The passed channel will
|
||||
// return the same value once the call is done. If ch is nil, a new channel
|
||||
// will be allocated. Otherwise, ch has to be buffered or Go will panic.
|
||||
//
|
||||
// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
|
||||
// is returned of which only the Err member is valid.
|
||||
//
|
||||
// If the method parameter contains a dot ('.'), the part before the last dot
|
||||
// specifies the interface on which the method is called.
|
||||
func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
|
||||
iface := ""
|
||||
i := strings.LastIndex(method, ".")
|
||||
if i != -1 {
|
||||
iface = method[:i]
|
||||
}
|
||||
method = method[i+1:]
|
||||
msg := new(Message)
|
||||
msg.Type = TypeMethodCall
|
||||
msg.serial = o.conn.getSerial()
|
||||
msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
msg.Headers[FieldPath] = MakeVariant(o.path)
|
||||
msg.Headers[FieldDestination] = MakeVariant(o.dest)
|
||||
msg.Headers[FieldMember] = MakeVariant(method)
|
||||
if iface != "" {
|
||||
msg.Headers[FieldInterface] = MakeVariant(iface)
|
||||
}
|
||||
msg.Body = args
|
||||
if len(args) > 0 {
|
||||
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
|
||||
}
|
||||
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||
if ch == nil {
|
||||
ch = make(chan *Call, 10)
|
||||
} else if cap(ch) == 0 {
|
||||
panic("dbus: unbuffered channel passed to (*Object).Go")
|
||||
}
|
||||
call := &Call{
|
||||
Destination: o.dest,
|
||||
Path: o.path,
|
||||
Method: method,
|
||||
Args: args,
|
||||
Done: ch,
|
||||
}
|
||||
o.conn.callsLck.Lock()
|
||||
o.conn.calls[msg.serial] = call
|
||||
o.conn.callsLck.Unlock()
|
||||
o.conn.outLck.RLock()
|
||||
if o.conn.closed {
|
||||
call.Err = ErrClosed
|
||||
call.Done <- call
|
||||
} else {
|
||||
o.conn.out <- msg
|
||||
}
|
||||
o.conn.outLck.RUnlock()
|
||||
return call
|
||||
}
|
||||
o.conn.outLck.RLock()
|
||||
defer o.conn.outLck.RUnlock()
|
||||
if o.conn.closed {
|
||||
return &Call{Err: ErrClosed}
|
||||
}
|
||||
o.conn.out <- msg
|
||||
return &Call{Err: nil}
|
||||
}
|
||||
|
||||
// Destination returns the destination that calls on o are sent to.
|
||||
func (o *Object) Destination() string {
|
||||
return o.dest
|
||||
}
|
||||
|
||||
// Path returns the path that calls on o are sent to.
|
||||
func (o *Object) Path() ObjectPath {
|
||||
return o.path
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ var ErrClosed = errors.New("dbus: connection closed by user")
|
|||
type Conn struct {
|
||||
transport
|
||||
|
||||
busObj *Object
|
||||
busObj BusObject
|
||||
unixFD bool
|
||||
uuid string
|
||||
|
||||
|
@ -46,7 +46,7 @@ type Conn struct {
|
|||
calls map[uint32]*Call
|
||||
callsLck sync.RWMutex
|
||||
|
||||
handlers map[ObjectPath]map[string]interface{}
|
||||
handlers map[ObjectPath]map[string]exportWithMapping
|
||||
handlersLck sync.RWMutex
|
||||
|
||||
out chan *Message
|
||||
|
@ -157,7 +157,7 @@ func newConn(tr transport) (*Conn, error) {
|
|||
conn.transport = tr
|
||||
conn.calls = make(map[uint32]*Call)
|
||||
conn.out = make(chan *Message, 10)
|
||||
conn.handlers = make(map[ObjectPath]map[string]interface{})
|
||||
conn.handlers = make(map[ObjectPath]map[string]exportWithMapping)
|
||||
conn.nextSerial = 1
|
||||
conn.serialUsed = map[uint32]bool{0: true}
|
||||
conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
|
||||
|
@ -166,7 +166,7 @@ func newConn(tr transport) (*Conn, error) {
|
|||
|
||||
// BusObject returns the object owned by the bus daemon which handles
|
||||
// administrative requests.
|
||||
func (conn *Conn) BusObject() *Object {
|
||||
func (conn *Conn) BusObject() BusObject {
|
||||
return conn.busObj
|
||||
}
|
||||
|
||||
|
@ -303,18 +303,33 @@ func (conn *Conn) inWorker() {
|
|||
// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
|
||||
// sender is optional for signals.
|
||||
sender, _ := msg.Headers[FieldSender].value.(string)
|
||||
if iface == "org.freedesktop.DBus" && member == "NameLost" &&
|
||||
sender == "org.freedesktop.DBus" {
|
||||
|
||||
name, _ := msg.Body[0].(string)
|
||||
conn.namesLck.Lock()
|
||||
for i, v := range conn.names {
|
||||
if v == name {
|
||||
copy(conn.names[i:], conn.names[i+1:])
|
||||
conn.names = conn.names[:len(conn.names)-1]
|
||||
if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" {
|
||||
if member == "NameLost" {
|
||||
// If we lost the name on the bus, remove it from our
|
||||
// tracking list.
|
||||
name, ok := msg.Body[0].(string)
|
||||
if !ok {
|
||||
panic("Unable to read the lost name")
|
||||
}
|
||||
conn.namesLck.Lock()
|
||||
for i, v := range conn.names {
|
||||
if v == name {
|
||||
conn.names = append(conn.names[:i],
|
||||
conn.names[i+1:]...)
|
||||
}
|
||||
}
|
||||
conn.namesLck.Unlock()
|
||||
} else if member == "NameAcquired" {
|
||||
// If we acquired the name on the bus, add it to our
|
||||
// tracking list.
|
||||
name, ok := msg.Body[0].(string)
|
||||
if !ok {
|
||||
panic("Unable to read the acquired name")
|
||||
}
|
||||
conn.namesLck.Lock()
|
||||
conn.names = append(conn.names, name)
|
||||
conn.namesLck.Unlock()
|
||||
}
|
||||
conn.namesLck.Unlock()
|
||||
}
|
||||
signal := &Signal{
|
||||
Sender: sender,
|
||||
|
@ -360,7 +375,7 @@ func (conn *Conn) Names() []string {
|
|||
}
|
||||
|
||||
// Object returns the object identified by the given destination name and path.
|
||||
func (conn *Conn) Object(dest string, path ObjectPath) *Object {
|
||||
func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
|
||||
return &Object{conn, dest, path}
|
||||
}
|
||||
|
||||
|
@ -554,7 +569,7 @@ type transport interface {
|
|||
}
|
||||
|
||||
var (
|
||||
transports map[string]func(string) (transport, error) = make(map[string]func(string) (transport, error))
|
||||
transports = make(map[string]func(string) (transport, error))
|
||||
)
|
||||
|
||||
func getTransport(address string) (transport, error) {
|
||||
|
@ -571,6 +586,7 @@ func getTransport(address string) (transport, error) {
|
|||
f := transports[v[:i]]
|
||||
if f == nil {
|
||||
err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
|
||||
continue
|
||||
}
|
||||
t, err = f(v[i+1:])
|
||||
if err == nil {
|
||||
|
|
|
@ -1,199 +0,0 @@
|
|||
package dbus
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSessionBus(t *testing.T) {
|
||||
_, err := SessionBus()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBus(t *testing.T) {
|
||||
_, err := SystemBus()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSend(t *testing.T) {
|
||||
bus, err := SessionBus()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
ch := make(chan *Call, 1)
|
||||
msg := &Message{
|
||||
Type: TypeMethodCall,
|
||||
Flags: 0,
|
||||
Headers: map[HeaderField]Variant{
|
||||
FieldDestination: MakeVariant(bus.Names()[0]),
|
||||
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/DBus")),
|
||||
FieldInterface: MakeVariant("org.freedesktop.DBus.Peer"),
|
||||
FieldMember: MakeVariant("Ping"),
|
||||
},
|
||||
}
|
||||
call := bus.Send(msg, ch)
|
||||
<-ch
|
||||
if call.Err != nil {
|
||||
t.Error(call.Err)
|
||||
}
|
||||
}
|
||||
|
||||
type server struct{}
|
||||
|
||||
func (server) Double(i int64) (int64, *Error) {
|
||||
return 2 * i, nil
|
||||
}
|
||||
|
||||
func BenchmarkCall(b *testing.B) {
|
||||
b.StopTimer()
|
||||
var s string
|
||||
bus, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
name := bus.Names()[0]
|
||||
obj := bus.BusObject()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := obj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&s)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if s != name {
|
||||
b.Errorf("got %s, wanted %s", s, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCallAsync(b *testing.B) {
|
||||
b.StopTimer()
|
||||
bus, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
name := bus.Names()[0]
|
||||
obj := bus.BusObject()
|
||||
c := make(chan *Call, 50)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for i := 0; i < b.N; i++ {
|
||||
v := <-c
|
||||
if v.Err != nil {
|
||||
b.Error(v.Err)
|
||||
}
|
||||
s := v.Body[0].(string)
|
||||
if s != name {
|
||||
b.Errorf("got %s, wanted %s", s, name)
|
||||
}
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
obj.Go("org.freedesktop.DBus.GetNameOwner", 0, c, name)
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
func BenchmarkServe(b *testing.B) {
|
||||
b.StopTimer()
|
||||
srv, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
cli, err := SessionBusPrivate()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err = cli.Auth(nil); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err = cli.Hello(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
benchmarkServe(b, srv, cli)
|
||||
}
|
||||
|
||||
func BenchmarkServeAsync(b *testing.B) {
|
||||
b.StopTimer()
|
||||
srv, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
cli, err := SessionBusPrivate()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err = cli.Auth(nil); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err = cli.Hello(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
benchmarkServeAsync(b, srv, cli)
|
||||
}
|
||||
|
||||
func BenchmarkServeSameConn(b *testing.B) {
|
||||
b.StopTimer()
|
||||
bus, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
benchmarkServe(b, bus, bus)
|
||||
}
|
||||
|
||||
func BenchmarkServeSameConnAsync(b *testing.B) {
|
||||
b.StopTimer()
|
||||
bus, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
benchmarkServeAsync(b, bus, bus)
|
||||
}
|
||||
|
||||
func benchmarkServe(b *testing.B, srv, cli *Conn) {
|
||||
var r int64
|
||||
var err error
|
||||
dest := srv.Names()[0]
|
||||
srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
|
||||
obj := cli.Object(dest, "/org/guelfey/DBus/Test")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err = obj.Call("org.guelfey.DBus.Test.Double", 0, int64(i)).Store(&r)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if r != 2*int64(i) {
|
||||
b.Errorf("got %d, wanted %d", r, 2*int64(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkServeAsync(b *testing.B, srv, cli *Conn) {
|
||||
dest := srv.Names()[0]
|
||||
srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
|
||||
obj := cli.Object(dest, "/org/guelfey/DBus/Test")
|
||||
c := make(chan *Call, 50)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for i := 0; i < b.N; i++ {
|
||||
v := <-c
|
||||
if v.Err != nil {
|
||||
b.Fatal(v.Err)
|
||||
}
|
||||
i, r := v.Args[0].(int64), v.Body[0].(int64)
|
||||
if 2*i != r {
|
||||
b.Errorf("got %d, wanted %d", r, 2*i)
|
||||
}
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
obj.Go("org.guelfey.DBus.Test.Double", 0, c, int64(i))
|
||||
}
|
||||
<-done
|
||||
}
|
|
@ -16,24 +16,43 @@ type encoder struct {
|
|||
|
||||
// NewEncoder returns a new encoder that writes to out in the given byte order.
|
||||
func newEncoder(out io.Writer, order binary.ByteOrder) *encoder {
|
||||
return newEncoderAtOffset(out, 0, order)
|
||||
}
|
||||
|
||||
// newEncoderAtOffset returns a new encoder that writes to out in the given
|
||||
// byte order. Specify the offset to initialize pos for proper alignment
|
||||
// computation.
|
||||
func newEncoderAtOffset(out io.Writer, offset int, order binary.ByteOrder) *encoder {
|
||||
enc := new(encoder)
|
||||
enc.out = out
|
||||
enc.order = order
|
||||
enc.pos = offset
|
||||
return enc
|
||||
}
|
||||
|
||||
// Aligns the next output to be on a multiple of n. Panics on write errors.
|
||||
func (enc *encoder) align(n int) {
|
||||
if enc.pos%n != 0 {
|
||||
newpos := (enc.pos + n - 1) & ^(n - 1)
|
||||
empty := make([]byte, newpos-enc.pos)
|
||||
pad := enc.padding(0, n)
|
||||
if pad > 0 {
|
||||
empty := make([]byte, pad)
|
||||
if _, err := enc.out.Write(empty); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos = newpos
|
||||
enc.pos += pad
|
||||
}
|
||||
}
|
||||
|
||||
// pad returns the number of bytes of padding, based on current position and additional offset.
|
||||
// and alignment.
|
||||
func (enc *encoder) padding(offset, algn int) int {
|
||||
abs := enc.pos + offset
|
||||
if abs%algn != 0 {
|
||||
newabs := (abs + algn - 1) & ^(algn - 1)
|
||||
return newabs - abs
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Calls binary.Write(enc.out, enc.order, v) and panics on write errors.
|
||||
func (enc *encoder) binwrite(v interface{}) {
|
||||
if err := binary.Write(enc.out, enc.order, v); err != nil {
|
||||
|
@ -108,8 +127,13 @@ func (enc *encoder) encode(v reflect.Value, depth int) {
|
|||
if depth >= 64 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
// Lookahead offset: 4 bytes for uint32 length (with alignment),
|
||||
// plus alignment for elements.
|
||||
n := enc.padding(0, 4) + 4
|
||||
offset := enc.pos + n + enc.padding(n, alignment(v.Type().Elem()))
|
||||
|
||||
var buf bytes.Buffer
|
||||
bufenc := newEncoder(&buf, enc.order)
|
||||
bufenc := newEncoderAtOffset(&buf, offset, enc.order)
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
bufenc.encode(v.Index(i), depth+1)
|
||||
|
@ -159,8 +183,13 @@ func (enc *encoder) encode(v reflect.Value, depth int) {
|
|||
panic(InvalidTypeError{v.Type()})
|
||||
}
|
||||
keys := v.MapKeys()
|
||||
// Lookahead offset: 4 bytes for uint32 length (with alignment),
|
||||
// plus 8-byte alignment
|
||||
n := enc.padding(0, 4) + 4
|
||||
offset := enc.pos + n + enc.padding(n, 8)
|
||||
|
||||
var buf bytes.Buffer
|
||||
bufenc := newEncoder(&buf, enc.order)
|
||||
bufenc := newEncoderAtOffset(&buf, offset, enc.order)
|
||||
for _, k := range keys {
|
||||
bufenc.align(8)
|
||||
bufenc.encode(k, depth+2)
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
package dbus
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleConn_Emit() {
|
||||
conn, err := SessionBus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn.Emit("/foo/bar", "foo.bar.Baz", uint32(0xDAEDBEEF))
|
||||
}
|
||||
|
||||
func ExampleObject_Call() {
|
||||
var list []string
|
||||
|
||||
conn, err := SessionBus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&list)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range list {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleObject_Go() {
|
||||
conn, err := SessionBus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ch := make(chan *Call, 10)
|
||||
conn.BusObject().Go("org.freedesktop.DBus.ListActivatableNames", 0, ch)
|
||||
select {
|
||||
case call := <-ch:
|
||||
if call.Err != nil {
|
||||
panic(err)
|
||||
}
|
||||
list := call.Body[0].([]string)
|
||||
for _, v := range list {
|
||||
fmt.Println(v)
|
||||
}
|
||||
// put some other cases here
|
||||
}
|
||||
}
|
|
@ -2,9 +2,9 @@ package dbus
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -22,16 +22,52 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// exportWithMapping represents an exported struct along with a method name
|
||||
// mapping to allow for exporting lower-case methods, etc.
|
||||
type exportWithMapping struct {
|
||||
export interface{}
|
||||
|
||||
// Method name mapping; key -> struct method, value -> dbus method.
|
||||
mapping map[string]string
|
||||
|
||||
// Whether or not this export is for the entire subtree
|
||||
includeSubtree bool
|
||||
}
|
||||
|
||||
// Sender is a type which can be used in exported methods to receive the message
|
||||
// sender.
|
||||
type Sender string
|
||||
|
||||
func exportedMethod(v interface{}, name string) reflect.Value {
|
||||
if v == nil {
|
||||
func exportedMethod(export exportWithMapping, name string) reflect.Value {
|
||||
if export.export == nil {
|
||||
return reflect.Value{}
|
||||
}
|
||||
m := reflect.ValueOf(v).MethodByName(name)
|
||||
if !m.IsValid() {
|
||||
|
||||
// If a mapping was included in the export, check the map to see if we
|
||||
// should be looking for a different method in the export.
|
||||
if export.mapping != nil {
|
||||
for key, value := range export.mapping {
|
||||
if value == name {
|
||||
name = key
|
||||
break
|
||||
}
|
||||
|
||||
// Catch the case where a method is aliased but the client is calling
|
||||
// the original, e.g. the "Foo" method was exported mapped to
|
||||
// "foo," and dbus client called the original "Foo."
|
||||
if key == name {
|
||||
return reflect.Value{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(export.export)
|
||||
m := value.MethodByName(name)
|
||||
|
||||
// Catch the case of attempting to call an unexported method
|
||||
method, ok := value.Type().MethodByName(name)
|
||||
|
||||
if !m.IsValid() || !ok || method.PkgPath != "" {
|
||||
return reflect.Value{}
|
||||
}
|
||||
t := m.Type()
|
||||
|
@ -43,6 +79,42 @@ func exportedMethod(v interface{}, name string) reflect.Value {
|
|||
return m
|
||||
}
|
||||
|
||||
// searchHandlers will look through all registered handlers looking for one
|
||||
// to handle the given path. If a verbatim one isn't found, it will check for
|
||||
// a subtree registration for the path as well.
|
||||
func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportWithMapping, bool) {
|
||||
conn.handlersLck.RLock()
|
||||
defer conn.handlersLck.RUnlock()
|
||||
|
||||
handlers, ok := conn.handlers[path]
|
||||
if ok {
|
||||
return handlers, ok
|
||||
}
|
||||
|
||||
// If handlers weren't found for this exact path, look for a matching subtree
|
||||
// registration
|
||||
handlers = make(map[string]exportWithMapping)
|
||||
path = path[:strings.LastIndex(string(path), "/")]
|
||||
for len(path) > 0 {
|
||||
var subtreeHandlers map[string]exportWithMapping
|
||||
subtreeHandlers, ok = conn.handlers[path]
|
||||
if ok {
|
||||
for iface, handler := range subtreeHandlers {
|
||||
// Only include this handler if it registered for the subtree
|
||||
if handler.includeSubtree {
|
||||
handlers[iface] = handler
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
path = path[:strings.LastIndex(string(path), "/")]
|
||||
}
|
||||
|
||||
return handlers, ok
|
||||
}
|
||||
|
||||
// handleCall handles the given method call (i.e. looks if it's one of the
|
||||
// pre-implemented ones and searches for a corresponding handler if not).
|
||||
func (conn *Conn) handleCall(msg *Message) {
|
||||
|
@ -62,40 +134,35 @@ func (conn *Conn) handleCall(msg *Message) {
|
|||
}
|
||||
return
|
||||
}
|
||||
if len(name) == 0 || unicode.IsLower([]rune(name)[0]) {
|
||||
if len(name) == 0 {
|
||||
conn.sendError(errmsgUnknownMethod, sender, serial)
|
||||
}
|
||||
|
||||
// Find the exported handler (if any) for this path
|
||||
handlers, ok := conn.searchHandlers(path)
|
||||
if !ok {
|
||||
conn.sendError(errmsgNoObject, sender, serial)
|
||||
return
|
||||
}
|
||||
|
||||
var m reflect.Value
|
||||
if hasIface {
|
||||
conn.handlersLck.RLock()
|
||||
obj, ok := conn.handlers[path]
|
||||
if !ok {
|
||||
conn.sendError(errmsgNoObject, sender, serial)
|
||||
conn.handlersLck.RUnlock()
|
||||
return
|
||||
}
|
||||
iface := obj[ifaceName]
|
||||
conn.handlersLck.RUnlock()
|
||||
iface := handlers[ifaceName]
|
||||
m = exportedMethod(iface, name)
|
||||
} else {
|
||||
conn.handlersLck.RLock()
|
||||
if _, ok := conn.handlers[path]; !ok {
|
||||
conn.sendError(errmsgNoObject, sender, serial)
|
||||
conn.handlersLck.RUnlock()
|
||||
return
|
||||
}
|
||||
for _, v := range conn.handlers[path] {
|
||||
for _, v := range handlers {
|
||||
m = exportedMethod(v, name)
|
||||
if m.IsValid() {
|
||||
break
|
||||
}
|
||||
}
|
||||
conn.handlersLck.RUnlock()
|
||||
}
|
||||
|
||||
if !m.IsValid() {
|
||||
conn.sendError(errmsgUnknownMethod, sender, serial)
|
||||
return
|
||||
}
|
||||
|
||||
t := m.Type()
|
||||
vs := msg.Body
|
||||
pointers := make([]interface{}, t.NumIn())
|
||||
|
@ -106,27 +173,36 @@ func (conn *Conn) handleCall(msg *Message) {
|
|||
pointers[i] = val.Interface()
|
||||
if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
|
||||
val.Elem().SetString(sender)
|
||||
} else if tp == reflect.TypeOf((*Message)(nil)).Elem() {
|
||||
val.Elem().Set(reflect.ValueOf(*msg))
|
||||
} else {
|
||||
decode = append(decode, pointers[i])
|
||||
}
|
||||
}
|
||||
|
||||
if len(decode) != len(vs) {
|
||||
conn.sendError(errmsgInvalidArg, sender, serial)
|
||||
return
|
||||
}
|
||||
|
||||
if err := Store(vs, decode...); err != nil {
|
||||
conn.sendError(errmsgInvalidArg, sender, serial)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract parameters
|
||||
params := make([]reflect.Value, len(pointers))
|
||||
for i := 0; i < len(pointers); i++ {
|
||||
params[i] = reflect.ValueOf(pointers[i]).Elem()
|
||||
}
|
||||
|
||||
// Call method
|
||||
ret := m.Call(params)
|
||||
if em := ret[t.NumOut()-1].Interface().(*Error); em != nil {
|
||||
conn.sendError(*em, sender, serial)
|
||||
return
|
||||
}
|
||||
|
||||
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||
reply := new(Message)
|
||||
reply.Type = TypeMethodReply
|
||||
|
@ -203,6 +279,10 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro
|
|||
// contribute to the dbus signature of the method (i.e. the method is exposed
|
||||
// as if the parameters of type Sender were not there).
|
||||
//
|
||||
// Similarly, any parameters with the type Message are set to the raw message
|
||||
// received on the bus. Again, parameters of this type do not contribute to the
|
||||
// dbus signature of the method.
|
||||
//
|
||||
// Every method call is executed in a new goroutine, so the method may be called
|
||||
// in multiple goroutines at once.
|
||||
//
|
||||
|
@ -214,10 +294,51 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro
|
|||
//
|
||||
// Export returns an error if path is not a valid path name.
|
||||
func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
|
||||
return conn.ExportWithMap(v, nil, path, iface)
|
||||
}
|
||||
|
||||
// ExportWithMap works exactly like Export but provides the ability to remap
|
||||
// method names (e.g. export a lower-case method).
|
||||
//
|
||||
// The keys in the map are the real method names (exported on the struct), and
|
||||
// the values are the method names to be exported on DBus.
|
||||
func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
|
||||
return conn.exportWithMap(v, mapping, path, iface, false)
|
||||
}
|
||||
|
||||
// ExportSubtree works exactly like Export but registers the given value for
|
||||
// an entire subtree rather under the root path provided.
|
||||
//
|
||||
// In order to make this useful, one parameter in each of the value's exported
|
||||
// methods should be a Message, in which case it will contain the raw message
|
||||
// (allowing one to get access to the path that caused the method to be called).
|
||||
//
|
||||
// Note that more specific export paths take precedence over less specific. For
|
||||
// example, a method call using the ObjectPath /foo/bar/baz will call a method
|
||||
// exported on /foo/bar before a method exported on /foo.
|
||||
func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
|
||||
return conn.ExportSubtreeWithMap(v, nil, path, iface)
|
||||
}
|
||||
|
||||
// ExportSubtreeWithMap works exactly like ExportSubtree but provides the
|
||||
// ability to remap method names (e.g. export a lower-case method).
|
||||
//
|
||||
// The keys in the map are the real method names (exported on the struct), and
|
||||
// the values are the method names to be exported on DBus.
|
||||
func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
|
||||
return conn.exportWithMap(v, mapping, path, iface, true)
|
||||
}
|
||||
|
||||
// exportWithMap is the worker function for all exports/registrations.
|
||||
func (conn *Conn) exportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string, includeSubtree bool) error {
|
||||
if !path.IsValid() {
|
||||
return errors.New("dbus: invalid path name")
|
||||
return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
|
||||
}
|
||||
|
||||
conn.handlersLck.Lock()
|
||||
defer conn.handlersLck.Unlock()
|
||||
|
||||
// Remove a previous export if the interface is nil
|
||||
if v == nil {
|
||||
if _, ok := conn.handlers[path]; ok {
|
||||
delete(conn.handlers[path], iface)
|
||||
|
@ -225,51 +346,39 @@ func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
|
|||
delete(conn.handlers, path)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// If this is the first handler for this path, make a new map to hold all
|
||||
// handlers for this path.
|
||||
if _, ok := conn.handlers[path]; !ok {
|
||||
conn.handlers[path] = make(map[string]interface{})
|
||||
conn.handlers[path] = make(map[string]exportWithMapping)
|
||||
}
|
||||
conn.handlers[path][iface] = v
|
||||
conn.handlersLck.Unlock()
|
||||
|
||||
// Finally, save this handler
|
||||
conn.handlers[path][iface] = exportWithMapping{export: v, mapping: mapping, includeSubtree: includeSubtree}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReleaseName calls org.freedesktop.DBus.ReleaseName. You should use only this
|
||||
// method to release a name (see below).
|
||||
// ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response.
|
||||
func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
|
||||
var r uint32
|
||||
err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if r == uint32(ReleaseNameReplyReleased) {
|
||||
conn.namesLck.Lock()
|
||||
for i, v := range conn.names {
|
||||
if v == name {
|
||||
copy(conn.names[i:], conn.names[i+1:])
|
||||
conn.names = conn.names[:len(conn.names)-1]
|
||||
}
|
||||
}
|
||||
conn.namesLck.Unlock()
|
||||
}
|
||||
return ReleaseNameReply(r), nil
|
||||
}
|
||||
|
||||
// RequestName calls org.freedesktop.DBus.RequestName. You should use only this
|
||||
// method to request a name because package dbus needs to keep track of all
|
||||
// names that the connection has.
|
||||
// RequestName calls org.freedesktop.DBus.RequestName and awaits a response.
|
||||
func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
|
||||
var r uint32
|
||||
err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if r == uint32(RequestNameReplyPrimaryOwner) {
|
||||
conn.namesLck.Lock()
|
||||
conn.names = append(conn.names, name)
|
||||
conn.namesLck.Unlock()
|
||||
}
|
||||
return RequestNameReply(r), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
// Call calls org.freedesktop.Introspectable.Introspect on a remote object
|
||||
// and returns the introspection data.
|
||||
func Call(o *dbus.Object) (*Node, error) {
|
||||
func Call(o dbus.BusObject) (*Node, error) {
|
||||
var xmldata string
|
||||
var node Node
|
||||
|
||||
|
|
|
@ -59,7 +59,8 @@ func Methods(v interface{}) []Method {
|
|||
m.Name = t.Method(i).Name
|
||||
m.Args = make([]Arg, 0, mt.NumIn()+mt.NumOut()-2)
|
||||
for j := 1; j < mt.NumIn(); j++ {
|
||||
if mt.In(j) != reflect.TypeOf((*dbus.Sender)(nil)).Elem() {
|
||||
if mt.In(j) != reflect.TypeOf((*dbus.Sender)(nil)).Elem() &&
|
||||
mt.In(j) != reflect.TypeOf((*dbus.Message)(nil)).Elem() {
|
||||
arg := Arg{"", dbus.SignatureOfType(mt.In(j)).String(), "in"}
|
||||
m.Args = append(m.Args, arg)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BusObject is the interface of a remote object on which methods can be
|
||||
// invoked.
|
||||
type BusObject interface {
|
||||
Call(method string, flags Flags, args ...interface{}) *Call
|
||||
Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call
|
||||
GetProperty(p string) (Variant, error)
|
||||
Destination() string
|
||||
Path() ObjectPath
|
||||
}
|
||||
|
||||
// Object represents a remote object on which methods can be invoked.
|
||||
type Object struct {
|
||||
conn *Conn
|
||||
dest string
|
||||
path ObjectPath
|
||||
}
|
||||
|
||||
// Call calls a method with (*Object).Go and waits for its reply.
|
||||
func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
|
||||
return <-o.Go(method, flags, make(chan *Call, 1), args...).Done
|
||||
}
|
||||
|
||||
// Go calls a method with the given arguments asynchronously. It returns a
|
||||
// Call structure representing this method call. The passed channel will
|
||||
// return the same value once the call is done. If ch is nil, a new channel
|
||||
// will be allocated. Otherwise, ch has to be buffered or Go will panic.
|
||||
//
|
||||
// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
|
||||
// is returned of which only the Err member is valid.
|
||||
//
|
||||
// If the method parameter contains a dot ('.'), the part before the last dot
|
||||
// specifies the interface on which the method is called.
|
||||
func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
|
||||
iface := ""
|
||||
i := strings.LastIndex(method, ".")
|
||||
if i != -1 {
|
||||
iface = method[:i]
|
||||
}
|
||||
method = method[i+1:]
|
||||
msg := new(Message)
|
||||
msg.Type = TypeMethodCall
|
||||
msg.serial = o.conn.getSerial()
|
||||
msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
msg.Headers[FieldPath] = MakeVariant(o.path)
|
||||
msg.Headers[FieldDestination] = MakeVariant(o.dest)
|
||||
msg.Headers[FieldMember] = MakeVariant(method)
|
||||
if iface != "" {
|
||||
msg.Headers[FieldInterface] = MakeVariant(iface)
|
||||
}
|
||||
msg.Body = args
|
||||
if len(args) > 0 {
|
||||
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
|
||||
}
|
||||
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||
if ch == nil {
|
||||
ch = make(chan *Call, 10)
|
||||
} else if cap(ch) == 0 {
|
||||
panic("dbus: unbuffered channel passed to (*Object).Go")
|
||||
}
|
||||
call := &Call{
|
||||
Destination: o.dest,
|
||||
Path: o.path,
|
||||
Method: method,
|
||||
Args: args,
|
||||
Done: ch,
|
||||
}
|
||||
o.conn.callsLck.Lock()
|
||||
o.conn.calls[msg.serial] = call
|
||||
o.conn.callsLck.Unlock()
|
||||
o.conn.outLck.RLock()
|
||||
if o.conn.closed {
|
||||
call.Err = ErrClosed
|
||||
call.Done <- call
|
||||
} else {
|
||||
o.conn.out <- msg
|
||||
}
|
||||
o.conn.outLck.RUnlock()
|
||||
return call
|
||||
}
|
||||
o.conn.outLck.RLock()
|
||||
defer o.conn.outLck.RUnlock()
|
||||
if o.conn.closed {
|
||||
return &Call{Err: ErrClosed}
|
||||
}
|
||||
o.conn.out <- msg
|
||||
return &Call{Err: nil}
|
||||
}
|
||||
|
||||
// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
|
||||
// object. The property name must be given in interface.member notation.
|
||||
func (o *Object) GetProperty(p string) (Variant, error) {
|
||||
idx := strings.LastIndex(p, ".")
|
||||
if idx == -1 || idx+1 == len(p) {
|
||||
return Variant{}, errors.New("dbus: invalid property " + p)
|
||||
}
|
||||
|
||||
iface := p[:idx]
|
||||
prop := p[idx+1:]
|
||||
|
||||
result := Variant{}
|
||||
err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
|
||||
|
||||
if err != nil {
|
||||
return Variant{}, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Destination returns the destination that calls on o are sent to.
|
||||
func (o *Object) Destination() string {
|
||||
return o.dest
|
||||
}
|
||||
|
||||
// Path returns the path that calls on o are sent to.
|
||||
func (o *Object) Path() ObjectPath {
|
||||
return o.path
|
||||
}
|
|
@ -1,369 +0,0 @@
|
|||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var protoTests = []struct {
|
||||
vs []interface{}
|
||||
bigEndian []byte
|
||||
littleEndian []byte
|
||||
}{
|
||||
{
|
||||
[]interface{}{int32(0)},
|
||||
[]byte{0, 0, 0, 0},
|
||||
[]byte{0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{true, false},
|
||||
[]byte{0, 0, 0, 1, 0, 0, 0, 0},
|
||||
[]byte{1, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{byte(0), uint16(12), int16(32), uint32(43)},
|
||||
[]byte{0, 0, 0, 12, 0, 32, 0, 0, 0, 0, 0, 43},
|
||||
[]byte{0, 0, 12, 0, 32, 0, 0, 0, 43, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{int64(-1), uint64(1<<64 - 1)},
|
||||
bytes.Repeat([]byte{255}, 16),
|
||||
bytes.Repeat([]byte{255}, 16),
|
||||
},
|
||||
{
|
||||
[]interface{}{math.Inf(+1)},
|
||||
[]byte{0x7f, 0xf0, 0, 0, 0, 0, 0, 0},
|
||||
[]byte{0, 0, 0, 0, 0, 0, 0xf0, 0x7f},
|
||||
},
|
||||
{
|
||||
[]interface{}{"foo"},
|
||||
[]byte{0, 0, 0, 3, 'f', 'o', 'o', 0},
|
||||
[]byte{3, 0, 0, 0, 'f', 'o', 'o', 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{Signature{"ai"}},
|
||||
[]byte{2, 'a', 'i', 0},
|
||||
[]byte{2, 'a', 'i', 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]int16{42, 256}},
|
||||
[]byte{0, 0, 0, 4, 0, 42, 1, 0},
|
||||
[]byte{4, 0, 0, 0, 42, 0, 0, 1},
|
||||
},
|
||||
{
|
||||
[]interface{}{MakeVariant("foo")},
|
||||
[]byte{1, 's', 0, 0, 0, 0, 0, 3, 'f', 'o', 'o', 0},
|
||||
[]byte{1, 's', 0, 0, 3, 0, 0, 0, 'f', 'o', 'o', 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{MakeVariant(MakeVariant(Signature{"v"}))},
|
||||
[]byte{1, 'v', 0, 1, 'g', 0, 1, 'v', 0},
|
||||
[]byte{1, 'v', 0, 1, 'g', 0, 1, 'v', 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{map[int32]bool{42: true}},
|
||||
[]byte{0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 1},
|
||||
[]byte{8, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 1, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{map[string]Variant{}, byte(42)},
|
||||
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]uint64{}, byte(42)},
|
||||
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||
},
|
||||
}
|
||||
|
||||
func TestProto(t *testing.T) {
|
||||
for i, v := range protoTests {
|
||||
buf := new(bytes.Buffer)
|
||||
bigEnc := newEncoder(buf, binary.BigEndian)
|
||||
bigEnc.Encode(v.vs...)
|
||||
marshalled := buf.Bytes()
|
||||
if bytes.Compare(marshalled, v.bigEndian) != 0 {
|
||||
t.Errorf("test %d (marshal be): got '%v', but expected '%v'\n", i+1, marshalled,
|
||||
v.bigEndian)
|
||||
}
|
||||
buf.Reset()
|
||||
litEnc := newEncoder(buf, binary.LittleEndian)
|
||||
litEnc.Encode(v.vs...)
|
||||
marshalled = buf.Bytes()
|
||||
if bytes.Compare(marshalled, v.littleEndian) != 0 {
|
||||
t.Errorf("test %d (marshal le): got '%v', but expected '%v'\n", i+1, marshalled,
|
||||
v.littleEndian)
|
||||
}
|
||||
unmarshalled := reflect.MakeSlice(reflect.TypeOf(v.vs),
|
||||
0, 0)
|
||||
for i := range v.vs {
|
||||
unmarshalled = reflect.Append(unmarshalled,
|
||||
reflect.New(reflect.TypeOf(v.vs[i])))
|
||||
}
|
||||
bigDec := newDecoder(bytes.NewReader(v.bigEndian), binary.BigEndian)
|
||||
vs, err := bigDec.Decode(SignatureOf(v.vs...))
|
||||
if err != nil {
|
||||
t.Errorf("test %d (unmarshal be): %s\n", i+1, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(vs, v.vs) {
|
||||
t.Errorf("test %d (unmarshal be): got %#v, but expected %#v\n", i+1, vs, v.vs)
|
||||
}
|
||||
litDec := newDecoder(bytes.NewReader(v.littleEndian), binary.LittleEndian)
|
||||
vs, err = litDec.Decode(SignatureOf(v.vs...))
|
||||
if err != nil {
|
||||
t.Errorf("test %d (unmarshal le): %s\n", i+1, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(vs, v.vs) {
|
||||
t.Errorf("test %d (unmarshal le): got %#v, but expected %#v\n", i+1, vs, v.vs)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoMap(t *testing.T) {
|
||||
m := map[string]uint8{
|
||||
"foo": 23,
|
||||
"bar": 2,
|
||||
}
|
||||
var n map[string]uint8
|
||||
buf := new(bytes.Buffer)
|
||||
enc := newEncoder(buf, binary.LittleEndian)
|
||||
enc.Encode(m)
|
||||
dec := newDecoder(buf, binary.LittleEndian)
|
||||
vs, err := dec.Decode(Signature{"a{sy}"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = Store(vs, &n); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(n) != 2 || n["foo"] != 23 || n["bar"] != 2 {
|
||||
t.Error("got", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoVariantStruct(t *testing.T) {
|
||||
var variant Variant
|
||||
v := MakeVariant(struct {
|
||||
A int32
|
||||
B int16
|
||||
}{1, 2})
|
||||
buf := new(bytes.Buffer)
|
||||
enc := newEncoder(buf, binary.LittleEndian)
|
||||
enc.Encode(v)
|
||||
dec := newDecoder(buf, binary.LittleEndian)
|
||||
vs, err := dec.Decode(Signature{"v"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = Store(vs, &variant); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sl := variant.Value().([]interface{})
|
||||
v1, v2 := sl[0].(int32), sl[1].(int16)
|
||||
if v1 != int32(1) {
|
||||
t.Error("got", v1, "as first int")
|
||||
}
|
||||
if v2 != int16(2) {
|
||||
t.Error("got", v2, "as second int")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoStructTag(t *testing.T) {
|
||||
type Bar struct {
|
||||
A int32
|
||||
B chan interface{} `dbus:"-"`
|
||||
C int32
|
||||
}
|
||||
var bar1, bar2 Bar
|
||||
bar1.A = 234
|
||||
bar2.C = 345
|
||||
buf := new(bytes.Buffer)
|
||||
enc := newEncoder(buf, binary.LittleEndian)
|
||||
enc.Encode(bar1)
|
||||
dec := newDecoder(buf, binary.LittleEndian)
|
||||
vs, err := dec.Decode(Signature{"(ii)"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = Store(vs, &bar2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bar1 != bar2 {
|
||||
t.Error("struct tag test: got", bar2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoStoreStruct(t *testing.T) {
|
||||
var foo struct {
|
||||
A int32
|
||||
B string
|
||||
c chan interface{}
|
||||
D interface{} `dbus:"-"`
|
||||
}
|
||||
src := []interface{}{[]interface{}{int32(42), "foo"}}
|
||||
err := Store(src, &foo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoStoreNestedStruct(t *testing.T) {
|
||||
var foo struct {
|
||||
A int32
|
||||
B struct {
|
||||
C string
|
||||
D float64
|
||||
}
|
||||
}
|
||||
src := []interface{}{
|
||||
[]interface{}{
|
||||
int32(42),
|
||||
[]interface{}{
|
||||
"foo",
|
||||
3.14,
|
||||
},
|
||||
},
|
||||
}
|
||||
err := Store(src, &foo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessage(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
message := new(Message)
|
||||
message.Type = TypeMethodCall
|
||||
message.serial = 32
|
||||
message.Headers = map[HeaderField]Variant{
|
||||
FieldPath: MakeVariant(ObjectPath("/org/foo/bar")),
|
||||
FieldMember: MakeVariant("baz"),
|
||||
}
|
||||
message.Body = make([]interface{}, 0)
|
||||
err := message.EncodeTo(buf, binary.LittleEndian)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = DecodeMessage(buf)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoStructInterfaces(t *testing.T) {
|
||||
b := []byte{42}
|
||||
vs, err := newDecoder(bytes.NewReader(b), binary.LittleEndian).Decode(Signature{"(y)"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if vs[0].([]interface{})[0].(byte) != 42 {
|
||||
t.Errorf("wrongs results (got %v)", vs)
|
||||
}
|
||||
}
|
||||
|
||||
// ordinary org.freedesktop.DBus.Hello call
|
||||
var smallMessage = &Message{
|
||||
Type: TypeMethodCall,
|
||||
serial: 1,
|
||||
Headers: map[HeaderField]Variant{
|
||||
FieldDestination: MakeVariant("org.freedesktop.DBus"),
|
||||
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/DBus")),
|
||||
FieldInterface: MakeVariant("org.freedesktop.DBus"),
|
||||
FieldMember: MakeVariant("Hello"),
|
||||
},
|
||||
}
|
||||
|
||||
// org.freedesktop.Notifications.Notify
|
||||
var bigMessage = &Message{
|
||||
Type: TypeMethodCall,
|
||||
serial: 2,
|
||||
Headers: map[HeaderField]Variant{
|
||||
FieldDestination: MakeVariant("org.freedesktop.Notifications"),
|
||||
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/Notifications")),
|
||||
FieldInterface: MakeVariant("org.freedesktop.Notifications"),
|
||||
FieldMember: MakeVariant("Notify"),
|
||||
FieldSignature: MakeVariant(Signature{"susssasa{sv}i"}),
|
||||
},
|
||||
Body: []interface{}{
|
||||
"app_name",
|
||||
uint32(0),
|
||||
"dialog-information",
|
||||
"Notification",
|
||||
"This is the body of a notification",
|
||||
[]string{"ok", "Ok"},
|
||||
map[string]Variant{
|
||||
"sound-name": MakeVariant("dialog-information"),
|
||||
},
|
||||
int32(-1),
|
||||
},
|
||||
}
|
||||
|
||||
func BenchmarkDecodeMessageSmall(b *testing.B) {
|
||||
var err error
|
||||
var rd *bytes.Reader
|
||||
|
||||
b.StopTimer()
|
||||
buf := new(bytes.Buffer)
|
||||
err = smallMessage.EncodeTo(buf, binary.LittleEndian)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
decoded := buf.Bytes()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rd = bytes.NewReader(decoded)
|
||||
_, err = DecodeMessage(rd)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeMessageBig(b *testing.B) {
|
||||
var err error
|
||||
var rd *bytes.Reader
|
||||
|
||||
b.StopTimer()
|
||||
buf := new(bytes.Buffer)
|
||||
err = bigMessage.EncodeTo(buf, binary.LittleEndian)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
decoded := buf.Bytes()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rd = bytes.NewReader(decoded)
|
||||
_, err = DecodeMessage(rd)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeMessageSmall(b *testing.B) {
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
err = smallMessage.EncodeTo(ioutil.Discard, binary.LittleEndian)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeMessageBig(b *testing.B) {
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
err = bigMessage.EncodeTo(ioutil.Discard, binary.LittleEndian)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var sigTests = []struct {
|
||||
vs []interface{}
|
||||
sig Signature
|
||||
}{
|
||||
{
|
||||
[]interface{}{new(int32)},
|
||||
Signature{"i"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new(string)},
|
||||
Signature{"s"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new(Signature)},
|
||||
Signature{"g"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new([]int16)},
|
||||
Signature{"an"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new(int16), new(uint32)},
|
||||
Signature{"nu"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new(map[byte]Variant)},
|
||||
Signature{"a{yv}"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new(Variant), new([]map[int32]string)},
|
||||
Signature{"vaa{is}"},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSig(t *testing.T) {
|
||||
for i, v := range sigTests {
|
||||
sig := SignatureOf(v.vs...)
|
||||
if sig != v.sig {
|
||||
t.Errorf("test %d: got %q, expected %q", i+1, sig.str, v.sig.str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var getSigTest = []interface{}{
|
||||
[]struct {
|
||||
b byte
|
||||
i int32
|
||||
t uint64
|
||||
s string
|
||||
}{},
|
||||
map[string]Variant{},
|
||||
}
|
||||
|
||||
func BenchmarkGetSignatureSimple(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
SignatureOf("", int32(0))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetSignatureLong(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
SignatureOf(getSigTest...)
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package dbus
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testString = `This is a test!
|
||||
This text should be read from the file that is created by this test.`
|
||||
|
||||
type unixFDTest struct{}
|
||||
|
||||
func (t unixFDTest) Test(fd UnixFD) (string, *Error) {
|
||||
var b [4096]byte
|
||||
file := os.NewFile(uintptr(fd), "testfile")
|
||||
defer file.Close()
|
||||
n, err := file.Read(b[:])
|
||||
if err != nil {
|
||||
return "", &Error{"com.github.guelfey.test.Error", nil}
|
||||
}
|
||||
return string(b[:n]), nil
|
||||
}
|
||||
|
||||
func TestUnixFDs(t *testing.T) {
|
||||
conn, err := SessionBus()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer w.Close()
|
||||
if _, err := w.Write([]byte(testString)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
name := conn.Names()[0]
|
||||
test := unixFDTest{}
|
||||
conn.Export(test, "/com/github/guelfey/test", "com.github.guelfey.test")
|
||||
var s string
|
||||
obj := conn.Object(name, "/com/github/guelfey/test")
|
||||
err = obj.Call("com.github.guelfey.test.Test", 0, UnixFD(r.Fd())).Store(&s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s != testString {
|
||||
t.Fatal("got", s, "wanted", testString)
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package dbus
|
||||
|
||||
import "reflect"
|
||||
import "testing"
|
||||
|
||||
var variantFormatTests = []struct {
|
||||
v interface{}
|
||||
s string
|
||||
}{
|
||||
{int32(1), `1`},
|
||||
{"foo", `"foo"`},
|
||||
{ObjectPath("/org/foo"), `@o "/org/foo"`},
|
||||
{Signature{"i"}, `@g "i"`},
|
||||
{[]byte{}, `@ay []`},
|
||||
{[]int32{1, 2}, `[1, 2]`},
|
||||
{[]int64{1, 2}, `@ax [1, 2]`},
|
||||
{[][]int32{{3, 4}, {5, 6}}, `[[3, 4], [5, 6]]`},
|
||||
{[]Variant{MakeVariant(int32(1)), MakeVariant(1.0)}, `[<1>, <@d 1>]`},
|
||||
{map[string]int32{"one": 1, "two": 2}, `{"one": 1, "two": 2}`},
|
||||
{map[int32]ObjectPath{1: "/org/foo"}, `@a{io} {1: "/org/foo"}`},
|
||||
{map[string]Variant{}, `@a{sv} {}`},
|
||||
}
|
||||
|
||||
func TestFormatVariant(t *testing.T) {
|
||||
for i, v := range variantFormatTests {
|
||||
if s := MakeVariant(v.v).String(); s != v.s {
|
||||
t.Errorf("test %d: got %q, wanted %q", i+1, s, v.s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var variantParseTests = []struct {
|
||||
s string
|
||||
v interface{}
|
||||
}{
|
||||
{"1", int32(1)},
|
||||
{"true", true},
|
||||
{"false", false},
|
||||
{"1.0", float64(1.0)},
|
||||
{"0x10", int32(16)},
|
||||
{"1e1", float64(10)},
|
||||
{`"foo"`, "foo"},
|
||||
{`"\a\b\f\n\r\t"`, "\x07\x08\x0c\n\r\t"},
|
||||
{`"\u00e4\U0001f603"`, "\u00e4\U0001f603"},
|
||||
{"[1]", []int32{1}},
|
||||
{"[1, 2, 3]", []int32{1, 2, 3}},
|
||||
{"@ai []", []int32{}},
|
||||
{"[1, 5.0]", []float64{1, 5.0}},
|
||||
{"[[1, 2], [3, 4.0]]", [][]float64{{1, 2}, {3, 4}}},
|
||||
{`[@o "/org/foo", "/org/bar"]`, []ObjectPath{"/org/foo", "/org/bar"}},
|
||||
{"<1>", MakeVariant(int32(1))},
|
||||
{"[<1>, <2.0>]", []Variant{MakeVariant(int32(1)), MakeVariant(2.0)}},
|
||||
{`[[], [""]]`, [][]string{{}, {""}}},
|
||||
{`@a{ss} {}`, map[string]string{}},
|
||||
{`{"foo": 1}`, map[string]int32{"foo": 1}},
|
||||
{`[{}, {"foo": "bar"}]`, []map[string]string{{}, {"foo": "bar"}}},
|
||||
{`{"a": <1>, "b": <"foo">}`,
|
||||
map[string]Variant{"a": MakeVariant(int32(1)), "b": MakeVariant("foo")}},
|
||||
{`b''`, []byte{0}},
|
||||
{`b"abc"`, []byte{'a', 'b', 'c', 0}},
|
||||
{`b"\x01\0002\a\b\f\n\r\t"`, []byte{1, 2, 0x7, 0x8, 0xc, '\n', '\r', '\t', 0}},
|
||||
{`[[0], b""]`, [][]byte{{0}, {0}}},
|
||||
{"int16 0", int16(0)},
|
||||
{"byte 0", byte(0)},
|
||||
}
|
||||
|
||||
func TestParseVariant(t *testing.T) {
|
||||
for i, v := range variantParseTests {
|
||||
nv, err := ParseVariant(v.s, Signature{})
|
||||
if err != nil {
|
||||
t.Errorf("test %d: parsing failed: %s", i+1, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(nv.value, v.v) {
|
||||
t.Errorf("test %d: got %q, wanted %q", i+1, nv, v.v)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue