aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra Umiker <cat@ophivana.moe>2024-09-28 00:06:16 +0900
committerOphestra Umiker <cat@ophivana.moe>2024-09-28 00:06:16 +0900
commit1038af98f08f5c16828fabde3d6b559013c6c123 (patch)
tree30a4ddf80b15bbc433ede50720cecc35403946fe
parentaa2be18f471892bdb103e58cbe850dafb5b6099c (diff)
dbus: add tests
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
-rw-r--r--dbus/config_test.go152
-rw-r--r--dbus/dbus_test.go132
-rw-r--r--dbus/export_test.go13
-rw-r--r--dbus/samples_test.go167
-rw-r--r--dbus/testdata/dev.vencord.Vesktop.json18
-rw-r--r--dbus/testdata/org.chromium.Chromium*.json8
-rw-r--r--dbus/testdata/org.chromium.Chromium.json20
-rw-r--r--flake.nix2
8 files changed, 511 insertions, 1 deletions
diff --git a/dbus/config_test.go b/dbus/config_test.go
new file mode 100644
index 00000000..f92fe568
--- /dev/null
+++ b/dbus/config_test.go
@@ -0,0 +1,152 @@
+package dbus_test
+
+import (
+ "path"
+ "reflect"
+ "slices"
+ "strings"
+ "testing"
+
+ "git.ophivana.moe/cat/fortify/dbus"
+)
+
+func TestConfig_Args(t *testing.T) {
+ for _, tc := range testCases() {
+ if tc.wantErr {
+ // args does not check for nulls
+ continue
+ }
+
+ t.Run("build arguments for "+tc.id, func(t *testing.T) {
+ if got := tc.c.Args(tc.bus); !slices.Equal(got, tc.want) {
+ t.Errorf("Args(%q) = %v, want %v",
+ tc.bus,
+ got, tc.want)
+ }
+ })
+ }
+}
+
+func TestNewConfigFromFile(t *testing.T) {
+ for _, tc := range testCases() {
+ name := new(strings.Builder)
+ name.WriteString("parse configuration file for application ")
+ name.WriteString(tc.id)
+ if tc.wantErr {
+ name.WriteString(" with unexpected results")
+ }
+
+ samplePath := path.Join("testdata", tc.id+".json")
+
+ t.Run(name.String(), func(t *testing.T) {
+ got, err := dbus.NewConfigFromFile(samplePath)
+ if err != nil {
+ t.Errorf("NewConfigFromFile(%q) error = %v",
+ samplePath,
+ err)
+ return
+ }
+ if !tc.wantErr && !reflect.DeepEqual(got, tc.c) {
+ t.Errorf("NewConfigFromFile(%q) got = %v, want %v",
+ samplePath,
+ got, tc.c)
+ }
+ if tc.wantErr && reflect.DeepEqual(got, tc.c) {
+ t.Errorf("NewConfigFromFile(%q) got = %v, wantErr %v",
+ samplePath,
+ got, tc.wantErr)
+ }
+ })
+ }
+}
+
+func TestNewConfig(t *testing.T) {
+ ids := [...]string{"org.chromium.Chromium", "dev.vencord.Vesktop"}
+
+ type newTestCase struct {
+ id string
+ args [2]bool
+ want *dbus.Config
+ }
+
+ // populate tests from IDs in generic tests
+ tcs := make([]newTestCase, 0, (len(ids)+1)*4)
+ // tests for defaults without id
+ tcs = append(tcs,
+ newTestCase{"", [2]bool{false, false}, &dbus.Config{
+ Call: make(map[string]string),
+ Broadcast: make(map[string]string),
+ Filter: true,
+ }},
+ newTestCase{"", [2]bool{false, true}, &dbus.Config{
+ Call: make(map[string]string),
+ Broadcast: make(map[string]string),
+ Filter: true,
+ }},
+ newTestCase{"", [2]bool{true, false}, &dbus.Config{
+ Talk: []string{"org.freedesktop.DBus", "org.freedesktop.Notifications"},
+ Call: map[string]string{"org.freedesktop.portal.*": "*"},
+ Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"},
+ Filter: true,
+ }},
+ newTestCase{"", [2]bool{true, true}, &dbus.Config{
+ Talk: []string{"org.freedesktop.DBus", "org.freedesktop.Notifications"},
+ Call: map[string]string{"org.freedesktop.portal.*": "*"},
+ Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"},
+ Filter: true,
+ }},
+ )
+ for _, id := range ids {
+ tcs = append(tcs,
+ newTestCase{id, [2]bool{false, false}, &dbus.Config{
+ Call: make(map[string]string),
+ Broadcast: make(map[string]string),
+ Filter: true,
+ }},
+ newTestCase{id, [2]bool{false, true}, &dbus.Config{
+ Call: make(map[string]string),
+ Broadcast: make(map[string]string),
+ Filter: true,
+ }},
+ newTestCase{id, [2]bool{true, false}, &dbus.Config{
+ Talk: []string{"org.freedesktop.DBus", "org.freedesktop.Notifications"},
+ Own: []string{id + ".*"},
+ Call: map[string]string{"org.freedesktop.portal.*": "*"},
+ Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"},
+ Filter: true,
+ }},
+ newTestCase{id, [2]bool{true, true}, &dbus.Config{
+ Talk: []string{"org.freedesktop.DBus", "org.freedesktop.Notifications"},
+ Own: []string{id + ".*", "org.mpris.MediaPlayer2." + id + ".*"},
+ Call: map[string]string{"org.freedesktop.portal.*": "*"},
+ Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"},
+ Filter: true,
+ }},
+ )
+ }
+
+ for _, tc := range tcs {
+ name := new(strings.Builder)
+ name.WriteString("create new configuration struct")
+
+ if tc.args[0] {
+ name.WriteString(" with builtin defaults")
+ if tc.args[1] {
+ name.WriteString(" (mpris)")
+ }
+ }
+
+ if tc.id != "" {
+ name.WriteString(" for application ID ")
+ name.WriteString(tc.id)
+ }
+
+ t.Run(name.String(), func(t *testing.T) {
+ if gotC := dbus.NewConfig(tc.id, tc.args[0], tc.args[1]); !reflect.DeepEqual(gotC, tc.want) {
+ t.Errorf("NewConfig(%q, %t, %t) = %v, want %v",
+ tc.id, tc.args[0], tc.args[1],
+ gotC, tc.want)
+ }
+ })
+ }
+}
diff --git a/dbus/dbus_test.go b/dbus/dbus_test.go
new file mode 100644
index 00000000..e2e27956
--- /dev/null
+++ b/dbus/dbus_test.go
@@ -0,0 +1,132 @@
+package dbus_test
+
+import (
+ "errors"
+ "strings"
+ "testing"
+
+ "git.ophivana.moe/cat/fortify/dbus"
+)
+
+const (
+ binPath = "/usr/bin/bwrap"
+)
+
+func TestNew(t *testing.T) {
+ for _, tc := range [][2][2]string{
+ {
+ {"unix:path=/run/user/1971/bus", "/tmp/fortify.1971/1ca5d183ef4c99e74c3e544715f32702/bus"},
+ {"unix:path=/run/dbus/system_bus_socket", "/tmp/fortify.1971/1ca5d183ef4c99e74c3e544715f32702/system_bus_socket"},
+ },
+ {
+ {"unix:path=/run/user/1971/bus", "/tmp/fortify.1971/881ac3796ff3f3bf0a773824383187a0/bus"},
+ {"unix:path=/run/dbus/system_bus_socket", "/tmp/fortify.1971/881ac3796ff3f3bf0a773824383187a0/system_bus_socket"},
+ },
+ {
+ {"unix:path=/run/user/1971/bus", "/tmp/fortify.1971/3d1a5084520ef79c0c6a49a675bac701/bus"},
+ {"unix:path=/run/dbus/system_bus_socket", "/tmp/fortify.1971/3d1a5084520ef79c0c6a49a675bac701/system_bus_socket"},
+ },
+ {
+ {"unix:path=/run/user/1971/bus", "/tmp/fortify.1971/2a1639bab712799788ea0ff7aa280c35/bus"},
+ {"unix:path=/run/dbus/system_bus_socket", "/tmp/fortify.1971/2a1639bab712799788ea0ff7aa280c35/system_bus_socket"},
+ },
+ } {
+ t.Run("create instance for "+tc[0][0]+" and "+tc[1][0], func(t *testing.T) {
+ if got := dbus.New(binPath, tc[0], tc[1]); !got.CompareTestNew(binPath, tc[0], tc[1]) {
+ t.Errorf("New(%q, %q, %q) = %v",
+ binPath, tc[0], tc[1],
+ got)
+ }
+ })
+ }
+}
+
+func TestProxy_Seal(t *testing.T) {
+ ep := dbus.New(binPath, [2]string{}, [2]string{})
+ if err := ep.Seal(nil, nil); !errors.Is(err, dbus.ErrConfig) {
+ t.Errorf("Seal(nil, nil) error = %v, want %v",
+ err, dbus.ErrConfig)
+ }
+
+ for id, tc := range testCasePairs() {
+ t.Run("create seal for "+id, func(t *testing.T) {
+ p := dbus.New(binPath, tc[0].bus, tc[1].bus)
+ if err := p.Seal(tc[0].c, tc[1].c); (err != nil) != tc[0].wantErr {
+ t.Errorf("Seal(%p, %p) error = %v, wantErr %v",
+ tc[0].c, tc[1].c,
+ err, tc[0].wantErr)
+ return
+ }
+
+ // rest of the tests happen for sealed instances
+ if tc[0].wantErr {
+ return
+ }
+
+ // build null-terminated string from wanted args
+ want := new(strings.Builder)
+ args := append(tc[0].want, tc[1].want...)
+ for _, arg := range args {
+ want.WriteString(arg)
+ want.WriteByte('\x00')
+ }
+
+ wt := p.AccessTestProxySeal()
+ got := new(strings.Builder)
+ if _, err := wt.WriteTo(got); err != nil {
+ t.Errorf("p.seal.WriteTo(): %v", err)
+ }
+
+ if want.String() != got.String() {
+ t.Errorf("Seal(%p, %p) seal = %v, want %v",
+ tc[0].c, tc[1].c,
+ got.String(), want.String())
+ }
+ })
+ }
+}
+
+func TestProxy_Seal_Panic(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("Seal: did not panic from repeated seal")
+ }
+ }()
+
+ p := dbus.New(binPath, [2]string{}, [2]string{})
+ _ = p.Seal(dbus.NewConfig("", true, false), nil)
+ _ = p.Seal(dbus.NewConfig("", true, false), nil)
+}
+
+func TestProxy_String(t *testing.T) {
+ for id, tc := range testCasePairs() {
+ // this test does not test errors
+ if tc[0].wantErr {
+ continue
+ }
+
+ t.Run("strings for "+id, func(t *testing.T) {
+ p := dbus.New(binPath, tc[0].bus, tc[1].bus)
+
+ // test unsealed behaviour
+ want := "(unsealed dbus proxy)"
+ if got := p.String(); got != want {
+ t.Errorf("String() = %v, want %v",
+ got, want)
+ }
+
+ if err := p.Seal(tc[0].c, tc[1].c); err != nil {
+ t.Errorf("Seal(%p, %p) error = %v, wantErr %v",
+ tc[0].c, tc[1].c,
+ err, tc[0].wantErr)
+ }
+
+ // test sealed behaviour
+ want = strings.Join(append(tc[0].want, tc[1].want...), " ")
+ if got := p.String(); got != want {
+ t.Errorf("String() = %v, want %v",
+ got, want)
+ }
+ })
+ }
+}
diff --git a/dbus/export_test.go b/dbus/export_test.go
new file mode 100644
index 00000000..dff02412
--- /dev/null
+++ b/dbus/export_test.go
@@ -0,0 +1,13 @@
+package dbus
+
+import "io"
+
+// CompareTestNew provides TestNew with comparison access to unexported Proxy fields.
+func (p *Proxy) CompareTestNew(path string, session, system [2]string) bool {
+ return path == p.path && session == p.session && system == p.system
+}
+
+// AccessTestProxySeal provides TestProxy_Seal with access to unexported Proxy seal field.
+func (p *Proxy) AccessTestProxySeal() io.WriterTo {
+ return p.seal
+}
diff --git a/dbus/samples_test.go b/dbus/samples_test.go
new file mode 100644
index 00000000..8fde1d77
--- /dev/null
+++ b/dbus/samples_test.go
@@ -0,0 +1,167 @@
+package dbus_test
+
+import (
+ "sync"
+
+ "git.ophivana.moe/cat/fortify/dbus"
+)
+
+var samples = []dbusTestCase{
+ {
+ "org.chromium.Chromium", &dbus.Config{
+ See: nil,
+ Talk: []string{"org.freedesktop.Notifications", "org.freedesktop.FileManager1", "org.freedesktop.ScreenSaver"},
+ Own: []string{"org.chromium.Chromium.*", "org.mpris.MediaPlayer2.org.chromium.Chromium.*", "org.mpris.MediaPlayer2.chromium.*"},
+ Call: map[string]string{"org.freedesktop.portal.*": "*"},
+ Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"},
+ Log: false,
+ Filter: true,
+ }, false,
+ [2]string{"unix:path=/run/user/1971/bus", "/tmp/fortify.1971/12622d846cc3fe7b4c10359d01f0eb47/bus"},
+ []string{
+ "unix:path=/run/user/1971/bus",
+ "/tmp/fortify.1971/12622d846cc3fe7b4c10359d01f0eb47/bus",
+ "--filter",
+ "--talk=org.freedesktop.Notifications",
+ "--talk=org.freedesktop.FileManager1",
+ "--talk=org.freedesktop.ScreenSaver",
+ "--own=org.chromium.Chromium.*",
+ "--own=org.mpris.MediaPlayer2.org.chromium.Chromium.*",
+ "--own=org.mpris.MediaPlayer2.chromium.*",
+ "--call=org.freedesktop.portal.*=*",
+ "--broadcast=org.freedesktop.portal.*=@/org/freedesktop/portal/*",
+ },
+ },
+ {
+ "org.chromium.Chromium*", &dbus.Config{
+ See: nil,
+ Talk: []string{"org.freedesktop.Avahi", "org.freedesktop.UPower"},
+ Own: nil,
+ Call: nil,
+ Broadcast: nil,
+ Log: false,
+ Filter: true,
+ }, false,
+ [2]string{"unix:path=/run/dbus/system_bus_socket", "/tmp/fortify.1971/12622d846cc3fe7b4c10359d01f0eb47/system_bus_socket"},
+ []string{"unix:path=/run/dbus/system_bus_socket",
+ "/tmp/fortify.1971/12622d846cc3fe7b4c10359d01f0eb47/system_bus_socket",
+ "--filter",
+ "--talk=org.freedesktop.Avahi",
+ "--talk=org.freedesktop.UPower",
+ },
+ },
+
+ {
+ "dev.vencord.Vesktop", &dbus.Config{
+ See: nil,
+ Talk: []string{"org.freedesktop.Notifications", "org.kde.StatusNotifierWatcher"},
+ Own: []string{"dev.vencord.Vesktop.*", "org.mpris.MediaPlayer2.dev.vencord.Vesktop.*"},
+ Call: map[string]string{"org.freedesktop.portal.*": "*"},
+ Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"},
+ Log: false,
+ Filter: true,
+ }, false,
+ [2]string{"unix:path=/run/user/1971/bus", "/tmp/fortify.1971/34c24f16a0d791d28835ededaf446033/bus"},
+ []string{
+ "unix:path=/run/user/1971/bus",
+ "/tmp/fortify.1971/34c24f16a0d791d28835ededaf446033/bus",
+ "--filter",
+ "--talk=org.freedesktop.Notifications",
+ "--talk=org.kde.StatusNotifierWatcher",
+ "--own=dev.vencord.Vesktop.*",
+ "--own=org.mpris.MediaPlayer2.dev.vencord.Vesktop.*",
+ "--call=org.freedesktop.portal.*=*",
+ "--broadcast=org.freedesktop.portal.*=@/org/freedesktop/portal/*"},
+ },
+}
+
+type dbusTestCase struct {
+ id string
+ c *dbus.Config
+ wantErr bool
+ bus [2]string
+ want []string
+}
+
+var (
+ testCasesV []dbusTestCase
+ testCasePairsV map[string][2]dbusTestCase
+
+ testCaseOnce sync.Once
+)
+
+func testCases() []dbusTestCase {
+ testCaseOnce.Do(testCaseGenerate)
+ return testCasesV
+}
+
+func testCasePairs() map[string][2]dbusTestCase {
+ testCaseOnce.Do(testCaseGenerate)
+ return testCasePairsV
+}
+
+func injectNulls(t *[]string) {
+ f := make([]string, len(*t))
+ for i := range f {
+ f[i] = "\x00" + (*t)[i] + "\x00"
+ }
+ *t = f
+}
+
+func testCaseGenerate() {
+ // create null-injected test cases
+ testCasesV = make([]dbusTestCase, len(samples)*2)
+ for i := range samples {
+ testCasesV[i] = samples[i]
+ testCasesV[len(samples)+i] = samples[i]
+ testCasesV[len(samples)+i].c = new(dbus.Config)
+ *testCasesV[len(samples)+i].c = *samples[i].c
+
+ // inject nulls
+ fi := &testCasesV[len(samples)+i]
+ fi.wantErr = true
+ fi.c = &*fi.c
+
+ injectNulls(&fi.c.See)
+ injectNulls(&fi.c.Talk)
+ injectNulls(&fi.c.Own)
+ }
+
+ // enumerate test case pairs
+ var pc int
+ for _, tc := range samples {
+ if tc.id != "" {
+ pc++
+ }
+ }
+ testCasePairsV = make(map[string][2]dbusTestCase, pc)
+ for i, tc := range testCasesV {
+ if tc.id == "" {
+ continue
+ }
+
+ // skip already enumerated system bus test
+ if tc.id[len(tc.id)-1] == '*' {
+ continue
+ }
+
+ ftp := [2]dbusTestCase{tc}
+
+ // system proxy tests always place directly after its user counterpart with id ending in *
+ if i+1 < len(testCasesV) && testCasesV[i+1].id[len(testCasesV[i+1].id)-1] == '*' {
+ // attach system bus config
+ ftp[1] = testCasesV[i+1]
+
+ // check for misplaced/mismatching tests
+ if ftp[0].wantErr != ftp[1].wantErr || ftp[0].id+"*" != ftp[1].id {
+ panic("mismatching session/system pairing")
+ }
+ }
+
+ k := tc.id
+ if tc.wantErr {
+ k = "malformed_" + k
+ }
+ testCasePairsV[k] = ftp
+ }
+}
diff --git a/dbus/testdata/dev.vencord.Vesktop.json b/dbus/testdata/dev.vencord.Vesktop.json
new file mode 100644
index 00000000..05259774
--- /dev/null
+++ b/dbus/testdata/dev.vencord.Vesktop.json
@@ -0,0 +1,18 @@
+{
+ "talk":[
+ "org.freedesktop.Notifications",
+ "org.kde.StatusNotifierWatcher"
+ ],
+ "own":[
+ "dev.vencord.Vesktop.*",
+ "org.mpris.MediaPlayer2.dev.vencord.Vesktop.*"
+ ],
+ "call":{
+ "org.freedesktop.portal.*":"*"
+ },
+ "broadcast":{
+ "org.freedesktop.portal.*":"@/org/freedesktop/portal/*"
+ },
+
+ "filter":true
+}
diff --git a/dbus/testdata/org.chromium.Chromium*.json b/dbus/testdata/org.chromium.Chromium*.json
new file mode 100644
index 00000000..977c26f5
--- /dev/null
+++ b/dbus/testdata/org.chromium.Chromium*.json
@@ -0,0 +1,8 @@
+{
+ "talk":[
+ "org.freedesktop.Avahi",
+ "org.freedesktop.UPower"
+ ],
+
+ "filter":true
+} \ No newline at end of file
diff --git a/dbus/testdata/org.chromium.Chromium.json b/dbus/testdata/org.chromium.Chromium.json
new file mode 100644
index 00000000..8b930236
--- /dev/null
+++ b/dbus/testdata/org.chromium.Chromium.json
@@ -0,0 +1,20 @@
+{
+ "talk":[
+ "org.freedesktop.Notifications",
+ "org.freedesktop.FileManager1",
+ "org.freedesktop.ScreenSaver"
+ ],
+ "own":[
+ "org.chromium.Chromium.*",
+ "org.mpris.MediaPlayer2.org.chromium.Chromium.*",
+ "org.mpris.MediaPlayer2.chromium.*"
+ ],
+ "call":{
+ "org.freedesktop.portal.*":"*"
+ },
+ "broadcast":{
+ "org.freedesktop.portal.*":"@/org/freedesktop/portal/*"
+ },
+
+ "filter":true
+}
diff --git a/flake.nix b/flake.nix
index 1d5357db..3a2f6698 100644
--- a/flake.nix
+++ b/flake.nix
@@ -36,7 +36,7 @@
default = nixpkgsFor.${system}.mkShell {
buildInputs =
with nixpkgsFor.${system};
- [ self.packages.${system}.fortify ] ++ self.packages.${system}.fortify.buildInputs;
+ self.packages.${system}.fortify.buildInputs ++ [ self.packages.${system}.fortify ];
};
});
};