Merge pull request #3577 from justincormack/darwin-arm64

Update containerd vendor
This commit is contained in:
Justin Cormack 2020-11-26 19:09:14 +00:00 committed by GitHub
commit 0af595e49b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 301 additions and 4407 deletions

View File

@ -9,7 +9,7 @@ github.com/agl/ed25519 5312a61534124124185d41f09206b9fef1d88403
github.com/aws/aws-sdk-go fa107560b5f3528a859a1a1511086646731bb1a8 github.com/aws/aws-sdk-go fa107560b5f3528a859a1a1511086646731bb1a8
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925 github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925
github.com/containerd/containerd v1.3.3 github.com/containerd/containerd v1.4.2
github.com/containerd/continuity 0f16d7a0959cac64d7a54ce015e50cf4839d1970 github.com/containerd/continuity 0f16d7a0959cac64d7a54ce015e50cf4839d1970
github.com/containerd/fifo f15a3290365b9d2627d189e619ab4008e0069caf github.com/containerd/fifo f15a3290365b9d2627d189e619ab4008e0069caf
github.com/containerd/ttrpc 0be804eadb152bc3b3c20c5edc314c4633833398 github.com/containerd/ttrpc 0be804eadb152bc3b3c20c5edc314c4633833398
@ -58,7 +58,7 @@ github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc ccbb3364d49d2dc6d9f057134570b0f382f6ceb7 github.com/opencontainers/runc ccbb3364d49d2dc6d9f057134570b0f382f6ceb7
github.com/opencontainers/runtime-spec v1.0.1 github.com/opencontainers/runtime-spec v1.0.1
github.com/packethost/packngo f1be085ecd6fca1b0a0e25eda71f208dcfcee5ab github.com/packethost/packngo f1be085ecd6fca1b0a0e25eda71f208dcfcee5ab
github.com/pkg/errors v0.8.0 github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib v1.0.0
github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e
github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6 github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6

View File

