mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-15 07:44:01 -04:00
164 lines
5.4 KiB
Go
164 lines
5.4 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"testing"
|
|
"testing/synctest"
|
|
"time"
|
|
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/apppicker"
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput"
|
|
"github.com/AvengeMedia/dankgo/files"
|
|
"github.com/AvengeMedia/dankgo/ipc"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestSubscribeStreams(t *testing.T) {
|
|
for _, snapshot := range []bool{false, true} {
|
|
t.Run(fmt.Sprintf("snapshot=%t", snapshot), func(t *testing.T) {
|
|
synctest.Test(t, func(t *testing.T) {
|
|
originalPicker, originalOutput := appPickerManager, wlrOutputManager
|
|
appPickerManager = apppicker.NewManager()
|
|
wlrOutputManager = &wlroutput.Manager{}
|
|
defer func() {
|
|
appPickerManager.Close()
|
|
appPickerManager, wlrOutputManager = originalPicker, originalOutput
|
|
}()
|
|
|
|
services := []any{"browser", 123, "unavailable"}
|
|
if snapshot {
|
|
services = append(services, "wlroutput")
|
|
}
|
|
serverConn, clientConn := net.Pipe()
|
|
defer serverConn.Close()
|
|
defer clientConn.Close()
|
|
writer := ipc.NewConnWriter(serverConn)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
handleSubscribe(context.Background(), writer, ipc.Request{ID: 24, Params: map[string]any{"services": services}})
|
|
}()
|
|
|
|
decoder := json.NewDecoder(clientConn)
|
|
readEvent := func(service string, expected any) {
|
|
t.Helper()
|
|
var response ipc.Response[struct {
|
|
Service string `json:"service"`
|
|
Data json.RawMessage `json:"data"`
|
|
}]
|
|
require.NoError(t, decoder.Decode(&response))
|
|
require.Equal(t, 24, response.ID)
|
|
require.Empty(t, response.Error)
|
|
require.NotNil(t, response.Result)
|
|
require.Equal(t, service, response.Result.Service)
|
|
data, err := json.Marshal(expected)
|
|
require.NoError(t, err)
|
|
require.JSONEq(t, string(data), string(response.Result.Data))
|
|
}
|
|
|
|
readEvent("server", getServerInfo())
|
|
if snapshot {
|
|
readEvent("wlroutput", wlrOutputManager.GetState())
|
|
}
|
|
event := apppicker.OpenEvent{Target: "https://example.com", RequestType: "url"}
|
|
appPickerManager.RequestOpen(event)
|
|
readEvent("browser.open_requested", event)
|
|
|
|
notifyCapabilityChange()
|
|
readEvent("server", getServerInfo())
|
|
require.NoError(t, clientConn.Close())
|
|
appPickerManager.RequestOpen(event)
|
|
<-done
|
|
synctest.Wait()
|
|
_, subscribed := capabilitySubscribers.Load(fmt.Sprintf("meta-client-%p-capabilities", writer))
|
|
require.False(t, subscribed)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSubscribeInitialWriteFailure(t *testing.T) {
|
|
synctest.Test(t, func(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer serverConn.Close()
|
|
require.NoError(t, clientConn.Close())
|
|
writer := ipc.NewConnWriter(serverConn)
|
|
handleSubscribe(context.Background(), writer, ipc.Request{Params: map[string]any{"services": []any{"unavailable"}}})
|
|
synctest.Wait()
|
|
_, subscribed := capabilitySubscribers.Load(fmt.Sprintf("meta-client-%p-capabilities", writer))
|
|
require.False(t, subscribed)
|
|
})
|
|
}
|
|
|
|
func TestSubscribeFilesDeliversWatchBatches(t *testing.T) {
|
|
synctest.Test(t, func(t *testing.T) {
|
|
original := filesService
|
|
filesService = files.NewService(&filesEvents, t.TempDir(), nil)
|
|
defer func() {
|
|
filesService.Close()
|
|
filesService = original
|
|
}()
|
|
|
|
type stream struct {
|
|
conn net.Conn
|
|
decoder *json.Decoder
|
|
cancel context.CancelFunc
|
|
done chan struct{}
|
|
}
|
|
subscribe := func(services ...any) stream {
|
|
serverConn, clientConn := net.Pipe()
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
s := stream{conn: clientConn, decoder: json.NewDecoder(clientConn), cancel: cancel, done: make(chan struct{})}
|
|
go func() {
|
|
defer close(s.done)
|
|
defer serverConn.Close()
|
|
handleSubscribe(ctx, ipc.NewConnWriter(serverConn), ipc.Request{ID: 25, Params: map[string]any{"services": services}})
|
|
}()
|
|
var first ipc.Response[ServiceEvent]
|
|
require.NoError(t, s.decoder.Decode(&first))
|
|
require.Equal(t, "server", first.Result.Service)
|
|
return s
|
|
}
|
|
next := func(s stream) (ipc.Response[json.RawMessage], error) {
|
|
require.NoError(t, s.conn.SetReadDeadline(time.Now().Add(time.Second)))
|
|
var response ipc.Response[json.RawMessage]
|
|
return response, s.decoder.Decode(&response)
|
|
}
|
|
|
|
subscriber := subscribe("browser", "files")
|
|
bystander := subscribe("browser")
|
|
|
|
payload := map[string]any{"watchId": "w1", "kind": "batch", "seq": float64(1), "removed": []any{"old.txt"}}
|
|
filesEvents.Publish("files:w1", payload)
|
|
filesEvents.Publish("sizes", map[string]any{"scanId": "s1", "done": true})
|
|
|
|
response, err := next(subscriber)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 25, response.ID)
|
|
require.JSONEq(t, `{"service":"files","data":{"watchId":"w1","kind":"batch","seq":1,"removed":["old.txt"]}}`, string(*response.Result))
|
|
|
|
_, err = next(subscriber)
|
|
require.True(t, errors.Is(err, os.ErrDeadlineExceeded), "the sizes topic is not forwarded, got %v", err)
|
|
_, err = next(bystander)
|
|
require.True(t, errors.Is(err, os.ErrDeadlineExceeded), "a client without the files service gets no batches, got %v", err)
|
|
|
|
for _, s := range []stream{subscriber, bystander} {
|
|
s.cancel()
|
|
<-s.done
|
|
require.NoError(t, s.conn.Close())
|
|
}
|
|
synctest.Wait()
|
|
subscribers := 0
|
|
filesEvents.subscribers.Range(func(string, chan any) bool {
|
|
subscribers++
|
|
return true
|
|
})
|
|
require.Zero(t, subscribers, "no channel outlives its subscription")
|
|
})
|
|
}
|