129 lines
4.0 KiB
Go
129 lines
4.0 KiB
Go
package wshub
|
|
|
|
import "testing"
|
|
|
|
func heldRamp(t0, dt float64, n int) sigData {
|
|
sd := sigData{T: make([]float64, n), V: make([]float64, n)}
|
|
for i := range sd.T {
|
|
sd.T[i] = t0 + float64(i)*dt
|
|
sd.V[i] = float64(i)
|
|
}
|
|
return sd
|
|
}
|
|
|
|
func TestCaptureHoldServesRangesInsideTheWindow(t *testing.T) {
|
|
var ch captureHold
|
|
ch.publish(0, 10, map[string]sigData{"s1:sig": heldRamp(0, 0.1, 101)})
|
|
|
|
gt, gv, ok := ch.slice("s1:sig", 2, 3)
|
|
if !ok {
|
|
t.Fatal("held capture declined a range inside its window")
|
|
}
|
|
if gt[0] < 2 || gt[len(gt)-1] > 3 {
|
|
t.Fatalf("range %v..%v escapes the request 2..3", gt[0], gt[len(gt)-1])
|
|
}
|
|
if len(gt) != len(gv) {
|
|
t.Fatalf("t/v length mismatch: %d vs %d", len(gt), len(gv))
|
|
}
|
|
if gv[0] != 20 {
|
|
t.Fatalf("first value %v, want the sample at t=2", gv[0])
|
|
}
|
|
}
|
|
|
|
// A range poking outside the capture is a live zoom: only the rings still track
|
|
// the stream, so the hold must stand aside rather than answer a clipped range.
|
|
func TestCaptureHoldDeclinesRangesOutsideTheWindow(t *testing.T) {
|
|
var ch captureHold
|
|
ch.publish(0, 10, map[string]sigData{"s1:sig": heldRamp(0, 0.1, 101)})
|
|
|
|
for _, r := range [][2]float64{{-1, 5}, {5, 11}, {20, 30}, {-5, -1}} {
|
|
if _, _, ok := ch.slice("s1:sig", r[0], r[1]); ok {
|
|
t.Fatalf("held capture answered %v..%v, which is not inside 0..10", r[0], r[1])
|
|
}
|
|
}
|
|
if _, _, ok := ch.slice("other:sig", 2, 3); ok {
|
|
t.Fatal("held capture answered for a signal it does not hold")
|
|
}
|
|
}
|
|
|
|
func TestCaptureHoldZeroValueAndClearDecline(t *testing.T) {
|
|
var ch captureHold
|
|
if _, _, ok := ch.slice("s1:sig", 0, 1); ok {
|
|
t.Fatal("empty hold answered a request")
|
|
}
|
|
ch.publish(0, 10, map[string]sigData{"s1:sig": heldRamp(0, 0.1, 101)})
|
|
ch.clear()
|
|
if _, _, ok := ch.slice("s1:sig", 2, 3); ok {
|
|
t.Fatal("cleared hold still answered a request")
|
|
}
|
|
}
|
|
|
|
// The point of the double buffer: the window a client is exploring survives the
|
|
// re-armed acquisition rolling the rings past it, and is replaced only when the
|
|
// next shot completes.
|
|
func TestZoomIntoACaptureSurvivesTheRingRollingPast(t *testing.T) {
|
|
h := NewHub()
|
|
rb := newSigRing(4000)
|
|
h.rings["s1:sig"] = rb
|
|
|
|
// 2 s of 1 kSps, then fire a trigger over [0.5, 1.5].
|
|
ts, vs := make([]float64, 2000), make([]float64, 2000)
|
|
for i := range ts {
|
|
ts[i], vs[i] = float64(i)*1e-3, float64(i)
|
|
}
|
|
rb.write(ts, vs)
|
|
if msg := h.buildTriggerCapture(1.0, 0.5, 0.5); msg == nil {
|
|
t.Fatal("buildTriggerCapture produced no frame")
|
|
}
|
|
|
|
// The trigger re-arms and the stream runs on until the captured window has
|
|
// been overwritten several times over.
|
|
for pass := 0; pass < 5; pass++ {
|
|
for i := range ts {
|
|
ts[i] += 2.0
|
|
}
|
|
rb.write(ts, vs)
|
|
}
|
|
if rt, _ := rb.slice(0.5, 1.5); len(rt) != 0 {
|
|
t.Fatalf("ring still holds %d points of the captured window; the test is not exercising the hold", len(rt))
|
|
}
|
|
|
|
got := h.zoomSlice(0.8, 0.9, []string{"s1:sig"}, 1<<30)
|
|
sd, ok := got["s1:sig"]
|
|
if !ok {
|
|
t.Fatal("zoom into the held capture returned nothing")
|
|
}
|
|
if len(sd.T) != 101 {
|
|
t.Fatalf("zoom returned %d points, want the 101 samples in 0.8..0.9", len(sd.T))
|
|
}
|
|
if sd.V[0] != 800 || sd.V[len(sd.V)-1] != 900 {
|
|
t.Fatalf("zoom returned values %v..%v, want 800..900", sd.V[0], sd.V[len(sd.V)-1])
|
|
}
|
|
|
|
// A live zoom outside the held window still reaches the rings.
|
|
if live := h.zoomSlice(11.0, 11.1, []string{"s1:sig"}, 1<<30); len(live["s1:sig"].T) == 0 {
|
|
t.Fatal("live zoom outside the capture was swallowed by the hold")
|
|
}
|
|
}
|
|
|
|
// A shot that yields nothing must not blank the window already on screen.
|
|
func TestEmptyCaptureKeepsThePreviousHold(t *testing.T) {
|
|
h := NewHub()
|
|
rb := newSigRing(4000)
|
|
h.rings["s1:sig"] = rb
|
|
ts, vs := make([]float64, 2000), make([]float64, 2000)
|
|
for i := range ts {
|
|
ts[i], vs[i] = float64(i)*1e-3, float64(i)
|
|
}
|
|
rb.write(ts, vs)
|
|
h.buildTriggerCapture(1.0, 0.5, 0.5)
|
|
|
|
// A window the rings have no samples for at all.
|
|
if msg := h.buildTriggerCapture(500.0, 0.5, 0.5); msg != nil {
|
|
t.Fatal("capture of an empty window produced a frame")
|
|
}
|
|
if _, _, ok := h.capture.slice("s1:sig", 0.8, 0.9); !ok {
|
|
t.Fatal("empty capture dropped the previously held window")
|
|
}
|
|
}
|