@ -1,8 +1,9 @@
![containerd banner](https://raw.githubusercontent.com/cncf/artwork/master/projects/containerd/horizontal/color/containerd-horizontal-color.png) ![containerd banner](https://raw.githubusercontent.com/cncf/artwork/master/projects/containerd/horizontal/color/containerd-horizontal-color.png)
[![GoDoc](https://godoc.org/github.com/containerd/containerd?status.svg)](https://godoc.org/github.com/containerd/containerd) [![GoDoc](https://godoc.org/github.com/containerd/containerd?status.svg)](https://godoc.org/github.com/containerd/containerd)
[![Build Status](https://travis-ci.org/containerd/containerd.svg?branch=master)](https://travis-ci.org/containerd/containerd) [![Build Status](https://github.com/containerd/containerd/workflows/CI/badge.svg)](https://github.com/containerd/containerd/actions?query=workflow%3ACI)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/containerd/containerd?branch=master&svg=true)](https://ci.appveyor.com/project/mlaventure/containerd-3g73f?branch=master) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/containerd/containerd?branch=master&svg=true)](https://ci.appveyor.com/project/mlaventure/containerd-3g73f?branch=master)
[![Nightlies](https://github.com/containerd/containerd/workflows/Nightly/badge.svg)](https://github.com/containerd/containerd/actions?query=workflow%3ANightly)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd?ref=badge_shield) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd?ref=badge_shield)
[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/containerd)](https://goreportcard.com/report/github.com/containerd/containerd) [![Go Report Card](https://goreportcard.com/badge/github.com/containerd/containerd)](https://goreportcard.com/report/github.com/containerd/containerd)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1271/badge)](https://bestpractices.coreinfrastructure.org/projects/1271) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1271/badge)](https://bestpractices.coreinfrastructure.org/projects/1271)
@ -24,6 +25,12 @@ See how to build containerd from source at [BUILDING](BUILDING.md).
If you are interested in trying out containerd see our example at [Getting Started](docs/getting-started.md). If you are interested in trying out containerd see our example at [Getting Started](docs/getting-started.md).
## Nightly builds
There are nightly builds available for download [here](https://github.com/containerd/containerd/actions?query=workflow%3ANightly).
Binaries are generated from `master` branch every night for `Linux` and `Windows`.
Please be aware: nightly builds might have critical bugs, it's not recommended for use in prodution and no support provided.
## Runtime Requirements ## Runtime Requirements
@ -147,7 +154,7 @@ Taking a container object and turning it into a runnable process on a system is
```go ```go
// create a new task // create a new task
task, err := redis.NewTask(context, cio.Stdio) task, err := redis.NewTask(context, cio.NewCreator(cio.WithStdio))
defer task.Delete(context) defer task.Delete(context)
// the task is now running and has a pid that can be use to setup networking // the task is now running and has a pid that can be use to setup networking
@ -177,7 +184,7 @@ checkpoint, err := client.Pull(context, "myregistry/checkpoints/redis:master")
redis, err = client.NewContainer(context, "redis-master", containerd.WithNewSnapshot("redis-rootfs", checkpoint)) redis, err = client.NewContainer(context, "redis-master", containerd.WithNewSnapshot("redis-rootfs", checkpoint))
defer container.Delete(context) defer container.Delete(context)
task, err = redis.NewTask(context, cio.Stdio, containerd.WithTaskCheckpoint(checkpoint)) task, err = redis.NewTask(context, cio.NewCreator(cio.WithStdio), containerd.WithTaskCheckpoint(checkpoint))
defer task.Delete(context) defer task.Delete(context)
err := task.Start(context) err := task.Start(context)
@ -210,6 +217,34 @@ See [PLUGINS.md](PLUGINS.md) for how to create plugins
Please see [RELEASES.md](RELEASES.md) for details on versioning and stability Please see [RELEASES.md](RELEASES.md) for details on versioning and stability
of containerd components. of containerd components.
Downloadable 64-bit Intel/AMD binaries of all official releases are available on
our [releases page](https://github.com/containerd/containerd/releases), as well as
auto-published to the [cri-containerd-release storage bucket](https://console.cloud.google.com/storage/browser/cri-containerd-release?pli=1).
For other architectures and distribution support, you will find that many
Linux distributions package their own containerd and provide it across several
architectures, such as [Canonical's Ubuntu packaging](https://launchpad.net/ubuntu/bionic/+package/containerd).
#### Enabling command auto-completion
Starting with containerd 1.4, the urfave client feature for auto-creation of bash and zsh
autocompletion data is enabled. To use the autocomplete feature in a bash shell for example, source
the autocomplete/ctr file in your `.bashrc`, or manually like:
```
$ source ./contrib/autocomplete/ctr
```
#### Distribution of `ctr` autocomplete for bash and zsh
For bash, copy the `contrib/autocomplete/ctr` script into
`/etc/bash_completion.d/` and rename it to `ctr`. The `zsh_autocomplete`
file is also available and can be used similarly for zsh users.
Provide documentation to users to `source` this file into their shell if
you don't place the autocomplete file in a location where it is automatically
loaded for the user's shell environment.
### Communication ### Communication
For async communication and long running discussions please use issues and pull requests on the github repo. For async communication and long running discussions please use issues and pull requests on the github repo.
@ -230,7 +265,7 @@ __If you are reporting a security issue, please reach out discreetly at security
## Licenses ## Licenses
The containerd codebase is released under the [Apache 2.0 license](LICENSE.code). The containerd codebase is released under the [Apache 2.0 license](LICENSE).
The README.md file, and files in the "docs" folder are licensed under the The README.md file, and files in the "docs" folder are licensed under the
Creative Commons Attribution 4.0 International License. You may obtain a Creative Commons Attribution 4.0 International License. You may obtain a
copy of the license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/. copy of the license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/.

View File

@ -51,43 +51,43 @@ var (
// IsInvalidArgument returns true if the error is due to an invalid argument // IsInvalidArgument returns true if the error is due to an invalid argument
func IsInvalidArgument(err error) bool { func IsInvalidArgument(err error) bool {
return errors.Cause(err) == ErrInvalidArgument return errors.Is(err, ErrInvalidArgument)
} }
// IsNotFound returns true if the error is due to a missing object // IsNotFound returns true if the error is due to a missing object
func IsNotFound(err error) bool { func IsNotFound(err error) bool {
return errors.Cause(err) == ErrNotFound return errors.Is(err, ErrNotFound)
} }
// IsAlreadyExists returns true if the error is due to an already existing // IsAlreadyExists returns true if the error is due to an already existing
// metadata item // metadata item
func IsAlreadyExists(err error) bool { func IsAlreadyExists(err error) bool {
return errors.Cause(err) == ErrAlreadyExists return errors.Is(err, ErrAlreadyExists)
} }
// IsFailedPrecondition returns true if an operation could not proceed to the // IsFailedPrecondition returns true if an operation could not proceed to the
// lack of a particular condition // lack of a particular condition
func IsFailedPrecondition(err error) bool { func IsFailedPrecondition(err error) bool {
return errors.Cause(err) == ErrFailedPrecondition return errors.Is(err, ErrFailedPrecondition)
} }
// IsUnavailable returns true if the error is due to a resource being unavailable // IsUnavailable returns true if the error is due to a resource being unavailable
func IsUnavailable(err error) bool { func IsUnavailable(err error) bool {
return errors.Cause(err) == ErrUnavailable return errors.Is(err, ErrUnavailable)
} }
// IsNotImplemented returns true if the error is due to not being implemented // IsNotImplemented returns true if the error is due to not being implemented
func IsNotImplemented(err error) bool { func IsNotImplemented(err error) bool {
return errors.Cause(err) == ErrNotImplemented return errors.Is(err, ErrNotImplemented)
} }
// IsCanceled returns true if the error is due to `context.Canceled`. // IsCanceled returns true if the error is due to `context.Canceled`.
func IsCanceled(err error) bool { func IsCanceled(err error) bool {
return errors.Cause(err) == context.Canceled return errors.Is(err, context.Canceled)
} }
// IsDeadlineExceeded returns true if the error is due to // IsDeadlineExceeded returns true if the error is due to
// `context.DeadlineExceeded`. // `context.DeadlineExceeded`.
func IsDeadlineExceeded(err error) bool { func IsDeadlineExceeded(err error) bool {
return errors.Cause(err) == context.DeadlineExceeded return errors.Is(err, context.DeadlineExceeded)
} }

View File

@ -18,7 +18,6 @@ package log
import ( import (
"context" "context"
"sync/atomic"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -38,23 +37,10 @@ type (
loggerKey struct{} loggerKey struct{}
) )
// TraceLevel is the log level for tracing. Trace level is lower than debug level,
// and is usually used to trace detailed behavior of the program.
const TraceLevel = logrus.Level(uint32(logrus.DebugLevel + 1))
// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to // RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
// ensure the formatted time is always the same number of characters. // ensure the formatted time is always the same number of characters.
const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
// ParseLevel takes a string level and returns the Logrus log level constant.
// It supports trace level.
func ParseLevel(lvl string) (logrus.Level, error) {
if lvl == "trace" {
return TraceLevel, nil
}
return logrus.ParseLevel(lvl)
}
// WithLogger returns a new context with the provided logger. Use in // WithLogger returns a new context with the provided logger. Use in
// combination with logger.WithField(s) for great effect. // combination with logger.WithField(s) for great effect.
func WithLogger(ctx context.Context, logger *logrus.Entry) context.Context { func WithLogger(ctx context.Context, logger *logrus.Entry) context.Context {
@ -72,19 +58,3 @@ func GetLogger(ctx context.Context) *logrus.Entry {
return logger.(*logrus.Entry) return logger.(*logrus.Entry)
} }
// Trace logs a message at level Trace with the log entry passed-in.
func Trace(e *logrus.Entry, args ...interface{}) {
level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level)))
if level >= TraceLevel {
e.Debug(args...)
}
}
// Tracef logs a message at level Trace with the log entry passed-in.
func Tracef(e *logrus.Entry, format string, args ...interface{}) {
level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level)))
if level >= TraceLevel {
e.Debugf(format, args...)
}
}

View File

@ -74,8 +74,8 @@ func getCPUInfo(pattern string) (info string, err error) {
} }
func getCPUVariant() string { func getCPUVariant() string {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
// Windows only supports v7 for ARM32 and v8 for ARM64 and so we can use // Windows/Darwin only supports v7 for ARM32 and v8 for ARM64 and so we can use
// runtime.GOARCH to determine the variants // runtime.GOARCH to determine the variants
var variant string var variant string
switch runtime.GOARCH { switch runtime.GOARCH {
@ -96,16 +96,21 @@ func getCPUVariant() string {
return "" return ""
} }
switch variant { switch strings.ToLower(variant) {
case "8", "AArch64": case "8", "aarch64":
variant = "v8" // special case: if running a 32-bit userspace on aarch64, the variant should be "v7"
case "7", "7M", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)": if runtime.GOARCH == "arm" {
variant = "v7" variant = "v7"
case "6", "6TEJ": } else {
variant = "v8"
}
case "7", "7m", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)":
variant = "v7"
case "6", "6tej":
variant = "v6" variant = "v6"
case "5", "5T", "5TE", "5TEJ": case "5", "5t", "5te", "5tej":
variant = "v5" variant = "v5"
case "4", "4T": case "4", "4t":
variant = "v4" variant = "v4"
case "3": case "3":
variant = "v3" variant = "v3"

View File

@ -124,7 +124,7 @@ func (r Spec) Hostname() string {
i := strings.Index(r.Locator, "/") i := strings.Index(r.Locator, "/")
if i < 0 { if i < 0 {
i = len(r.Locator) + 1 return r.Locator
} }
return r.Locator[:i] return r.Locator[:i]
} }

View File

@ -1,91 +1,105 @@
github.com/containerd/go-runc e029b79d8cda8374981c64eba71f28ec38e5526f github.com/beorn7/perks v1.0.1
github.com/containerd/console 0650fd9eeb50bab4fc99dceb9f2e14cf58f36e7f
github.com/containerd/cgroups c4b9ac5c7601384c965b9646fc515884e091ebb9
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containerd/fifo bda0ff6ed73c67bfb5e62bc9c697f146b7fd7f13
github.com/containerd/btrfs af5082808c833de0e79c1e72eea9fea239364877
github.com/containerd/continuity f2a389ac0a02ce21c09edd7344677a601970f41c
github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/docker/go-units v0.4.0
github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f
github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823
github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563
github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
github.com/matttproud/golang_protobuf_extensions v1.0.1
github.com/gogo/protobuf v1.2.1
github.com/gogo/googleapis v1.2.0
github.com/golang/protobuf v1.2.0
github.com/opencontainers/runtime-spec 29686dbc5559d93fb1ef402eeda3e35c38d75af4 # v1.0.1-59-g29686db
github.com/opencontainers/runc dc9208a3303feef5b3839f4323d9beb36df0a9dd # v1.0.0-rc10
github.com/konsorten/go-windows-terminal-sequences v1.0.1
github.com/sirupsen/logrus v1.4.1
github.com/urfave/cli v1.22.0
golang.org/x/net f3200d17e092c607f615320ecaad13d87ad9a2b3
google.golang.org/grpc 6eaf6f47437a6b4e2153a190160ef39a92c7eceb # v1.23.0
github.com/pkg/errors v0.8.1
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
golang.org/x/sys 9eafafc0a87e0fd0aeeba439a4573537970c44c7 https://github.com/golang/sys
github.com/opencontainers/image-spec v1.0.1
golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 github.com/cespare/xxhash/v2 v2.1.1
github.com/Microsoft/go-winio v0.4.14 github.com/containerd/btrfs 404b9149801e455c8076f615b06dc0abee0a977a
github.com/Microsoft/hcsshim 9e921883ac929bbe515b39793ece99ce3a9d7706 github.com/containerd/cgroups 318312a373405e5e91134d8063d04d59768a1bff
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 github.com/containerd/console v1.0.0
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 github.com/containerd/continuity efbc4488d8fe1bdc16bde3b2d2990d9b3a899165
github.com/containerd/ttrpc 92c8520ef9f86600c650dd540266a007bf03670f github.com/containerd/fifo f15a3290365b9d2627d189e619ab4008e0069caf
github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2 github.com/containerd/go-runc 7016d3ce2328dd2cb1192b2076ebd565c4e8df0c
gotest.tools v2.3.0 github.com/containerd/ttrpc v1.0.1
github.com/containerd/typeurl v1.0.1
github.com/coreos/go-systemd/v22 v22.1.0
github.com/cpuguy83/go-md2man/v2 v2.0.0
github.com/docker/go-events e31b211e4f1cd09aa76fe4ac244571fab96ae47f
github.com/docker/go-metrics v0.0.1
github.com/docker/go-units v0.4.0
github.com/godbus/dbus/v5 v5.0.3
github.com/gogo/googleapis v1.3.2
github.com/gogo/protobuf v1.3.1
github.com/golang/protobuf v1.3.5
github.com/google/go-cmp v0.2.0 github.com/google/go-cmp v0.2.0
go.etcd.io/bbolt v1.3.3 github.com/google/uuid v1.1.1
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/hashicorp/errwrap v1.0.0 github.com/hashicorp/errwrap v1.0.0
github.com/hashicorp/go-multierror v1.0.0 github.com/hashicorp/go-multierror v1.0.0
github.com/hashicorp/golang-lru v0.5.3 github.com/hashicorp/golang-lru v0.5.3
go.opencensus.io v0.22.0
github.com/imdario/mergo v0.3.7 github.com/imdario/mergo v0.3.7
github.com/cpuguy83/go-md2man v1.0.10 github.com/konsorten/go-windows-terminal-sequences v1.0.3
github.com/russross/blackfriday v1.5.2 github.com/matttproud/golang_protobuf_extensions v1.0.1
github.com/Microsoft/go-winio v0.4.14
github.com/Microsoft/hcsshim v0.8.10
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc v1.0.0-rc92
github.com/opencontainers/runtime-spec 4d89ac9fbff6c455f46a5bb59c6b1bb7184a5e43 # v1.0.3-0.20200728170252-4d89ac9fbff6
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.6.0
github.com/prometheus/client_model v0.2.0
github.com/prometheus/common v0.9.1
github.com/prometheus/procfs v0.0.11
github.com/russross/blackfriday/v2 v2.0.1
github.com/shurcooL/sanitized_anchor_name v1.0.0
github.com/sirupsen/logrus v1.6.0
github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2
github.com/urfave/cli v1.22.1 # NOTE: urfave/cli must be <= v1.22.1 due to a regression: https://github.com/urfave/cli/issues/1092
go.etcd.io/bbolt v1.3.5
go.opencensus.io v0.22.0
golang.org/x/net ab34263943818b32f575efc978a3d24e80b04bd7
golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
golang.org/x/sys ed371f2e16b4b305ee99df548828de367527b76b
golang.org/x/text v0.3.3
google.golang.org/genproto e50cd9704f63023d62cd06a1994b98227fc4d21a
google.golang.org/grpc v1.27.1
gotest.tools/v3 v3.0.2
# cgroups dependencies
github.com/cilium/ebpf 1c8d4c9ef7759622653a1d319284a44652333b28
# cri dependencies # cri dependencies
github.com/containerd/cri 50b9e10ea54a9b57049fe311e4fe0a96277ef1c2 # release/1.3 github.com/containerd/cri adc0b6a578ed6f646bb24c1c639d65b70e14cccc # release/1.4
github.com/containerd/go-cni 49fbd9b210f3c8ee3b7fd3cd797aabaf364627c1
github.com/containernetworking/cni v0.7.1
github.com/containernetworking/plugins v0.7.6
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580 github.com/docker/docker 4634ce647cf2ce2c6031129ccd109e557244986f
github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528 github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
github.com/emicklei/go-restful v2.9.5 github.com/emicklei/go-restful v2.9.5
github.com/google/gofuzz v1.0.0 github.com/go-logr/logr v0.2.0
github.com/json-iterator/go v1.1.8 github.com/google/gofuzz v1.1.0
github.com/modern-go/reflect2 1.0.1 github.com/json-iterator/go v1.1.10
github.com/modern-go/concurrent 1.0.3 github.com/modern-go/concurrent 1.0.3
github.com/opencontainers/selinux 5215b1806f52b1fcc2070a8826c542c9d33cd3cf github.com/modern-go/reflect2 v1.0.1
github.com/seccomp/libseccomp-golang v0.9.1 github.com/opencontainers/selinux v1.6.0
github.com/tchap/go-patricia v2.2.6 github.com/tchap/go-patricia v2.2.6
golang.org/x/crypto 69ecbb4d6d5dab05e49161c6e77ea40a030884e1 github.com/willf/bitset v1.1.11
golang.org/x/oauth2 0f29369cfe4552d0e4bcddc57cc75f4d7e672a33 golang.org/x/crypto 75b288015ac94e66e3d6715fb68a9b41bf046ec2
golang.org/x/time 9d24e82272b4f38b78bc8cff74fa936d31ccd8ef golang.org/x/oauth2 858c2ad4c8b6c5d10852cb89079f6ca1c7309787
golang.org/x/time 555d28b269f0569763d25dbe1a237ae74c6bcc82
gopkg.in/inf.v0 v0.9.1 gopkg.in/inf.v0 v0.9.1
gopkg.in/yaml.v2 53403b58ad1b561927d19068c655246f2db79d48 # v2.2.8 gopkg.in/yaml.v2 v2.2.8
k8s.io/api kubernetes-1.16.6 k8s.io/api v0.19.4
k8s.io/apimachinery kubernetes-1.16.6 k8s.io/apimachinery v0.19.4
k8s.io/apiserver kubernetes-1.16.6 k8s.io/apiserver v0.19.4
k8s.io/cri-api kubernetes-1.16.6 k8s.io/client-go v0.19.4
k8s.io/client-go kubernetes-1.16.6 k8s.io/cri-api v0.19.4
k8s.io/klog v1.0.0 k8s.io/klog/v2 v2.2.0
k8s.io/kubernetes v1.16.6 k8s.io/utils d5654de09c73da55eb19ae4ab4f734f7a61747a6
k8s.io/utils e782cd3c129fc98ee807f3c889c0f26eb7c9daf5 sigs.k8s.io/structured-merge-diff/v4 v4.0.1
sigs.k8s.io/yaml v1.1.0 sigs.k8s.io/yaml v1.2.0
# cni dependencies
github.com/containerd/go-cni v1.0.1
github.com/containernetworking/cni v0.8.0
github.com/containernetworking/plugins v0.8.6
github.com/fsnotify/fsnotify v1.4.9
# image decrypt depedencies
github.com/containerd/imgcrypt v1.0.1
github.com/containers/ocicrypt v1.0.1
github.com/fullsailor/pkcs7 8306686428a5fe132eac8cb7c4848af725098bd4
gopkg.in/square/go-jose.v2 v2.3.1
# zfs dependencies # zfs dependencies
github.com/containerd/zfs 2ceb2dbb8154202ed1b8fd32e4ea25b491d7b251 github.com/containerd/zfs 9abf673ca6ff9ab8d9bd776a4ceff8f6dc699c3d
github.com/mistifyio/go-zfs f784269be439d704d3dfa1906f45dd848fed2beb github.com/mistifyio/go-zfs f784269be439d704d3dfa1906f45dd848fed2beb
github.com/google/uuid v1.1.1
# aufs dependencies # aufs dependencies
github.com/containerd/aufs f894a800659b6e11c1a13084abd1712f346e349c github.com/containerd/aufs 371312c1e31c210a21e49bf3dfd3f31729ed9f2f

View File

@ -1,50 +0,0 @@
dist: bionic
language: go
go_import_path: github.com/godbus/dbus
go:
- 1.11.x
- 1.12.x
- 1.13.x
- tip
matrix:
fast_finish: true
allow_failures:
- go: tip
addons:
apt:
packages:
- dbus
- dbus-x11
before_install:
- export GO111MODULE=on
script:
- go test -v -race -mod=readonly ./... # Run all the tests with the race detector enabled
- go vet ./... # go vet is the official Go static analyzer
jobs:
include:
# The build matrix doesn't cover build stages, so manually expand
# the jobs with anchors
- &multiarch
stage: "Multiarch Test"
go: 1.11.x
env: TARGETS="386 arm arm64 ppc64le"
before_install:
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
script:
- |
set -e
for target in $TARGETS; do
printf "\e[1mRunning test suite under ${target}.\e[0m\n"
GOARCH="$target" go test -v ./...
printf "\n\n"
done
- <<: *multiarch
go: 1.12.x
- <<: *multiarch
go: 1.13.x

View File

@ -1,50 +0,0 @@
# How to Contribute
## Getting Started
- Fork the repository on GitHub
- Read the [README](README.markdown) for build and test instructions
- Play with the project, submit bugs, submit patches!
## Contribution Flow
This is a rough outline of what a contributor's workflow looks like:
- Create a topic branch from where you want to base your work (usually master).
- Make commits of logical units.
- Make sure your commit messages are in the proper format (see below).
- Push your changes to a topic branch in your fork of the repository.
- Make sure the tests pass, and add any new tests as appropriate.
- Submit a pull request to the original repository.
Thanks for your contributions!
### Format of the Commit Message
We follow a rough convention for commit messages that is designed to answer two
questions: what changed and why. The subject line should feature the what and
the body of the commit should describe the why.
```
scripts: add the test-cluster command
this uses tmux to setup a test cluster that you can easily kill and
start for debugging.
Fixes #38
```
The format can be described more formally as follows:
```
<subsystem>: <what changed>
<BLANK LINE>
<why this change was made>
<BLANK LINE>
<footer>
```
The first line is the subject and should be no longer than 70 characters, the
second line is always blank, and other lines should be wrapped at 80 characters.
This allows the message to be easier to read on GitHub as well as in various
git tools.

View File

@ -1,3 +0,0 @@
Brandon Philips <brandon@ifup.org> (@philips)
Brian Waldon <brian@waldon.cc> (@bcwaldon)
John Southworth <jsouthwo@brocade.com> (@jsouthworth)

View File

@ -1,25 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/godbus/dbus/v5"
)
func main() {
conn, err := dbus.SystemBus()
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to connect to SystemBus bus:", err)
os.Exit(1)
}
var s string
err = conn.Object("org.bluez", "/").Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&s)
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to introspect bluez", err)
os.Exit(1)
}
fmt.Println(s)
}

View File

@ -1,31 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/godbus/dbus/v5"
)
func main() {
conn, err := dbus.SessionBus()
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to connect to session bus:", err)
os.Exit(1)
}
for _, v := range []string{"method_call", "method_return", "error", "signal"} {
call := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
"eavesdrop='true',type='"+v+"'")
if call.Err != nil {
fmt.Fprintln(os.Stderr, "Failed to add match:", call.Err)
os.Exit(1)
}
}
c := make(chan *dbus.Message, 10)
conn.Eavesdrop(c)
fmt.Println("Listening for everything")
for v := range c {
fmt.Println(v)
}
}

View File

@ -1,22 +0,0 @@
package main
import (
"encoding/json"
"os"
"github.com/godbus/dbus/v5"
"github.com/godbus/dbus/v5/introspect"
)
func main() {
conn, err := dbus.SessionBus()
if err != nil {
panic(err)
}
node, err := introspect.Call(conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus"))
if err != nil {
panic(err)
}
data, _ := json.MarshalIndent(node, "", " ")
os.Stdout.Write(data)
}

View File

@ -1,28 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/godbus/dbus/v5"
)
func main() {
conn, err := dbus.SessionBus()
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to connect to session bus:", err)
os.Exit(1)
}
var s []string
err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&s)
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to get list of owned names:", err)
os.Exit(1)
}
fmt.Println("Currently owned names on the session bus:")
for _, v := range s {
fmt.Println(v)
}
}

View File

