~jcc/swaybar-commander

0aa53e7372e580f488ea131bd60bd2bf9c02b7be — Jason Cox 1 year, 8 months ago 2c009c5
Allow triggering block updates with signals
3 files changed, 76 insertions(+), 4 deletions(-)

M block/block.go
M block/block_test.go
M commander.go
M block/block.go => block/block.go +11 -2
@@ 6,7 6,9 @@ import (
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"git.sr.ht/~jcc/swaybar-commander/config"


@@ 34,7 36,10 @@ func New(conf config.Block) Block {

// Run runs a Block indefinitely, sending updated swaybar protocol blocks on the
// provided channel whenever there is a change.
func (b *Block) Run(updateChan chan<- proto.Block) {
func (b *Block) Run(updateChan chan<- proto.Block, updateRequestSig syscall.Signal) {
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, updateRequestSig)

	b.ticker = time.NewTicker(b.pollFreq)
	defer b.ticker.Stop()



@@ 65,6 70,10 @@ func (b *Block) Run(updateChan chan<- proto.Block) {
			updateChan <- pBlock
		}

		<-b.ticker.C
		select {
		case <-b.ticker.C:
		case <-sigChan:
			b.ticker.Reset(b.pollFreq)
		}
	}
}

M block/block_test.go => block/block_test.go +61 -1
@@ 6,12 6,15 @@ import (
	"os/exec"
	"reflect"
	"strconv"
	"syscall"
	"testing"
	"time"

	"git.sr.ht/~jcc/swaybar-commander/proto"
)

const sigrtmin = 34

func TestRun(t *testing.T) {
	hi := "hi"



@@ 113,7 116,7 @@ func TestRun(t *testing.T) {

		actual := []proto.Block{}

		go block.Run(updateChan)
		go block.Run(updateChan, syscall.Signal(sigrtmin+1))

		for i := 0; i < len(c.expected); i++ {
			actual = append(actual, <-updateChan)


@@ 127,6 130,63 @@ func TestRun(t *testing.T) {
	}
}

func TestRunUpdateRequest(t *testing.T) {
	block := Block{
		cmd:      []string{"cmd", "test", "args"},
		pollFreq: 100 * time.Second,
	}

	execCommand = makeFakeExecCommand(
		t,
		[]string{"a", "b"},
		[]int{0, 0},
		block.cmd[0],
		block.cmd[1:]...,
	)

	updateChan := make(chan proto.Block, 1)
	sig := syscall.Signal(sigrtmin + 1)

	getUpdateInMax := func(d time.Duration) *proto.Block {
		delayChan := make(chan bool, 1)

		go func() {
			time.Sleep(d)
			delayChan <- true
		}()

		select {
		case update := <-updateChan:
			return &update
		case <-delayChan:
			return nil
		}
	}

	go block.Run(updateChan, sig)

	first := <-updateChan
	if first.FullText != "a" {
		t.Fatalf("want %v first, got %v", "a", first.FullText)
	}

	second := getUpdateInMax(25 * time.Millisecond)
	if second != nil {
		t.Fatalf("want no update, got update %v", second)
	}

	process, err := os.FindProcess(os.Getpid())
	if err != nil {
		t.Fatalf("want process, got err %v", err)
	}

	process.Signal(sig)
	second = getUpdateInMax(25 * time.Millisecond)
	if second.FullText != "b" {
		t.Fatalf("want %v second, got %v", "b", second.FullText)
	}
}

// modified hacks to mock exec.Command -- see
// https://stackoverflow.com/questions/71102318/how-to-mock-exec-cmd-exec-command
func makeFakeExecCommand(

M commander.go => commander.go +4 -1
@@ 7,6 7,7 @@ import (
	"fmt"
	"os"
	"sync"
	"syscall"
	"time"

	"git.sr.ht/~jcc/swaybar-commander/block"


@@ 15,6 16,8 @@ import (
	"github.com/bep/debounce"
)

const sigrtmin = 34

func main() {
	conf, err := config.Load()
	if err != nil {


@@ 41,7 44,7 @@ func main() {
		go func(i int) {
			defer wg.Done()
			updateChan := make(chan proto.Block, 1)
			go blocks[i].Run(updateChan)
			go blocks[i].Run(updateChan, syscall.Signal(sigrtmin+i+1))

			for {
				latestPBlocks[i] = <-updateChan