client refactor
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"image"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testExecutor struct {
|
||||
mu sync.Mutex
|
||||
queue []func()
|
||||
}
|
||||
|
||||
func (e *testExecutor) Post(fn func()) {
|
||||
e.mu.Lock()
|
||||
e.queue = append(e.queue, fn)
|
||||
e.mu.Unlock()
|
||||
}
|
||||
|
||||
func (e *testExecutor) FlushAll() {
|
||||
for {
|
||||
var fn func()
|
||||
e.mu.Lock()
|
||||
if len(e.queue) > 0 {
|
||||
fn = e.queue[0]
|
||||
e.queue = e.queue[1:]
|
||||
}
|
||||
e.mu.Unlock()
|
||||
if fn == nil {
|
||||
return
|
||||
}
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
type testRefresher struct {
|
||||
mu sync.Mutex
|
||||
count int
|
||||
}
|
||||
|
||||
func (r *testRefresher) Refresh() {
|
||||
r.mu.Lock()
|
||||
r.count++
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
func (r *testRefresher) Count() int {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
return r.count
|
||||
}
|
||||
|
||||
func TestRasterCoalescer_RequestBeforeDraw_CoalescesToLatest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
exec := &testExecutor{}
|
||||
ref := &testRefresher{}
|
||||
|
||||
var got []int
|
||||
|
||||
co := NewRasterCoalescer(exec, ref, func(w, h int, p int) image.Image {
|
||||
got = append(got, p)
|
||||
return image.NewRGBA(image.Rect(0, 0, w, h))
|
||||
})
|
||||
|
||||
co.Request(1)
|
||||
co.Request(2)
|
||||
co.Request(3)
|
||||
|
||||
// Only a single refresh should be scheduled before the next Draw().
|
||||
exec.FlushAll()
|
||||
require.Equal(t, 1, ref.Count())
|
||||
|
||||
_ = co.Draw(10, 10)
|
||||
require.Equal(t, []int{3}, got)
|
||||
}
|
||||
|
||||
func TestRasterCoalescer_RequestDuringDraw_SchedulesOneFollowUpRefresh(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
exec := &testExecutor{}
|
||||
ref := &testRefresher{}
|
||||
var got []int
|
||||
|
||||
var co *RasterCoalescer[int]
|
||||
co = NewRasterCoalescer(exec, ref, func(w, h int, p int) image.Image {
|
||||
got = append(got, p)
|
||||
if p == 1 {
|
||||
co.Request(2)
|
||||
co.Request(3)
|
||||
}
|
||||
return image.NewRGBA(image.Rect(0, 0, w, h))
|
||||
})
|
||||
|
||||
co.Request(1)
|
||||
|
||||
exec.FlushAll()
|
||||
require.Equal(t, 1, ref.Count())
|
||||
|
||||
// First draw renders 1 and schedules exactly one additional refresh.
|
||||
_ = co.Draw(10, 10)
|
||||
exec.FlushAll()
|
||||
require.Equal(t, 2, ref.Count())
|
||||
|
||||
// Second draw renders latest (3).
|
||||
_ = co.Draw(10, 10)
|
||||
require.Equal(t, []int{1, 3}, got)
|
||||
}
|
||||
|
||||
func TestRasterCoalescer_ManyRequestsWhileDrawing_StillOnlyOneExtraRefresh(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
exec := &testExecutor{}
|
||||
ref := &testRefresher{}
|
||||
var got []int
|
||||
|
||||
var co *RasterCoalescer[int]
|
||||
co = NewRasterCoalescer(exec, ref, func(w, h int, p int) image.Image {
|
||||
got = append(got, p)
|
||||
if p == 1 {
|
||||
for i := 2; i <= 50; i++ {
|
||||
co.Request(i)
|
||||
}
|
||||
}
|
||||
return image.NewRGBA(image.Rect(0, 0, w, h))
|
||||
})
|
||||
|
||||
co.Request(1)
|
||||
exec.FlushAll()
|
||||
require.Equal(t, 1, ref.Count())
|
||||
|
||||
_ = co.Draw(10, 10)
|
||||
exec.FlushAll()
|
||||
require.Equal(t, 2, ref.Count())
|
||||
|
||||
_ = co.Draw(10, 10)
|
||||
require.Equal(t, []int{1, 50}, got)
|
||||
}
|
||||
|
||||
func TestCopyViewportRGBA_CopiesROIAndIsIndependentFromSource(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
src := image.NewRGBA(image.Rect(0, 0, 20, 20))
|
||||
dst := image.NewRGBA(image.Rect(0, 0, 5, 6))
|
||||
|
||||
// Fill src with a pattern: pixel (x,y) has RGBA = (x, y, 0, 255).
|
||||
for y := 0; y < 20; y++ {
|
||||
for x := 0; x < 20; x++ {
|
||||
off := y*src.Stride + x*4
|
||||
src.Pix[off+0] = byte(x)
|
||||
src.Pix[off+1] = byte(y)
|
||||
src.Pix[off+2] = 0
|
||||
src.Pix[off+3] = 255
|
||||
}
|
||||
}
|
||||
|
||||
marginX, marginY := 7, 9
|
||||
copyViewportRGBA(dst, src, marginX, marginY, 5, 6)
|
||||
|
||||
// Verify a few pixels in dst match the expected source ROI.
|
||||
// dst(0,0) == src(marginX, marginY)
|
||||
{
|
||||
off := 0*dst.Stride + 0*4
|
||||
require.Equal(t, byte(marginX), dst.Pix[off+0])
|
||||
require.Equal(t, byte(marginY), dst.Pix[off+1])
|
||||
require.Equal(t, byte(255), dst.Pix[off+3])
|
||||
}
|
||||
// dst(4,5) == src(marginX+4, marginY+5)
|
||||
{
|
||||
off := 5*dst.Stride + 4*4
|
||||
require.Equal(t, byte(marginX+4), dst.Pix[off+0])
|
||||
require.Equal(t, byte(marginY+5), dst.Pix[off+1])
|
||||
require.Equal(t, byte(255), dst.Pix[off+3])
|
||||
}
|
||||
|
||||
// Mutate src ROI after copy and ensure dst is unchanged (no aliasing).
|
||||
{
|
||||
off := (marginY+0)*src.Stride + (marginX+0)*4
|
||||
src.Pix[off+0] = 200
|
||||
src.Pix[off+1] = 201
|
||||
src.Pix[off+3] = 123
|
||||
}
|
||||
|
||||
offDst := 0*dst.Stride + 0*4
|
||||
require.Equal(t, byte(marginX), dst.Pix[offDst+0])
|
||||
require.Equal(t, byte(marginY), dst.Pix[offDst+1])
|
||||
require.Equal(t, byte(255), dst.Pix[offDst+3])
|
||||
}
|
||||
|
||||
func TestEventPosToPixel_FloorMapping(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := &client{}
|
||||
|
||||
// Pretend raster logical is 100x50, pixel is 1000x500.
|
||||
e.metaMu.Lock()
|
||||
e.lastRasterLogicW = 100
|
||||
e.lastRasterLogicH = 50
|
||||
e.lastRasterPxW = 1000
|
||||
e.lastRasterPxH = 500
|
||||
e.metaMu.Unlock()
|
||||
|
||||
x, y, ok := e.eventPosToPixel(0, 0)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, 0, x)
|
||||
require.Equal(t, 0, y)
|
||||
|
||||
// Middle
|
||||
x, y, ok = e.eventPosToPixel(50, 25)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, 500, x)
|
||||
require.Equal(t, 250, y)
|
||||
|
||||
// Near max logical should map near max pixel with floor.
|
||||
x, y, ok = e.eventPosToPixel(99.9, 49.9)
|
||||
require.True(t, ok)
|
||||
require.GreaterOrEqual(t, x, 998)
|
||||
require.GreaterOrEqual(t, y, 498)
|
||||
|
||||
// Clamp
|
||||
x, y, ok = e.eventPosToPixel(-10, 999)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, 0, x)
|
||||
require.Equal(t, 500, y)
|
||||
}
|
||||
Reference in New Issue
Block a user