@ -1,37 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/godbus/dbus/v5"
)
func main() {
conn, err := dbus.SessionBus()
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to connect to session bus:", err)
os.Exit(1)
}
var rules = []string{
"type='signal',member='Notify',path='/org/freedesktop/Notifications',interface='org.freedesktop.Notifications'",
"type='method_call',member='Notify',path='/org/freedesktop/Notifications',interface='org.freedesktop.Notifications'",
"type='method_return',member='Notify',path='/org/freedesktop/Notifications',interface='org.freedesktop.Notifications'",
"type='error',member='Notify',path='/org/freedesktop/Notifications',interface='org.freedesktop.Notifications'",
}
var flag uint = 0
call := conn.BusObject().Call("org.freedesktop.DBus.Monitoring.BecomeMonitor", 0, rules, flag)
if call.Err != nil {
fmt.Fprintln(os.Stderr, "Failed to become monitor:", call.Err)
os.Exit(1)
}
c := make(chan *dbus.Message, 10)
conn.Eavesdrop(c)
fmt.Println("Monitoring notifications")
for v := range c {
fmt.Println(v)
}
}

View File

@ -1,17 +0,0 @@
package main
import "github.com/godbus/dbus/v5"
func main() {
conn, err := dbus.SessionBus()
if err != nil {
panic(err)
}
obj := conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
call := obj.Call("org.freedesktop.Notifications.Notify", 0, "", uint32(0),
"", "Test", "This is a test of the DBus bindings for go.", []string{},
map[string]dbus.Variant{}, int32(5000))
if call.Err != nil {
panic(call.Err)
}
}

View File

@ -1,69 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/godbus/dbus/v5"
"github.com/godbus/dbus/v5/introspect"
"github.com/godbus/dbus/v5/prop"
)
type foo string
func (f foo) Foo() (string, *dbus.Error) {
fmt.Println(f)
return string(f), nil
}
func main() {
conn, err := dbus.SessionBus()
if err != nil {
panic(err)
}
reply, err := conn.RequestName("com.github.guelfey.Demo",
dbus.NameFlagDoNotQueue)
if err != nil {
panic(err)
}
if reply != dbus.RequestNameReplyPrimaryOwner {
fmt.Fprintln(os.Stderr, "name already taken")
os.Exit(1)
}
propsSpec := map[string]map[string]*prop.Prop{
"com.github.guelfey.Demo": {
"SomeInt": {
int32(0),
true,
prop.EmitTrue,
func(c *prop.Change) *dbus.Error {
fmt.Println(c.Name, "changed to", c.Value)
return nil
},
},
},
}
f := foo("Bar")
conn.Export(f, "/com/github/guelfey/Demo", "com.github.guelfey.Demo")
props := prop.New(conn, "/com/github/guelfey/Demo", propsSpec)
n := &introspect.Node{
Name: "/com/github/guelfey/Demo",
Interfaces: []introspect.Interface{
introspect.IntrospectData,
prop.IntrospectData,
{
Name: "com.github.guelfey.Demo",
Methods: introspect.Methods(f),
Properties: props.Introspection("com.github.guelfey.Demo"),
},
},
}
conn.Export(introspect.NewIntrospectable(n), "/com/github/guelfey/Demo",
"org.freedesktop.DBus.Introspectable")
fmt.Println("Listening on com.github.guelfey.Demo / /com/github/guelfey/Demo ...")
c := make(chan *dbus.Signal)
conn.Signal(c)
for _ = range c {
}
}

View File

@ -1,48 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/godbus/dbus/v5"
"github.com/godbus/dbus/v5/introspect"
)
const intro = `
<node>
<interface name="com.github.guelfey.Demo">
<method name="Foo">
<arg direction="out" type="s"/>
</method>
</interface>` + introspect.IntrospectDataString + `</node> `
type foo string
func (f foo) Foo() (string, *dbus.Error) {
fmt.Println(f)
return string(f), nil
}
func main() {
conn, err := dbus.SessionBus()
if err != nil {
panic(err)
}
f := foo("Bar!")
conn.Export(f, "/com/github/guelfey/Demo", "com.github.guelfey.Demo")
conn.Export(introspect.Introspectable(intro), "/com/github/guelfey/Demo",
"org.freedesktop.DBus.Introspectable")
reply, err := conn.RequestName("com.github.guelfey.Demo",
dbus.NameFlagDoNotQueue)
if err != nil {
panic(err)
}
if reply != dbus.RequestNameReplyPrimaryOwner {
fmt.Fprintln(os.Stderr, "name already taken")
os.Exit(1)
}
fmt.Println("Listening on com.github.guelfey.Demo / /com/github/guelfey/Demo ...")
select {}
}

View File

@ -1,25 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/godbus/dbus/v5"
)
func main() {
conn, err := dbus.SessionBus()
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to connect to session bus:", err)
os.Exit(1)
}
conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
"type='signal',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',sender='org.freedesktop.DBus'")
c := make(chan *dbus.Signal, 10)
conn.Signal(c)
for v := range c {
fmt.Println(v)
}
}

View File

@ -1,58 +0,0 @@
package main
import (
"fmt"
"net"
"os"
"github.com/godbus/dbus/v5"
)
// In order to enable TCP connections add the following configuration
// file to /etc/dbus-1/system.d or /etc/dbus-1/session.d, the location
// depends on your OS (you may update your /etc/dbus-1/session.conf instead):
//
// <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
// "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
// <busconfig>
// <listen>tcp:host=localhost,bind=*,port=12345,family=ipv4</listen>
// <listen>unix:tmpdir=/tmp</listen>
// <auth>ANONYMOUS</auth>
// <allow_anonymous/>
// </busconfig>
//
// If you're using systemd you may also need to reconfigure dbus.socket,
// say put this into /etc/systemd/system/dbus.socket.d/overrides.conf:
//
// [Socket]
// ListenStream=/var/run/dbus/system_bus_socket
// ListenStream=12345
//
// Reboot.
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "usage: %s DBUS_TCP_ADDRESS\n", os.Args[0])
os.Exit(1)
}
if err := run(os.Args[1]); err != nil {
fmt.Fprintf(os.Stderr, "error: %s\n", err)
os.Exit(2)
}
fmt.Println("ok")
}
func run(addr string) error {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return err
}
conn, err := dbus.Dial("tcp:host=" + host + ",port=" + port)
if err != nil {
return err
}
if err = conn.Auth([]dbus.Auth{dbus.AuthAnonymous()}); err != nil {
return err
}
return conn.Hello()
}

View File

@ -1,560 +0,0 @@
package dbus
import (
"context"
"encoding/binary"
"io"
"io/ioutil"
"log"
"testing"
"time"
)
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 ExampleSystemBusPrivate() {
setupPrivateSystemBus := func() (conn *Conn, err error) {
conn, err = SystemBusPrivate()
if err != nil {
return nil, err
}
if err = conn.Auth(nil); err != nil {
conn.Close()
conn = nil
return
}
if err = conn.Hello(); err != nil {
conn.Close()
conn = nil
}
return conn, nil // success
}
_, _ = setupPrivateSystemBus()
}
func TestSend(t *testing.T) {
bus, err := SessionBus()
if err != nil {
t.Fatal(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)
}
}
func TestFlagNoReplyExpectedSend(t *testing.T) {
bus, err := SessionBus()
if err != nil {
t.Fatal(err)
}
done := make(chan struct{})
go func() {
bus.BusObject().Call("org.freedesktop.DBus.ListNames", FlagNoReplyExpected)
close(done)
}()
select {
case <-done:
case <-time.After(1 * time.Second):
t.Error("Failed to announce that the call was done")
}
}
func TestRemoveSignal(t *testing.T) {
bus, err := NewConn(nil)
if err != nil {
t.Error(err)
}
signals := bus.signalHandler.(*defaultSignalHandler).signals
ch := make(chan *Signal)
ch2 := make(chan *Signal)
for _, ch := range []chan *Signal{ch, ch2, ch, ch2, ch2, ch} {
bus.Signal(ch)
}
signals = bus.signalHandler.(*defaultSignalHandler).signals
if len(signals) != 6 {
t.Errorf("remove signal: signals length not equal: got '%d', want '6'", len(signals))
}
bus.RemoveSignal(ch)
signals = bus.signalHandler.(*defaultSignalHandler).signals
if len(signals) != 3 {
t.Errorf("remove signal: signals length not equal: got '%d', want '3'", len(signals))
}
signals = bus.signalHandler.(*defaultSignalHandler).signals
for _, scd := range signals {
if scd.ch != ch2 {
t.Errorf("remove signal: removed signal present: got '%v', want '%v'", scd.ch, ch2)
}
}
}
type rwc struct {
io.Reader
io.Writer
}
func (rwc) Close() error { return nil }
type fakeAuth struct {
}
func (fakeAuth) FirstData() (name, resp []byte, status AuthStatus) {
return []byte("name"), []byte("resp"), AuthOk
}
func (fakeAuth) HandleData(data []byte) (resp []byte, status AuthStatus) {
return nil, AuthOk
}
func TestCloseBeforeSignal(t *testing.T) {
reader, pipewriter := io.Pipe()
defer pipewriter.Close()
defer reader.Close()
bus, err := NewConn(rwc{Reader: reader, Writer: ioutil.Discard})
if err != nil {
t.Fatal(err)
}
// give ch a buffer so sends won't block
ch := make(chan *Signal, 1)
bus.Signal(ch)
go func() {
_, err := pipewriter.Write([]byte("REJECTED name\r\nOK myuuid\r\n"))
if err != nil {
t.Errorf("error writing to pipe: %v", err)
}
}()
err = bus.Auth([]Auth{fakeAuth{}})
if err != nil {
t.Fatal(err)
}
err = bus.Close()
if err != nil {
t.Fatal(err)
}
msg := &Message{
Type: TypeSignal,
Headers: map[HeaderField]Variant{
FieldInterface: MakeVariant("foo.bar"),
FieldMember: MakeVariant("bar"),
FieldPath: MakeVariant(ObjectPath("/baz")),
},
}
err = msg.EncodeTo(pipewriter, binary.LittleEndian)
if err != nil {
t.Fatal(err)
}
}
func TestCloseChannelAfterRemoveSignal(t *testing.T) {
bus, err := NewConn(nil)
if err != nil {
t.Fatal(err)
}
// Add an unbuffered signal channel
ch := make(chan *Signal)
bus.Signal(ch)
// Send a signal
msg := &Message{
Type: TypeSignal,
Headers: map[HeaderField]Variant{
FieldInterface: MakeVariant("foo.bar"),
FieldMember: MakeVariant("bar"),
FieldPath: MakeVariant(ObjectPath("/baz")),
},
}
bus.handleSignal(msg)
// Remove and close the signal channel
bus.RemoveSignal(ch)
close(ch)
}
func TestAddAndRemoveMatchSignal(t *testing.T) {
conn, err := SessionBusPrivate()
if err != nil {
t.Fatal(err)
}
defer conn.Close()
if err = conn.Auth(nil); err != nil {
t.Fatal(err)
}
if err = conn.Hello(); err != nil {
t.Fatal(err)
}
sigc := make(chan *Signal, 1)
conn.Signal(sigc)
// subscribe to a made up signal name and emit one of the type
if err = conn.AddMatchSignal(
WithMatchInterface("org.test"),
WithMatchMember("Test"),
); err != nil {
t.Fatal(err)
}
if err = conn.Emit("/", "org.test.Test"); err != nil {
t.Fatal(err)
}
if sig := waitSignal(sigc, "org.test.Test", time.Second); sig == nil {
t.Fatal("signal receive timed out")
}
// unsubscribe from the signal and check that is not delivered anymore
if err = conn.RemoveMatchSignal(
WithMatchInterface("org.test"),
WithMatchMember("Test"),
); err != nil {
t.Fatal(err)
}
if err = conn.Emit("/", "org.test.Test"); err != nil {
t.Fatal(err)
}
if sig := waitSignal(sigc, "org.test.Test", time.Second); sig != nil {
t.Fatalf("unsubscribed from %q signal, but received %#v", "org.test.Test", sig)
}
}
func waitSignal(sigc <-chan *Signal, name string, timeout time.Duration) *Signal {
for {
select {
case sig := <-sigc:
if sig.Name == name {
return sig
}
case <-time.After(timeout):
return nil
}
}
}
type server struct{}
func (server) Double(i int64) (int64, *Error) {
return 2 * i, nil
}
func BenchmarkCall(b *testing.B) {
b.StopTimer()
b.ReportAllocs()
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()
b.ReportAllocs()
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
}
func TestGetKey(t *testing.T) {
keys := "host=1.2.3.4,port=5678,family=ipv4"
if host := getKey(keys, "host"); host != "1.2.3.4" {
t.Error(`Expected "1.2.3.4", got`, host)
}
if port := getKey(keys, "port"); port != "5678" {
t.Error(`Expected "5678", got`, port)
}
if family := getKey(keys, "family"); family != "ipv4" {
t.Error(`Expected "ipv4", got`, family)
}
}
func TestInterceptors(t *testing.T) {
conn, err := SessionBusPrivate(
WithIncomingInterceptor(func(msg *Message) {
log.Println("<", msg)
}),
WithOutgoingInterceptor(func(msg *Message) {
log.Println(">", msg)
}),
)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
if err = conn.Auth(nil); err != nil {
t.Fatal(err)
}
if err = conn.Hello(); err != nil {
t.Fatal(err)
}
}
func TestCloseCancelsConnectionContext(t *testing.T) {
bus, err := SessionBusPrivate()
if err != nil {
t.Fatal(err)
}
defer bus.Close()
if err = bus.Auth(nil); err != nil {
t.Fatal(err)
}
if err = bus.Hello(); err != nil {
t.Fatal(err)
}
if err != nil {
t.Fatal(err)
}
// The context is not done at this point
ctx := bus.Context()
select {
case <-ctx.Done():
t.Fatal("context should not be done")
default:
}
err = bus.Close()
if err != nil {
t.Fatal(err)
}
select {
case <-ctx.Done():
// expected
case <-time.After(5 * time.Second):
t.Fatal("context is not done after connection closed")
}
}
func TestDisconnectCancelsConnectionContext(t *testing.T) {
reader, pipewriter := io.Pipe()
defer pipewriter.Close()
defer reader.Close()
bus, err := NewConn(rwc{Reader: reader, Writer: ioutil.Discard})
if err != nil {
t.Fatal(err)
}
go func() {
_, err := pipewriter.Write([]byte("REJECTED name\r\nOK myuuid\r\n"))
if err != nil {
t.Errorf("error writing to pipe: %v", err)
}
}()
err = bus.Auth([]Auth{fakeAuth{}})
if err != nil {
t.Fatal(err)
}
ctx := bus.Context()
pipewriter.Close()
select {
case <-ctx.Done():
// expected
case <-time.After(5 * time.Second):
t.Fatal("context is not done after connection closed")
}
}
func TestCancellingContextClosesConnection(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
reader, pipewriter := io.Pipe()
defer pipewriter.Close()
defer reader.Close()
bus, err := NewConn(rwc{Reader: reader, Writer: ioutil.Discard}, WithContext(ctx))
if err != nil {
t.Fatal(err)
}
go func() {
_, err := pipewriter.Write([]byte("REJECTED name\r\nOK myuuid\r\n"))
if err != nil {
t.Errorf("error writing to pipe: %v", err)
}
}()
err = bus.Auth([]Auth{fakeAuth{}})
if err != nil {
t.Fatal(err)
}
// Cancel the connection's parent context and give time for
// other goroutines to schedule.
cancel()
time.Sleep(50 * time.Millisecond)
err = bus.BusObject().Call("org.freedesktop.DBus.Peer.Ping", 0).Store()
if err != ErrClosed {
t.Errorf("expected connection to be closed, but got: %v", err)
}
}

View File

@ -1,24 +0,0 @@
package dbus
import (
"testing"
)
type TestStruct struct {
TestInt int
TestStr string
}
func Test_VariantOfStruct(t *testing.T) {
tester := TestStruct{TestInt: 123, TestStr: "foobar"}
testerDecoded := []interface{}{123, "foobar"}
variant := MakeVariant(testerDecoded)
input := []interface{}{variant}
var output TestStruct
if err := Store(input, &output); err != nil {
t.Fatal(err)
}
if tester != output {
t.Fatalf("%v != %v\n", tester, output)
}
}

View File

@ -1,88 +0,0 @@
package dbus
import (
"bytes"
"encoding/binary"
"testing"
)
type pixmap struct {
Width int
Height int
Pixels []uint8
}
type property struct {
IconName string
Pixmaps []pixmap
Title string
Description string
}
func TestDecodeArrayEmptyStruct(t *testing.T) {
buf := bytes.NewBuffer(nil)
msg := &Message{
Type: 0x02,
Flags: 0x00,
Headers: map[HeaderField]Variant{
0x06: Variant{
sig: Signature{
str: "s",
},
value: ":1.391",
},
0x05: Variant{
sig: Signature{
str: "u",
},
value: uint32(2),
},
0x08: Variant{
sig: Signature{
str: "g",
},
value: Signature{
str: "v",
},
},
},
Body: []interface{}{
Variant{
sig: Signature{
str: "(sa(iiay)ss)",
},
value: property{
IconName: "iconname",
Pixmaps: []pixmap{},
Title: "title",
Description: "description",
},
},
},
serial: 0x00000003,
}
err := msg.EncodeTo(buf, binary.LittleEndian)
if err != nil {
t.Fatal(err)
}
msg, err = DecodeMessage(buf)
if err != nil {
t.Fatal(err)
}
}
func TestSigByteSize(t *testing.T) {
for sig, want := range map[string]int{
"b": 4,
"t": 8,
"(yy)": 2,
"(y(uu))": 9,
"(y(xs))": 0,
"s": 0,
"ao": 0,
} {
if have := sigByteSize(sig); have != want {
t.Errorf("sigByteSize(%q) = %d, want %d", sig, have, want)
}
}
}

View File

@ -1,414 +0,0 @@
package dbus
import (
"bytes"
"encoding/binary"
"reflect"
"testing"
)
func TestEncodeArrayOfMaps(t *testing.T) {
tests := []struct {
name string
vs []interface{}
}{
{
"aligned at 8 at start of array",
[]interface{}{
"12345",
[]map[string]Variant{
{
"abcdefg": MakeVariant("foo"),
"cdef": MakeVariant(uint32(2)),
},
},
},
},
{
"not aligned at 8 for start of array",
[]interface{}{
"1234567890",
[]map[string]Variant{
{
"abcdefg": MakeVariant("foo"),
"cdef": MakeVariant(uint32(2)),
},
},
},
},
}
for _, order := range []binary.ByteOrder{binary.LittleEndian, binary.BigEndian} {
for _, tt := range tests {
buf := new(bytes.Buffer)
enc := newEncoder(buf, order)
enc.Encode(tt.vs...)
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(tt.vs...))
if err != nil {
t.Errorf("%q: decode (%v) failed: %v", tt.name, order, err)
continue
}
if !reflect.DeepEqual(v, tt.vs) {
t.Errorf("%q: (%v) not equal: got '%v', want '%v'", tt.name, order, v, tt.vs)
continue
}
}
}
}
func TestEncodeMapStringInterface(t *testing.T) {
val := map[string]interface{}{"foo": "bar"}
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
out := map[string]interface{}{}
Store(v, &out)
if !reflect.DeepEqual(out, val) {
t.Errorf("not equal: got '%v', want '%v'",
out, val)
}
}
type empty interface{}
func TestEncodeMapStringNamedInterface(t *testing.T) {
val := map[string]empty{"foo": "bar"}
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
out := map[string]empty{}
Store(v, &out)
if !reflect.DeepEqual(out, val) {
t.Errorf("not equal: got '%v', want '%v'",
out, val)
}
}
type fooer interface {
Foo()
}
type fooimpl string
func (fooimpl) Foo() {}
func TestEncodeMapStringNonEmptyInterface(t *testing.T) {
val := map[string]fooer{"foo": fooimpl("bar")}
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
out := map[string]fooer{}
err = Store(v, &out)
if err == nil {
t.Fatal("Shouldn't be able to convert to non empty interfaces")
}
}
func TestEncodeSliceInterface(t *testing.T) {
val := []interface{}{"foo", "bar"}
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
out := []interface{}{}
Store(v, &out)
if !reflect.DeepEqual(out, val) {
t.Errorf("not equal: got '%v', want '%v'",
out, val)
}
}
func TestEncodeSliceNamedInterface(t *testing.T) {
val := []empty{"foo", "bar"}
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
out := []empty{}
Store(v, &out)
if !reflect.DeepEqual(out, val) {
t.Errorf("not equal: got '%v', want '%v'",
out, val)
}
}
func TestEncodeNestedInterface(t *testing.T) {
val := map[string]interface{}{
"foo": []interface{}{"1", "2", "3", "5",
map[string]interface{}{
"bar": "baz",
},
},
"bar": map[string]interface{}{
"baz": "quux",
"quux": "quuz",
},
}
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
out := map[string]interface{}{}
Store(v, &out)
if !reflect.DeepEqual(out, val) {
t.Errorf("not equal: got '%#v', want '%#v'",
out, val)
}
}
func TestEncodeInt(t *testing.T) {
val := 10
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
var out int
Store(v, &out)
if !reflect.DeepEqual(out, val) {
t.Errorf("not equal: got '%v', want '%v'",
out, val)
}
}
func TestEncodeIntToNonCovertible(t *testing.T) {
val := 150
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
var out bool
err = Store(v, &out)
if err == nil {
t.Logf("%t\n", out)
t.Fatal("Type mismatch should have occured")
}
}
func TestEncodeUint(t *testing.T) {
val := uint(10)
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
var out uint
Store(v, &out)
if !reflect.DeepEqual(out, val) {
t.Errorf("not equal: got '%v', want '%v'",
out, val)
}
}
func TestEncodeUintToNonCovertible(t *testing.T) {
val := uint(10)
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
var out bool
err = Store(v, &out)
if err == nil {
t.Fatal("Type mismatch should have occured")
}
}
type boolean bool
func TestEncodeOfAssignableType(t *testing.T) {
val := boolean(true)
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(val)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(val))
if err != nil {
t.Fatal(err)
}
var out boolean
err = Store(v, &out)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(out, val) {
t.Errorf("not equal: got '%v', want '%v'",
out, val)
}
}
func TestEncodeVariant(t *testing.T) {
var res map[ObjectPath]map[string]map[string]Variant
var src = map[ObjectPath]map[string]map[string]Variant{
ObjectPath("/foo/bar"): {
"foo": {
"bar": MakeVariant(10),
"baz": MakeVariant("20"),
},
},
}
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(src)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(src))
if err != nil {
t.Fatal(err)
}
err = Store(v, &res)
if err != nil {
t.Fatal(err)
}
_ = res[ObjectPath("/foo/bar")]["foo"]["baz"].Value().(string)
}
func TestEncodeVariantToList(t *testing.T) {
var res map[string]Variant
var src = map[string]interface{}{
"foo": []interface{}{"a", "b", "c"},
}
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(src)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(src))
if err != nil {
t.Fatal(err)
}
err = Store(v, &res)
if err != nil {
t.Fatal(err)
}
_ = res["foo"].Value().([]Variant)
}
func TestEncodeVariantToUint64(t *testing.T) {
var res map[string]Variant
var src = map[string]interface{}{
"foo": uint64(10),
}
buf := new(bytes.Buffer)
order := binary.LittleEndian
enc := newEncoder(buf, binary.LittleEndian)
err := enc.Encode(src)
if err != nil {
t.Fatal(err)
}
dec := newDecoder(buf, order)
v, err := dec.Decode(SignatureOf(src))
if err != nil {
t.Fatal(err)
}
err = Store(v, &res)
if err != nil {
t.Fatal(err)
}
_ = res["foo"].Value().(uint64)
}

View File

@ -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
}
}

View File

@ -1,61 +0,0 @@
package dbus
import (
"fmt"
"os"
"os/exec"
"strconv"
"testing"
)
// How to mock exec.Command for unit tests
// https://stackoverflow.com/q/45789101/10513533
var mockedExitStatus = 0
var mockedStdout string
func fakeExecCommand(command string, args ...string) *exec.Cmd {
cs := []string{"-test.run=TestExecCommandHelper", "--", command}
cs = append(cs, args...)
cmd := exec.Command(os.Args[0], cs...)
es := strconv.Itoa(mockedExitStatus)
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1",
"STDOUT=" + mockedStdout,
"EXIT_STATUS=" + es}
return cmd
}
func TestExecCommandHelper(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
fmt.Fprintf(os.Stdout, os.Getenv("STDOUT"))
i, _ := strconv.Atoi(os.Getenv("EXIT_STATUS"))
os.Exit(i)
}
func TestDbusLaunchMultilineResponse(t *testing.T) {
mockedExitStatus = 0
mockedStdout = `process 7616: D-Bus library appears to be incorrectly set up; failed to read machine uuid: UUID file '/etc/machine-id' should contain a hex string of length 32, not length 0, with no other text
See the manual page for dbus-uuidgen to correct this issue.
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-0SO9YZUBGA,guid=ac22f2f3b9d228496b4d4b935cae3417
DBUS_SESSION_BUS_PID=7620
DBUS_SESSION_BUS_WINDOWID=16777217`
execCommand = fakeExecCommand
defer func() { execCommand = exec.Command }()
expOut := ""
expErr := "dbus: couldn't determine address of session bus"
out, err := getSessionBusPlatformAddress()
if out != expOut {
t.Errorf("Expected %q, got %q", expOut, out)
}
if err == nil {
t.Error("Excepted error, got none")
} else {
if err.Error() != expErr {
t.Errorf("Expected error to be %q, got %q", expErr, err.Error())
}
}
}

View File

@ -1,635 +0,0 @@
package dbus
import (
"fmt"
"regexp"
"strings"
"testing"
)
type lowerCaseExport struct{}
func (export lowerCaseExport) foo() (string, *Error) {
return "bar", nil
}
type fooExport struct {
message Message
}
func (export *fooExport) Foo(message Message, param string) (string, *Error) {
export.message = message
return "foo", nil
}
type barExport struct{}
func (export barExport) Foo(param string) (string, *Error) {
return "bar", nil
}
type badExport struct{}
func (export badExport) Foo(param string) string {
return "bar"
}
// Test typical Export usage.
func TestExport(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
connection.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
object := connection.Object(name, "/org/guelfey/DBus/Test")
subtreeObject := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
var response int64
err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Double: %s", err)
}
if response != 4 {
t.Errorf("Response was %d, expected 4", response)
}
// Verify that calling a subtree of a regular export does not result in a
// valid method call.
err = subtreeObject.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
if err == nil {
t.Error("Expected error due to no object being exported on that path")
}
// Now remove export
connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
if err == nil {
t.Error("Expected an error since the export was removed")
}
}
// Test that Exported handlers can obtain raw message.
func TestExport_message(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
export := &fooExport{}
connection.Export(export, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
object := connection.Object(name, "/org/guelfey/DBus/Test")
var response string
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Foo: %s", err)
}
if response != "foo" {
t.Errorf(`Response was %s, expected "foo"`, response)
}
if export.message.serial == 0 {
t.Error("Expected a valid message to be given to handler")
}
}
// Test Export with an invalid path.
func TestExport_invalidPath(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
err = connection.Export(nil, "foo", "bar")
if err == nil {
t.Error("Expected an error due to exporting with an invalid path")
}
}
// Test Export with an un-exported method. This should not panic, but rather
// result in an invalid method call.
func TestExport_unexportedMethod(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
connection.Export(lowerCaseExport{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
object := connection.Object(name, "/org/guelfey/DBus/Test")
var response string
call := object.Call("org.guelfey.DBus.Test.foo", 0)
err = call.Store(&response)
if err == nil {
t.Errorf("Expected an error due to calling unexported method")
}
}
// Test Export with a method lacking the correct return signature. This should
// result in an invalid method call.
func TestExport_badSignature(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
connection.Export(badExport{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
object := connection.Object(name, "/org/guelfey/DBus/Test")
var response string
call := object.Call("org.guelfey.DBus.Test.Foo", 0)
err = call.Store(&response)
if err == nil {
t.Errorf("Expected an error due to the method lacking the right signature")
}
}
// Test typical ExportWithMap usage.
func TestExportWithMap(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
mapping := make(map[string]string)
mapping["Double"] = "double" // Export this method as lower-case
connection.ExportWithMap(server{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
object := connection.Object(name, "/org/guelfey/DBus/Test")
var response int64
err = object.Call("org.guelfey.DBus.Test.double", 0, int64(2)).Store(&response)
if err != nil {
t.Errorf("Unexpected error calling double: %s", err)
}
if response != 4 {
t.Errorf("Response was %d, expected 4", response)
}
}
// Test that ExportWithMap does not export both method alias and method.
func TestExportWithMap_bypassAlias(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
mapping := make(map[string]string)
mapping["Double"] = "double" // Export this method as lower-case
connection.ExportWithMap(server{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
object := connection.Object(name, "/org/guelfey/DBus/Test")
var response int64
// Call upper-case Double (i.e. the real method, not the alias)
err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
if err == nil {
t.Error("Expected an error due to calling actual method, not alias")
}
}
// Test typical ExportSubtree usage.
func TestExportSubtree(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
export := &fooExport{}
connection.ExportSubtree(export, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
// Call a subpath of the exported path
object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
var response string
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Foo: %s", err)
}
if response != "foo" {
t.Errorf(`Response was %s, expected "foo"`, response)
}
if export.message.serial == 0 {
t.Error("Expected the raw message, got an invalid one")
}
// Now remove export
connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err == nil {
t.Error("Expected an error since the export was removed")
}
}
// Test that using ExportSubtree with exported methods that don't contain a
// Message still work, they just don't get the message.
func TestExportSubtree_noMessage(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
connection.ExportSubtree(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
// Call a subpath of the exported path
object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
var response int64
err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Double: %s", err)
}
if response != 4 {
t.Errorf("Response was %d, expected 4", response)
}
// Now remove export
connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
err = object.Call("org.guelfey.DBus.Test.Double", 0, int64(2)).Store(&response)
if err == nil {
t.Error("Expected an error since the export was removed")
}
}
// Test that a regular Export takes precedence over ExportSubtree.
func TestExportSubtree_exportPrecedence(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
// Register for the entire subtree of /org/guelfey/DBus/Test
connection.ExportSubtree(&fooExport{},
"/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
// Explicitly register for /org/guelfey/DBus/Test/Foo, a subpath of above
connection.Export(&barExport{}, "/org/guelfey/DBus/Test/Foo",
"org.guelfey.DBus.Test")
// Call the explicitly exported path
object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
var response string
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Foo: %s", err)
}
if response != "bar" {
t.Errorf(`Response was %s, expected "bar"`, response)
}
response = "" // Reset response so errors aren't confusing
// Now remove explicit export
connection.Export(nil, "/org/guelfey/DBus/Test/Foo", "org.guelfey.DBus.Test")
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Foo: %s", err)
}
// Now the subtree export should handle the call
if response != "foo" {
t.Errorf(`Response was %s, expected "foo"`, response)
}
}
// Test typical ExportSubtreeWithMap usage.
func TestExportSubtreeWithMap(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
mapping := make(map[string]string)
mapping["Foo"] = "foo" // Export this method as lower-case
connection.ExportSubtreeWithMap(&fooExport{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
// Call a subpath of the exported path
object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
var response string
// Call the lower-case method
err = object.Call("org.guelfey.DBus.Test.foo", 0, "qux").Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Foo: %s", err)
}
if response != "foo" {
t.Errorf(`Response was %s, expected "foo"`, response)
}
// Now remove export
connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
err = object.Call("org.guelfey.DBus.Test.foo", 0, "qux").Store(&response)
if err == nil {
t.Error("Expected an error since the export was removed")
}
}
// Test that ExportSubtreeWithMap does not export both method alias and method.
func TestExportSubtreeWithMap_bypassAlias(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
mapping := make(map[string]string)
mapping["Foo"] = "foo" // Export this method as lower-case
connection.ExportSubtreeWithMap(&fooExport{}, mapping, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
var response string
// Call upper-case Foo (i.e. the real method, not the alias)
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err == nil {
t.Error("Expected an error due to calling actual method, not alias")
}
}
func TestExportMethodTable(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
export := &fooExport{}
tbl := make(map[string]interface{})
tbl["Foo"] = func(message Message, param string) (string, *Error) {
return export.Foo(message, param)
}
tbl["Foo2"] = export.Foo
connection.ExportMethodTable(tbl, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
object := connection.Object(name, "/org/guelfey/DBus/Test")
var response string
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Foo: %s", err)
}
if response != "foo" {
t.Errorf(`Response was %s, expected "foo"`, response)
}
if export.message.serial == 0 {
t.Error("Expected the raw message, got an invalid one")
}
err = object.Call("org.guelfey.DBus.Test.Foo2", 0, "qux").Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Foo: %s", err)
}
if response != "foo" {
t.Errorf(`Response was %s, expected "foo"`, response)
}
if export.message.serial == 0 {
t.Error("Expected the raw message, got an invalid one")
}
// Now remove export
connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err == nil {
t.Error("Expected an error since the export was removed")
}
}
func TestExportSubtreeMethodTable(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
export := &fooExport{}
tbl := make(map[string]interface{})
tbl["Foo"] = func(message Message, param string) (string, *Error) {
return export.Foo(message, param)
}
tbl["Foo2"] = export.Foo
connection.ExportSubtreeMethodTable(tbl, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
// Call a subpath of the exported path
object := connection.Object(name, "/org/guelfey/DBus/Test/Foo")
var response string
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Foo: %s", err)
}
if response != "foo" {
t.Errorf(`Response was %s, expected "foo"`, response)
}
if export.message.serial == 0 {
t.Error("Expected the raw message, got an invalid one")
}
err = object.Call("org.guelfey.DBus.Test.Foo2", 0, "qux").Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Foo: %s", err)
}
if response != "foo" {
t.Errorf(`Response was %s, expected "foo"`, response)
}
if export.message.serial == 0 {
t.Error("Expected the raw message, got an invalid one")
}
// Now remove export
connection.Export(nil, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err == nil {
t.Error("Expected an error since the export was removed")
}
}
func TestExportMethodTable_NotFunc(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
export := &fooExport{}
tbl := make(map[string]interface{})
tbl["Foo"] = func(message Message, param string) (string, *Error) {
return export.Foo(message, param)
}
tbl["Foo2"] = "foobar"
connection.ExportMethodTable(tbl, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
object := connection.Object(name, "/org/guelfey/DBus/Test")
var response string
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Foo: %s", err)
}
if response != "foo" {
t.Errorf(`Response was %s, expected "foo"`, response)
}
if export.message.serial == 0 {
t.Error("Expected the raw message, got an invalid one")
}
err = object.Call("org.guelfey.DBus.Test.Foo2", 0, "qux").Store(&response)
if err == nil {
t.Errorf("Expected an error since the Foo2 was not a function")
}
}
func TestExportMethodTable_ReturnNotError(t *testing.T) {
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
export := &fooExport{}
tbl := make(map[string]interface{})
tbl["Foo"] = func(message Message, param string) (string, string) {
out, _ := export.Foo(message, param)
return out, out
}
connection.ExportMethodTable(tbl, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
object := connection.Object(name, "/org/guelfey/DBus/Test")
var response string
err = object.Call("org.guelfey.DBus.Test.Foo", 0, "qux").Store(&response)
if err == nil {
t.Errorf("Expected an error since the Foo did not have a final return as *dbus.Error")
}
}
// Test that introspection works on sub path of every exported object
func TestExportSubPathIntrospection(t *testing.T) {
const (
introIntf = "org.freedesktop.DBus.Introspectable"
respTmpl = `^<node>\s*<node\s+name="%s"\s*/>\s*</node>$`
pathstr = "/org/guelfey/DBus/Test"
foopathstr = pathstr + "/Foo"
barpathstr = pathstr + "/Bar"
test1intfstr = "org.guelfey.DBus.Test1"
test2intfstr = "org.guelfey.DBus.Test2"
intro = `
<node>
<interface name="` + test1intfstr + `">
<method name="Foo">
<arg direction="out" type="s"/>
</method>
</interface>
<interface name="` + test2intfstr + `">
<method name="Foo">
<arg direction="out" type="s"/>
</method>
<method name="Bar">
<arg direction="out" type="s"/>
</method>
</interface>
<interface name="` + introIntf + `">
<method name="Introspect">
<arg name="out" direction="out" type="s"/>
</method>
</interface>
</node>`
)
connection, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := connection.Names()[0]
foo := &fooExport{}
bar := &barExport{}
connection.Export(foo, foopathstr, test1intfstr)
connection.Export(foo, foopathstr, test2intfstr)
connection.Export(bar, barpathstr, test2intfstr)
connection.Export(intro, pathstr, introIntf)
var response string
var match bool
path := strings.Split(pathstr, "/")
for i := 0; i < len(path)-1; i++ {
var subpath string
if i == 0 {
subpath = "/"
} else {
subpath = strings.Join(path[:i+1], "/")
}
object := connection.Object(name, ObjectPath(subpath))
err = object.Call(introIntf+".Introspect", 0).Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Introspect on %s: %s", subpath, err)
}
exp := fmt.Sprintf(respTmpl, path[i+1])
match, err = regexp.MatchString(exp, response)
if err != nil {
t.Fatalf("Error calling MatchString: %s", err)
}
if !match {
t.Errorf("Unexpected introspection response for %s: %s", subpath, response)
}
}
// Test invalid subpath
invalSubpath := "/org/guelfey/DBus/Test/Nonexistent"
object := connection.Object(name, ObjectPath(invalSubpath))
err = object.Call(introIntf+".Introspect", 0).Store(&response)
if err != nil {
t.Errorf("Unexpected error calling Introspect on %s: %s", invalSubpath, err)
}
match, err = regexp.MatchString(`^<node>\s*</node>$`, response)
if err != nil {
t.Fatalf("Error calling MatchString: %s", err)
}
if !match {
t.Errorf("Unexpected introspection response for %s: %s", invalSubpath, response)
}
}

View File

@ -1,28 +0,0 @@
package introspect
import (
"encoding/xml"
"strings"
"github.com/godbus/dbus/v5"
)
// Call calls org.freedesktop.Introspectable.Introspect on a remote object
// and returns the introspection data.
func Call(o dbus.BusObject) (*Node, error) {
var xmldata string
var node Node
err := o.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&xmldata)
if err != nil {
return nil, err
}
err = xml.NewDecoder(strings.NewReader(xmldata)).Decode(&node)
if err != nil {
return nil, err
}
if node.Name == "" {
node.Name = string(o.Path())
}
return &node, nil
}

View File

@ -1,86 +0,0 @@
// Package introspect provides some utilities for dealing with the DBus
// introspection format.
package introspect
import "encoding/xml"
// The introspection data for the org.freedesktop.DBus.Introspectable interface.
var IntrospectData = Interface{
Name: "org.freedesktop.DBus.Introspectable",
Methods: []Method{
{
Name: "Introspect",
Args: []Arg{
{"out", "s", "out"},
},
},
},
}
// XML document type declaration of the introspection format version 1.0
const IntrospectDeclarationString = `
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
`
// The introspection data for the org.freedesktop.DBus.Introspectable interface,
// as a string.
const IntrospectDataString = `
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="out" direction="out" type="s"/>
</method>
</interface>
`
// Node is the root element of an introspection.
type Node struct {
XMLName xml.Name `xml:"node"`
Name string `xml:"name,attr,omitempty"`
Interfaces []Interface `xml:"interface"`
Children []Node `xml:"node,omitempty"`
}
// Interface describes a DBus interface that is available on the message bus.
type Interface struct {
Name string `xml:"name,attr"`
Methods []Method `xml:"method"`
Signals []Signal `xml:"signal"`
Properties []Property `xml:"property"`
Annotations []Annotation `xml:"annotation"`
}
// Method describes a Method on an Interface as retured by an introspection.
type Method struct {
Name string `xml:"name,attr"`
Args []Arg `xml:"arg"`
Annotations []Annotation `xml:"annotation"`
}
// Signal describes a Signal emitted on an Interface.
type Signal struct {
Name string `xml:"name,attr"`
Args []Arg `xml:"arg"`
Annotations []Annotation `xml:"annotation"`
}
// Property describes a property of an Interface.
type Property struct {
Name string `xml:"name,attr"`
Type string `xml:"type,attr"`
Access string `xml:"access,attr"`
Annotations []Annotation `xml:"annotation"`
}
// Arg represents an argument of a method or a signal.
type Arg struct {
Name string `xml:"name,attr,omitempty"`
Type string `xml:"type,attr"`
Direction string `xml:"direction,attr,omitempty"`
}
// Annotation is an annotation in the introspection format.
type Annotation struct {
Name string `xml:"name,attr"`
Value string `xml:"value,attr"`
}

View File

@ -1,77 +0,0 @@
package introspect
import (
"encoding/xml"
"reflect"
"strings"
"github.com/godbus/dbus/v5"
)
// Introspectable implements org.freedesktop.Introspectable.
//
// You can create it by converting the XML-formatted introspection data from a
// string to an Introspectable or call NewIntrospectable with a Node. Then,
// export it as org.freedesktop.Introspectable on you object.
type Introspectable string
// NewIntrospectable returns an Introspectable that returns the introspection
// data that corresponds to the given Node. If n.Interfaces doesn't contain the
// data for org.freedesktop.DBus.Introspectable, it is added automatically.
func NewIntrospectable(n *Node) Introspectable {
found := false
for _, v := range n.Interfaces {
if v.Name == "org.freedesktop.DBus.Introspectable" {
found = true
break
}
}
if !found {
n.Interfaces = append(n.Interfaces, IntrospectData)
}
b, err := xml.Marshal(n)
if err != nil {
panic(err)
}
return Introspectable(strings.TrimSpace(IntrospectDeclarationString) + string(b))
}
// Introspect implements org.freedesktop.Introspectable.Introspect.
func (i Introspectable) Introspect() (string, *dbus.Error) {
return string(i), nil
}
// Methods returns the description of the methods of v. This can be used to
// create a Node which can be passed to NewIntrospectable.
func Methods(v interface{}) []Method {
t := reflect.TypeOf(v)
ms := make([]Method, 0, t.NumMethod())
for i := 0; i < t.NumMethod(); i++ {
if t.Method(i).PkgPath != "" {
continue
}
mt := t.Method(i).Type
if mt.NumOut() == 0 ||
mt.Out(mt.NumOut()-1) != reflect.TypeOf(&dbus.Error{}) {
continue
}
var m 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() &&
mt.In(j) != reflect.TypeOf((*dbus.Message)(nil)).Elem() {
arg := Arg{"", dbus.SignatureOfType(mt.In(j)).String(), "in"}
m.Args = append(m.Args, arg)
}
}
for j := 0; j < mt.NumOut()-1; j++ {
arg := Arg{"", dbus.SignatureOfType(mt.Out(j)).String(), "out"}
m.Args = append(m.Args, arg)
}
m.Annotations = make([]Annotation, 0)
ms = append(ms, m)
}
return ms
}

View File

@ -1,19 +0,0 @@
package dbus
import "testing"
func TestFormatMatchOptions(t *testing.T) {
opts := []MatchOption{
withMatchType("signal"),
WithMatchSender("org.bluez"),
WithMatchInterface("org.freedesktop.DBus.Properties"),
WithMatchMember("PropertiesChanged"),
WithMatchPathNamespace("/org/bluez/hci0"),
}
want := "type='signal',sender='org.bluez'," +
"interface='org.freedesktop.DBus.Properties'," +
"member='PropertiesChanged',path_namespace='/org/bluez/hci0'"
if have := formatMatchOptions(opts); have != want {
t.Fatalf("formatMatchOptions(%v) = %q, want %q", opts, have, want)
}
}

View File

@ -1,156 +0,0 @@
package dbus
import (
"context"
"testing"
"time"
)
type objectGoContextServer struct {
t *testing.T
sleep time.Duration
}
func (o objectGoContextServer) Sleep() *Error {
o.t.Log("Got object call and sleeping for ", o.sleep)
time.Sleep(o.sleep)
o.t.Log("Completed sleeping for ", o.sleep)
return nil
}
func TestObjectGoWithContextTimeout(t *testing.T) {
bus, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := bus.Names()[0]
bus.Export(objectGoContextServer{t, time.Second}, "/org/dannin/DBus/Test", "org.dannin.DBus.Test")
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
select {
case call := <-bus.Object(name, "/org/dannin/DBus/Test").GoWithContext(ctx, "org.dannin.DBus.Test.Sleep", 0, nil).Done:
if call.Err != ctx.Err() {
t.Fatal("Expected ", ctx.Err(), " but got ", call.Err)
}
case <-time.After(2 * time.Second):
t.Fatal("Expected call to not respond in time")
}
}
func TestObjectGoWithContext(t *testing.T) {
bus, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := bus.Names()[0]
bus.Export(objectGoContextServer{t, time.Millisecond}, "/org/dannin/DBus/Test", "org.dannin.DBus.Test")
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
select {
case call := <-bus.Object(name, "/org/dannin/DBus/Test").GoWithContext(ctx, "org.dannin.DBus.Test.Sleep", 0, nil).Done:
if call.Err != ctx.Err() {
t.Fatal("Expected ", ctx.Err(), " but got ", call.Err)
}
case <-time.After(time.Second):
t.Fatal("Expected call to respond in 1 Millisecond")
}
}
type nopServer struct{}
func (_ nopServer) Nop() *Error {
return nil
}
func fetchSignal(t *testing.T, ch chan *Signal, timeout time.Duration) *Signal {
select {
case sig := <-ch:
return sig
case <-time.After(timeout):
t.Fatalf("Failed to fetch signal in specified timeout %s", timeout)
}
return nil
}
func TestObjectSignalHandling(t *testing.T) {
bus, err := SessionBus()
if err != nil {
t.Fatalf("Unexpected error connecting to session bus: %s", err)
}
name := bus.Names()[0]
path := ObjectPath("/org/godbus/DBus/TestSignals")
otherPath := ObjectPath("/org/other-godbus/DBus/TestSignals")
iface := "org.godbus.DBus.TestSignals"
otherIface := "org.godbus.DBus.OtherTestSignals"
err = bus.Export(nopServer{}, path, iface)
if err != nil {
t.Fatalf("Unexpected error registering nop server: %v", err)
}
obj := bus.Object(name, path)
if err := bus.AddMatchSignal(
WithMatchInterface(iface),
WithMatchMember("Heartbeat"),
WithMatchObjectPath(path),
); err != nil {
t.Fatal(err)
}
ch := make(chan *Signal, 5)
bus.Signal(ch)
go func() {
defer func() {
if err := recover(); err != nil {
t.Errorf("Catched panic in emitter goroutine: %v", err)
}
}()
// desired signals
bus.Emit(path, iface+".Heartbeat", uint32(1))
bus.Emit(path, iface+".Heartbeat", uint32(2))
// undesired signals
bus.Emit(otherPath, iface+".Heartbeat", uint32(3))
bus.Emit(otherPath, otherIface+".Heartbeat", uint32(4))
bus.Emit(path, iface+".Updated", false)
// sentinel
bus.Emit(path, iface+".Heartbeat", uint32(5))
time.Sleep(100 * time.Millisecond)
bus.Emit(path, iface+".Heartbeat", uint32(6))
}()
checkSignal := func(sig *Signal, value uint32) {
if sig.Path != path {
t.Errorf("signal.Path mismatch: %s != %s", path, sig.Path)
}
name := iface + ".Heartbeat"
if sig.Name != name {
t.Errorf("signal.Name mismatch: %s != %s", name, sig.Name)
}
if len(sig.Body) != 1 {
t.Errorf("Invalid signal body length: %d", len(sig.Body))
return
}
if sig.Body[0] != interface{}(value) {
t.Errorf("signal value mismatch: %d != %d", value, sig.Body[0])
}
}
checkSignal(fetchSignal(t, ch, 50*time.Millisecond), 1)
checkSignal(fetchSignal(t, ch, 50*time.Millisecond), 2)
checkSignal(fetchSignal(t, ch, 50*time.Millisecond), 5)
obj.RemoveMatchSignal(iface, "Heartbeat", WithMatchObjectPath(obj.Path()))
select {
case sig := <-ch:
t.Errorf("Got signal after removing subscription: %v", sig)
case <-time.After(200 * time.Millisecond):
}
}

View File

@ -1,285 +0,0 @@
// Package prop provides the Properties struct which can be used to implement
// org.freedesktop.DBus.Properties.
package prop
import (
"sync"
"github.com/godbus/dbus/v5"
"github.com/godbus/dbus/v5/introspect"
)
// EmitType controls how org.freedesktop.DBus.Properties.PropertiesChanged is
// emitted for a property. If it is EmitTrue, the signal is emitted. If it is
// EmitInvalidates, the signal is also emitted, but the new value of the property
// is not disclosed.
type EmitType byte
const (
EmitFalse EmitType = iota
EmitTrue
EmitInvalidates
)
// ErrIfaceNotFound is the error returned to peers who try to access properties
// on interfaces that aren't found.
var ErrIfaceNotFound = dbus.NewError("org.freedesktop.DBus.Properties.Error.InterfaceNotFound", nil)
// ErrPropNotFound is the error returned to peers trying to access properties
// that aren't found.
var ErrPropNotFound = dbus.NewError("org.freedesktop.DBus.Properties.Error.PropertyNotFound", nil)
// ErrReadOnly is the error returned to peers trying to set a read-only
// property.
var ErrReadOnly = dbus.NewError("org.freedesktop.DBus.Properties.Error.ReadOnly", nil)
// ErrInvalidArg is returned to peers if the type of the property that is being
// changed and the argument don't match.
var ErrInvalidArg = dbus.NewError("org.freedesktop.DBus.Properties.Error.InvalidArg", nil)
// The introspection data for the org.freedesktop.DBus.Properties interface.
var IntrospectData = introspect.Interface{
Name: "org.freedesktop.DBus.Properties",
Methods: []introspect.Method{
{
Name: "Get",
Args: []introspect.Arg{
{Name: "interface", Type: "s", Direction: "in"},
{Name: "property", Type: "s", Direction: "in"},
{Name: "value", Type: "v", Direction: "out"},
},
},
{
Name: "GetAll",
Args: []introspect.Arg{
{Name: "interface", Type: "s", Direction: "in"},
{Name: "props", Type: "a{sv}", Direction: "out"},
},
},
{
Name: "Set",
Args: []introspect.Arg{
{Name: "interface", Type: "s", Direction: "in"},
{Name: "property", Type: "s", Direction: "in"},
{Name: "value", Type: "v", Direction: "in"},
},
},
},
Signals: []introspect.Signal{
{
Name: "PropertiesChanged",
Args: []introspect.Arg{
{Name: "interface", Type: "s", Direction: "out"},
{Name: "changed_properties", Type: "a{sv}", Direction: "out"},
{Name: "invalidates_properties", Type: "as", Direction: "out"},
},
},
},
}
// The introspection data for the org.freedesktop.DBus.Properties interface, as
// a string.
const IntrospectDataString = `
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="out" type="v"/>
</method>
<method name="GetAll">
<arg name="interface" direction="in" type="s"/>
<arg name="props" direction="out" type="a{sv}"/>
</method>
<method name="Set">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="in" type="v"/>
</method>
<signal name="PropertiesChanged">
<arg name="interface" type="s"/>
<arg name="changed_properties" type="a{sv}"/>
<arg name="invalidates_properties" type="as"/>
</signal>
</interface>
`
// Prop represents a single property. It is used for creating a Properties
// value.
type Prop struct {
// Initial value. Must be a DBus-representable type.
Value interface{}
// If true, the value can be modified by calls to Set.
Writable bool
// Controls how org.freedesktop.DBus.Properties.PropertiesChanged is
// emitted if this property changes.
Emit EmitType
// If not nil, anytime this property is changed by Set, this function is
// called with an appropriate Change as its argument. If the returned error
// is not nil, it is sent back to the caller of Set and the property is not
// changed.
Callback func(*Change) *dbus.Error
}
// Change represents a change of a property by a call to Set.
type Change struct {
Props *Properties
Iface string
Name string
Value interface{}
}
// Properties is a set of values that can be made available to the message bus
// using the org.freedesktop.DBus.Properties interface. It is safe for
// concurrent use by multiple goroutines.
type Properties struct {
m map[string]map[string]*Prop
mut sync.RWMutex
conn *dbus.Conn
path dbus.ObjectPath
}
// New falls back to Export, but it returns nil if properties export fails,
// swallowing the error, shouldn't be used.
//
// Deprecated: use Export instead.
func New(conn *dbus.Conn, path dbus.ObjectPath, props map[string]map[string]*Prop) *Properties {
p, err := Export(conn, path, props)
if err != nil {
return nil
}
return p
}
// Export returns a new Properties structure that manages the given properties.
// The key for the first-level map of props is the name of the interface; the
// second-level key is the name of the property. The returned structure will be
// exported as org.freedesktop.DBus.Properties on path.
func Export(
conn *dbus.Conn, path dbus.ObjectPath, props map[string]map[string]*Prop,
) (*Properties, error) {
p := &Properties{m: props, conn: conn, path: path}
if err := conn.Export(p, path, "org.freedesktop.DBus.Properties"); err != nil {
return nil, err
}
return p, nil
}
// Get implements org.freedesktop.DBus.Properties.Get.
func (p *Properties) Get(iface, property string) (dbus.Variant, *dbus.Error) {
p.mut.RLock()
defer p.mut.RUnlock()
m, ok := p.m[iface]
if !ok {
return dbus.Variant{}, ErrIfaceNotFound
}
prop, ok := m[property]
if !ok {
return dbus.Variant{}, ErrPropNotFound
}
return dbus.MakeVariant(prop.Value), nil
}
// GetAll implements org.freedesktop.DBus.Properties.GetAll.
func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
p.mut.RLock()
defer p.mut.RUnlock()
m, ok := p.m[iface]
if !ok {
return nil, ErrIfaceNotFound
}
rm := make(map[string]dbus.Variant, len(m))
for k, v := range m {
rm[k] = dbus.MakeVariant(v.Value)
}
return rm, nil
}
// GetMust returns the value of the given property and panics if either the
// interface or the property name are invalid.
func (p *Properties) GetMust(iface, property string) interface{} {
p.mut.RLock()
defer p.mut.RUnlock()
return p.m[iface][property].Value
}
// Introspection returns the introspection data that represents the properties
// of iface.
func (p *Properties) Introspection(iface string) []introspect.Property {
p.mut.RLock()
defer p.mut.RUnlock()
m := p.m[iface]
s := make([]introspect.Property, 0, len(m))
for k, v := range m {
p := introspect.Property{Name: k, Type: dbus.SignatureOf(v.Value).String()}
if v.Writable {
p.Access = "readwrite"
} else {
p.Access = "read"
}
s = append(s, p)
}
return s
}
// set sets the given property and emits PropertyChanged if appropriate. p.mut
// must already be locked.
func (p *Properties) set(iface, property string, v interface{}) error {
prop := p.m[iface][property]
prop.Value = v
switch prop.Emit {
case EmitFalse:
return nil // do nothing
case EmitInvalidates:
return p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged",
iface, map[string]dbus.Variant{}, []string{property})
case EmitTrue:
return p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged",
iface, map[string]dbus.Variant{property: dbus.MakeVariant(v)},
[]string{})
default:
panic("invalid value for EmitType")
}
}
// Set implements org.freedesktop.Properties.Set.
func (p *Properties) Set(iface, property string, newv dbus.Variant) *dbus.Error {
p.mut.Lock()
defer p.mut.Unlock()
m, ok := p.m[iface]
if !ok {
return ErrIfaceNotFound
}
prop, ok := m[property]
if !ok {
return ErrPropNotFound
}
if !prop.Writable {
return ErrReadOnly
}
if newv.Signature() != dbus.SignatureOf(prop.Value) {
return ErrInvalidArg
}
if prop.Callback != nil {
err := prop.Callback(&Change{p, iface, property, newv.Value()})
if err != nil {
return err
}
}
if err := p.set(iface, property, newv.Value()); err != nil {
return dbus.MakeFailedError(err)
}
return nil
}
// SetMust sets the value of the given property and panics if the interface or
// the property name are invalid.
func (p *Properties) SetMust(iface, property string, v interface{}) {
p.mut.Lock()
defer p.mut.Unlock() // unlock in case of panic
if err := p.set(iface, property, v); err != nil {
panic(err)
}
}

View File

@ -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.Equal(marshalled, v.bigEndian) {
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.Equal(marshalled, v.littleEndian) {
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)
}
}
}

View File

@ -1,487 +0,0 @@
package dbus
import (
"fmt"
"sync"
"sync/atomic"
"testing"
"time"
)
type tester struct {
conn *Conn
sigs chan *Signal
subSigsMu sync.Mutex
subSigs map[string]map[string]struct{}
serial uint32
}
type intro struct {
path ObjectPath
}
func (i *intro) introspectPath(path ObjectPath) string {
switch path {
case "/":
return `<node><node name="com"></node></node>`
case "/com":
return `<node><node name="github"></node></node>`
case "/com/github":
return `<node><node name="godbus"></node></node>`
case "/com/github/godbus":
return `<node><node name="tester"></node></node>`
}
return ""
}
func (i *intro) LookupInterface(name string) (Interface, bool) {
if name == "org.freedesktop.DBus.Introspectable" {
return i, true
}
return nil, false
}
func (i *intro) LookupMethod(name string) (Method, bool) {
if name == "Introspect" {
return intro_fn(func() string {
return i.introspectPath(i.path)
}), true
}
return nil, false
}
func newIntro(path ObjectPath) *intro {
return &intro{path}
}
//Handler
func (t *tester) LookupObject(path ObjectPath) (ServerObject, bool) {
if path == "/com/github/godbus/tester" {
return t, true
}
return newIntro(path), true
}
//ServerObject
func (t *tester) LookupInterface(name string) (Interface, bool) {
switch name {
case "com.github.godbus.dbus.Tester":
return t, true
case "org.freedesktop.DBus.Introspectable":
return t, true
}
return nil, false
}
//Interface
func (t *tester) LookupMethod(name string) (Method, bool) {
switch name {
case "Test":
return t, true
case "Error":
return terrfn(func(in string) error {
return fmt.Errorf(in)
}), true
case "Introspect":
return intro_fn(func() string {
return `<node>
<interface name="org.freedesktop.DBus.Introspectable.Introspect">
<method name="Introspect">
<arg name="out" type="i" direction="out">
</method>
</interface>
<interface name="com.github.godbus.dbus.Tester">
<method name="Test">
<arg name="in" type="i" direction="in">
<arg name="out" type="i" direction="out">
</method>
<signal name="sig1">
<arg name="out" type="i" direction="out">
</signal>
</interface>
</node>`
}), true
}
return nil, false
}
//Method
func (t *tester) Call(args ...interface{}) ([]interface{}, error) {
return args, nil
}
func (t *tester) NumArguments() int {
return 1
}
func (t *tester) NumReturns() int {
return 1
}
func (t *tester) ArgumentValue(position int) interface{} {
return ""
}
func (t *tester) ReturnValue(position int) interface{} {
return ""
}
type terrfn func(in string) error
func (t terrfn) Call(args ...interface{}) ([]interface{}, error) {
return nil, t(*args[0].(*string))
}
func (t terrfn) NumArguments() int {
return 1
}
func (t terrfn) NumReturns() int {
return 0
}
func (t terrfn) ArgumentValue(position int) interface{} {
return ""
}
func (t terrfn) ReturnValue(position int) interface{} {
return ""
}
//SignalHandler
func (t *tester) DeliverSignal(iface, name string, signal *Signal) {
t.subSigsMu.Lock()
intf, ok := t.subSigs[iface]
t.subSigsMu.Unlock()
if !ok {
return
}
if _, ok := intf[name]; !ok {
return
}
t.sigs <- signal
}
func (t *tester) AddSignal(iface, name string) error {
t.subSigsMu.Lock()
if i, ok := t.subSigs[iface]; ok {
i[name] = struct{}{}
} else {
t.subSigs[iface] = make(map[string]struct{})
t.subSigs[iface][name] = struct{}{}
}
t.subSigsMu.Unlock()
return t.conn.AddMatchSignal(WithMatchInterface(iface), WithMatchMember(name))
}
func (t *tester) Close() {
t.conn.Close()
close(t.sigs)
}
func (t *tester) Name() string {
return t.conn.Names()[0]
}
func (t *tester) GetSerial() uint32 {
return atomic.AddUint32(&t.serial, 1)
}
func (t *tester) RetireSerial(serial uint32) {}
type intro_fn func() string
func (intro intro_fn) Call(args ...interface{}) ([]interface{}, error) {
return []interface{}{intro()}, nil
}
func (_ intro_fn) NumArguments() int {
return 0
}
func (_ intro_fn) NumReturns() int {
return 1
}
func (_ intro_fn) ArgumentValue(position int) interface{} {
return nil
}
func (_ intro_fn) ReturnValue(position int) interface{} {
return ""
}
func newTester() (*tester, error) {
tester := &tester{
sigs: make(chan *Signal),
subSigs: make(map[string]map[string]struct{}),
}
conn, err := SessionBusPrivate(
WithHandler(tester),
WithSignalHandler(tester),
WithSerialGenerator(tester),
)
if err != nil {
return nil, err
}
err = conn.Auth(nil)
if err != nil {
conn.Close()
return nil, err
}
err = conn.Hello()
if err != nil {
conn.Close()
return nil, err
}
tester.conn = conn
return tester, nil
}
func TestHandlerCall(t *testing.T) {
tester, err := newTester()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
conn, err := SessionBus()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
var out string
in := "foo"
err = obj.Call("com.github.godbus.dbus.Tester.Test", 0, in).Store(&out)
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
if out != in {
t.Errorf("Unexpected error: got %s, expected %s", out, in)
}
tester.Close()
}
func TestHandlerCallGenericError(t *testing.T) {
tester, err := newTester()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
conn, err := SessionBus()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
var out string
in := "foo"
err = obj.Call("com.github.godbus.dbus.Tester.Error", 0, in).Store(&out)
if err != nil && err.(Error).Body[0].(string) != "foo" {
t.Errorf("Unexpected error: %s", err)
}
tester.Close()
}
func TestHandlerCallNonExistent(t *testing.T) {
tester, err := newTester()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
conn, err := SessionBus()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
obj := conn.Object(tester.Name(), "/com/github/godbus/tester/nonexist")
var out string
in := "foo"
err = obj.Call("com.github.godbus.dbus.Tester.Test", 0, in).Store(&out)
if err != nil {
if err.Error() != "Object does not implement the interface" {
t.Errorf("Unexpected error: %s", err)
}
}
tester.Close()
}
func TestHandlerInvalidFunc(t *testing.T) {
tester, err := newTester()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
conn, err := SessionBus()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
var out string
in := "foo"
err = obj.Call("com.github.godbus.dbus.Tester.Notexist", 0, in).Store(&out)
if err == nil {
t.Errorf("didn't get expected error")
}
tester.Close()
}
func TestHandlerInvalidNumArg(t *testing.T) {
tester, err := newTester()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
conn, err := SessionBus()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
var out string
err = obj.Call("com.github.godbus.dbus.Tester.Test", 0).Store(&out)
if err == nil {
t.Errorf("didn't get expected error")
}
tester.Close()
}
func TestHandlerInvalidArgType(t *testing.T) {
tester, err := newTester()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
conn, err := SessionBus()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
var out string
err = obj.Call("com.github.godbus.dbus.Tester.Test", 0, 2.10).Store(&out)
if err == nil {
t.Errorf("didn't get expected error")
}
tester.Close()
}
func TestHandlerIntrospect(t *testing.T) {
tester, err := newTester()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
conn, err := SessionBus()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
obj := conn.Object(tester.Name(), "/com/github/godbus/tester")
var out string
err = obj.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&out)
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
expected := `<node>
<interface name="org.freedesktop.DBus.Introspectable.Introspect">
<method name="Introspect">
<arg name="out" type="i" direction="out">
</method>
</interface>
<interface name="com.github.godbus.dbus.Tester">
<method name="Test">
<arg name="in" type="i" direction="in">
<arg name="out" type="i" direction="out">
</method>
<signal name="sig1">
<arg name="out" type="i" direction="out">
</signal>
</interface>
</node>`
if out != expected {
t.Errorf("didn't get expected return value, expected %s got %s", expected, out)
}
tester.Close()
}
func TestHandlerIntrospectPath(t *testing.T) {
tester, err := newTester()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
conn, err := SessionBus()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
obj := conn.Object(tester.Name(), "/com/github/godbus")
var out string
err = obj.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&out)
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
expected := `<node><node name="tester"></node></node>`
if out != expected {
t.Errorf("didn't get expected return value, expected %s got %s", expected, out)
}
tester.Close()
}
func TestHandlerSignal(t *testing.T) {
tester, err := newTester()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
conn, err := SessionBus()
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
if err = tester.AddSignal("com.github.godbus.dbus.Tester", "sig1"); err != nil {
t.Fatal(err)
}
if err = conn.Emit(
"/com/github/godbus/tester",
"com.github.godbus.dbus.Tester.sig1",
"foo",
); err != nil {
t.Fatal(err)
}
select {
case sig := <-tester.sigs:
if sig.Body[0] != "foo" {
t.Errorf("Unexpected signal got %s, expected %s", sig.Body[0], "foo")
}
case <-time.After(time.Second * 10): //overly generous timeout
t.Errorf("Didn't receive a signal after 10 seconds")
}
tester.Close()
}
type X struct {
}
func (x *X) Method1() *Error {
return nil
}
func TestRaceInExport(t *testing.T) {
const (
dbusPath = "/org/example/godbus/test1"
dbusInterface = "org.example.godbus.test1"
)
bus, err := SessionBus()
if err != nil {
t.Fatal(err)
}
var x X
var wg sync.WaitGroup
wg.Add(2)
go func() {
err = bus.Export(&x, dbusPath, dbusInterface)
if err != nil {
t.Fatal(err)
}
wg.Done()
}()
go func() {
obj := bus.Object(bus.Names()[0], dbusPath)
obj.Call(dbusInterface+".Method1", 0)
wg.Done()
}()
wg.Wait()
}

View File

@ -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...)
}
}

View File

@ -1,99 +0,0 @@
package dbus
import (
"reflect"
"testing"
)
func TestStoreStringToInterface(t *testing.T) {
var dest interface{}
err := Store([]interface{}{"foobar"}, &dest)
if err != nil {
t.Fatal(err)
}
_ = dest.(string)
}
func TestStoreVariantToInterface(t *testing.T) {
src := MakeVariant("foobar")
var dest interface{}
err := Store([]interface{}{src}, &dest)
if err != nil {
t.Fatal(err)
}
_ = dest.(string)
}
func TestStoreMapStringToMapInterface(t *testing.T) {
src := map[string]string{"foo": "bar"}
dest := map[string]interface{}{}
err := Store([]interface{}{src}, &dest)
if err != nil {
t.Fatal(err)
}
_ = dest["foo"].(string)
}
func TestStoreMapVariantToMapInterface(t *testing.T) {
src := map[string]Variant{"foo": MakeVariant("foobar")}
dest := map[string]interface{}{}
err := Store([]interface{}{src}, &dest)
if err != nil {
t.Fatal(err)
}
_ = dest["foo"].(string)
}
func TestStoreSliceStringToSliceInterface(t *testing.T) {
src := []string{"foo"}
dest := []interface{}{}
err := Store([]interface{}{src}, &dest)
if err != nil {
t.Fatal(err)
}
_ = dest[0].(string)
}
func TestStoreSliceVariantToSliceInterface(t *testing.T) {
src := []Variant{MakeVariant("foo")}
dest := []interface{}{}
err := Store([]interface{}{src}, &dest)
if err != nil {
t.Fatal(err)
}
_ = dest[0].(string)
}
func TestStoreSliceVariantToSliceInterfaceMulti(t *testing.T) {
src := []Variant{MakeVariant("foo"), MakeVariant(int32(1))}
dest := []interface{}{}
err := Store([]interface{}{src}, &dest)
if err != nil {
t.Fatal(err)
}
_ = dest[0].(string)
_ = dest[1].(int32)
}
func TestStoreNested(t *testing.T) {
src := map[string]interface{}{
"foo": []interface{}{"1", "2", "3", "5",
map[string]interface{}{
"bar": "baz",
},
},
"bar": map[string]interface{}{
"baz": "quux",
"quux": "quuz",
},
}
dest := map[string]interface{}{}
err := Store([]interface{}{src}, &dest)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(src, dest) {
t.Errorf("not equal: got '%v', want '%v'",
dest, src)
}
}

View File

@ -1,69 +0,0 @@
package dbus
import (
"bufio"
"io/ioutil"
"os"
"os/exec"
"testing"
)
func TestTcpNonceConnection(t *testing.T) {
addr, process := startDaemon(t, `<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<type>session</type>
<listen>nonce-tcp:</listen>
<auth>ANONYMOUS</auth>
<allow_anonymous/>
<apparmor mode="disabled"/>
<policy context="default">
<allow send_destination="*" eavesdrop="true"/>
<allow eavesdrop="true"/>
<allow own="*"/>
</policy>
</busconfig>
`)
defer process.Kill()
c, err := Dial(addr)
if err != nil {
t.Fatal(err)
}
if err = c.Auth([]Auth{AuthAnonymous()}); err != nil {
t.Fatal(err)
}
if err = c.Hello(); err != nil {
t.Fatal(err)
}
}
// startDaemon starts a dbus-daemon instance with the given config
// and returns its address string and underlying process.
func startDaemon(t *testing.T, config string) (string, *os.Process) {
cfg, err := ioutil.TempFile("", "")
if err != nil {
t.Fatal(err)
}
defer os.Remove(cfg.Name())
if _, err = cfg.Write([]byte(config)); err != nil {
t.Fatal(err)
}
cmd := exec.Command("dbus-daemon", "--nofork", "--print-address", "--config-file", cfg.Name())
cmd.Stderr = os.Stderr
out, err := cmd.StdoutPipe()
if err != nil {
t.Fatal(err)
}
if err = cmd.Start(); err != nil {
t.Fatal(err)
}
r := bufio.NewReader(out)
l, _, err := r.ReadLine()
if err != nil {
cmd.Process.Kill()
t.Fatal(err)
}
return string(l), cmd.Process
}

View File

@ -1,26 +0,0 @@
package dbus
import (
"fmt"
"net"
"testing"
)
func TestTcpConnection(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal("Failed to create listener")
}
host, port, err := net.SplitHostPort(listener.Addr().String())
if err != nil {
t.Fatal("Failed to parse host/port")
}
conn, err := Dial(fmt.Sprintf("tcp:host=%s,port=%s", host, port))
if err != nil {
t.Error("Expected no error, got", err)
}
if conn == nil {
t.Error("Expected connection, got nil")
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -1,4 +1,4 @@
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) # errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge)
Package errors provides simple error handling primitives. Package errors provides simple error handling primitives.
@ -41,12 +41,19 @@ default:
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). [Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
## Roadmap
With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows:
- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible)
- 1.0. Final release.
## Contributing ## Contributing
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports.
Before proposing a change, please discuss your change by raising an issue. Before sending a PR, please discuss your change by raising an issue.
## Licence ## License
BSD-2-Clause BSD-2-Clause

View File

@ -6,7 +6,7 @@
// return err // return err
// } // }
// //
// which applied recursively up the call stack results in error reports // which when applied recursively up the call stack results in error reports
// without context or debugging information. The errors package allows // without context or debugging information. The errors package allows
// programmers to add context to the failure path in their code in a way // programmers to add context to the failure path in their code in a way
// that does not destroy the original value of the error. // that does not destroy the original value of the error.
@ -15,16 +15,17 @@
// //
// The errors.Wrap function returns a new error that adds context to the // The errors.Wrap function returns a new error that adds context to the
// original error by recording a stack trace at the point Wrap is called, // original error by recording a stack trace at the point Wrap is called,
// and the supplied message. For example // together with the supplied message. For example
// //
// _, err := ioutil.ReadAll(r) // _, err := ioutil.ReadAll(r)
// if err != nil { // if err != nil {
// return errors.Wrap(err, "read failed") // return errors.Wrap(err, "read failed")
// } // }
// //
// If additional control is required the errors.WithStack and errors.WithMessage // If additional control is required, the errors.WithStack and
// functions destructure errors.Wrap into its component operations of annotating // errors.WithMessage functions destructure errors.Wrap into its component
// an error with a stack trace and an a message, respectively. // operations: annotating an error with a stack trace and with a message,
// respectively.
// //
// Retrieving the cause of an error // Retrieving the cause of an error
// //
@ -38,7 +39,7 @@
// } // }
// //
// can be inspected by errors.Cause. errors.Cause will recursively retrieve // can be inspected by errors.Cause. errors.Cause will recursively retrieve
// the topmost error which does not implement causer, which is assumed to be // the topmost error that does not implement causer, which is assumed to be
// the original cause. For example: // the original cause. For example:
// //
// switch err := errors.Cause(err).(type) { // switch err := errors.Cause(err).(type) {
@ -48,16 +49,16 @@
// // unknown error // // unknown error
// } // }
// //
// causer interface is not exported by this package, but is considered a part // Although the causer interface is not exported by this package, it is
// of stable public API. // considered a part of its stable public interface.
// //
// Formatted printing of errors // Formatted printing of errors
// //
// All error values returned from this package implement fmt.Formatter and can // All error values returned from this package implement fmt.Formatter and can
// be formatted by the fmt package. The following verbs are supported // be formatted by the fmt package. The following verbs are supported:
// //
// %s print the error. If the error has a Cause it will be // %s print the error. If the error has a Cause it will be
// printed recursively // printed recursively.
// %v see %s // %v see %s
// %+v extended format. Each Frame of the error's StackTrace will // %+v extended format. Each Frame of the error's StackTrace will
// be printed in detail. // be printed in detail.
@ -65,13 +66,13 @@
// Retrieving the stack trace of an error or wrapper // Retrieving the stack trace of an error or wrapper
// //
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
// invoked. This information can be retrieved with the following interface. // invoked. This information can be retrieved with the following interface:
// //
// type stackTracer interface { // type stackTracer interface {
// StackTrace() errors.StackTrace // StackTrace() errors.StackTrace
// } // }
// //
// Where errors.StackTrace is defined as // The returned errors.StackTrace type is defined as
// //
// type StackTrace []Frame // type StackTrace []Frame
// //
@ -81,12 +82,12 @@
// //
// if err, ok := err.(stackTracer); ok { // if err, ok := err.(stackTracer); ok {
// for _, f := range err.StackTrace() { // for _, f := range err.StackTrace() {
// fmt.Printf("%+s:%d", f) // fmt.Printf("%+s:%d\n", f, f)
// } // }
// } // }
// //
// stackTracer interface is not exported by this package, but is considered a part // Although the stackTracer interface is not exported by this package, it is
// of stable public API. // considered a part of its stable public interface.
// //
// See the documentation for Frame.Format for more details. // See the documentation for Frame.Format for more details.
package errors package errors
@ -158,6 +159,9 @@ type withStack struct {
func (w *withStack) Cause() error { return w.error } func (w *withStack) Cause() error { return w.error }
// Unwrap provides compatibility for Go 1.13 error chains.
func (w *withStack) Unwrap() error { return w.error }
func (w *withStack) Format(s fmt.State, verb rune) { func (w *withStack) Format(s fmt.State, verb rune) {
switch verb { switch verb {
case 'v': case 'v':
@ -192,7 +196,7 @@ func Wrap(err error, message string) error {
} }
// Wrapf returns an error annotating err with a stack trace // Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is call, and the format specifier. // at the point Wrapf is called, and the format specifier.
// If err is nil, Wrapf returns nil. // If err is nil, Wrapf returns nil.
func Wrapf(err error, format string, args ...interface{}) error { func Wrapf(err error, format string, args ...interface{}) error {
if err == nil { if err == nil {
@ -220,6 +224,18 @@ func WithMessage(err error, message string) error {
} }
} }
// WithMessagef annotates err with the format specifier.
// If err is nil, WithMessagef returns nil.
func WithMessagef(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
}
}
type withMessage struct { type withMessage struct {
cause error cause error
msg string msg string
@ -228,6 +244,9 @@ type withMessage struct {
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *withMessage) Cause() error { return w.cause } func (w *withMessage) Cause() error { return w.cause }
// Unwrap provides compatibility for Go 1.13 error chains.
func (w *withMessage) Unwrap() error { return w.cause }
func (w *withMessage) Format(s fmt.State, verb rune) { func (w *withMessage) Format(s fmt.State, verb rune) {
switch verb { switch verb {
case 'v': case 'v':

38
src/cmd/linuxkit/vendor/github.com/pkg/errors/go113.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
// +build go1.13
package errors
import (
stderrors "errors"
)
// Is reports whether any error in err's chain matches target.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error is considered to match a target if it is equal to that target or if
// it implements a method Is(error) bool such that Is(target) returns true.
func Is(err, target error) bool { return stderrors.Is(err, target) }
// As finds the first error in err's chain that matches target, and if so, sets
// target to that error value and returns true.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error matches target if the error's concrete value is assignable to the value
// pointed to by target, or if the error has a method As(interface{}) bool such that
// As(target) returns true. In the latter case, the As method is responsible for
// setting target.
//
// As will panic if target is not a non-nil pointer to either a type that implements
// error, or to any interface type. As returns false if err is nil.
func As(err error, target interface{}) bool { return stderrors.As(err, target) }
// Unwrap returns the result of calling the Unwrap method on err, if err's
// type contains an Unwrap method returning error.
// Otherwise, Unwrap returns nil.
func Unwrap(err error) error {
return stderrors.Unwrap(err)
}

View File

@ -5,10 +5,13 @@ import (
"io" "io"
"path" "path"
"runtime" "runtime"
"strconv"
"strings" "strings"
) )
// Frame represents a program counter inside a stack frame. // Frame represents a program counter inside a stack frame.
// For historical reasons if Frame is interpreted as a uintptr
// its value represents the program counter + 1.
type Frame uintptr type Frame uintptr
// pc returns the program counter for this frame; // pc returns the program counter for this frame;
@ -37,6 +40,15 @@ func (f Frame) line() int {
return line return line
} }
// name returns the name of this function, if known.
func (f Frame) name() string {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return "unknown"
}
return fn.Name()
}
// Format formats the frame according to the fmt.Formatter interface. // Format formats the frame according to the fmt.Formatter interface.
// //
// %s source file // %s source file
@ -46,29 +58,24 @@ func (f Frame) line() int {
// //
// Format accepts flags that alter the printing of some verbs, as follows: // Format accepts flags that alter the printing of some verbs, as follows:
// //
// %+s path of source file relative to the compile time GOPATH // %+s function name and path of source file relative to the compile time
// GOPATH separated by \n\t (<funcname>\n\t<path>)
// %+v equivalent to %+s:%d // %+v equivalent to %+s:%d
func (f Frame) Format(s fmt.State, verb rune) { func (f Frame) Format(s fmt.State, verb rune) {
switch verb { switch verb {
case 's': case 's':
switch { switch {
case s.Flag('+'): case s.Flag('+'):
pc := f.pc() io.WriteString(s, f.name())
fn := runtime.FuncForPC(pc) io.WriteString(s, "\n\t")
if fn == nil { io.WriteString(s, f.file())
io.WriteString(s, "unknown")
} else {
file, _ := fn.FileLine(pc)
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
}
default: default:
io.WriteString(s, path.Base(f.file())) io.WriteString(s, path.Base(f.file()))
} }
case 'd': case 'd':
fmt.Fprintf(s, "%d", f.line()) io.WriteString(s, strconv.Itoa(f.line()))
case 'n': case 'n':
name := runtime.FuncForPC(f.pc()).Name() io.WriteString(s, funcname(f.name()))
io.WriteString(s, funcname(name))
case 'v': case 'v':
f.Format(s, 's') f.Format(s, 's')
io.WriteString(s, ":") io.WriteString(s, ":")
@ -76,27 +83,59 @@ func (f Frame) Format(s fmt.State, verb rune) {
} }
} }
// MarshalText formats a stacktrace Frame as a text string. The output is the
// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
func (f Frame) MarshalText() ([]byte, error) {
name := f.name()
if name == "unknown" {
return []byte(name), nil
}
return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
}
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
type StackTrace []Frame type StackTrace []Frame
// Format formats the stack of Frames according to the fmt.Formatter interface.
//
// %s lists source files for each Frame in the stack
// %v lists the source file and line number for each Frame in the stack
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+v Prints filename, function, and line number for each Frame in the stack.
func (st StackTrace) Format(s fmt.State, verb rune) { func (st StackTrace) Format(s fmt.State, verb rune) {
switch verb { switch verb {
case 'v': case 'v':
switch { switch {
case s.Flag('+'): case s.Flag('+'):
for _, f := range st { for _, f := range st {
fmt.Fprintf(s, "\n%+v", f) io.WriteString(s, "\n")
f.Format(s, verb)
} }
case s.Flag('#'): case s.Flag('#'):
fmt.Fprintf(s, "%#v", []Frame(st)) fmt.Fprintf(s, "%#v", []Frame(st))
default: default:
fmt.Fprintf(s, "%v", []Frame(st)) st.formatSlice(s, verb)
} }
case 's': case 's':
fmt.Fprintf(s, "%s", []Frame(st)) st.formatSlice(s, verb)
} }
} }
// formatSlice will format this StackTrace into the given buffer as a slice of
// Frame, only valid when called with '%s' or '%v'.
func (st StackTrace) formatSlice(s fmt.State, verb rune) {
io.WriteString(s, "[")
for i, f := range st {
if i > 0 {
io.WriteString(s, " ")
}
f.Format(s, verb)
}
io.WriteString(s, "]")
}
// stack represents a stack of program counters. // stack represents a stack of program counters.
type stack []uintptr type stack []uintptr
@ -136,43 +175,3 @@ func funcname(name string) string {
i = strings.Index(name, ".") i = strings.Index(name, ".")
return name[i+1:] return name[i+1:]
} }
func trimGOPATH(name, file string) string {
// Here we want to get the source file path relative to the compile time
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
// GOPATH at runtime, but we can infer the number of path segments in the
// GOPATH. We note that fn.Name() returns the function name qualified by
// the import path, which does not include the GOPATH. Thus we can trim
// segments from the beginning of the file path until the number of path
// separators remaining is one more than the number of path separators in
// the function name. For example, given:
//
// GOPATH /home/user
// file /home/user/src/pkg/sub/file.go
// fn.Name() pkg/sub.Type.Method
//
// We want to produce:
//
// pkg/sub/file.go
//
// From this we can easily see that fn.Name() has one less path separator
// than our desired output. We count separators from the end of the file
// path until it finds two more than in the function name and then move
// one character forward to preserve the initial path segment without a
// leading separator.
const sep = "/"
goal := strings.Count(name, sep) + 2
i := len(file)
for n := 0; n < goal; n++ {
i = strings.LastIndex(file[:i], sep)
if i == -1 {
// not enough separators found, set i so that the slice expression
// below leaves file unmodified
i = -len(sep)
break
}
}
// get back to 0 or trim the leading separator
file = file[i+len(sep):]
return file
}