Initial go port of epics

This commit is contained in:
Martino Ferrari
2026-04-28 00:09:22 +02:00
parent 47e481a461
commit 6e51ffc5e1
28 changed files with 5634 additions and 178 deletions
+210
View File
@@ -0,0 +1,210 @@
package stub_test
import (
"context"
"testing"
"time"
"github.com/uopi/uopi/internal/datasource"
"github.com/uopi/uopi/internal/datasource/stub"
)
func newStub(t *testing.T) *stub.Stub {
t.Helper()
s := stub.New()
if err := s.Connect(context.Background()); err != nil {
t.Fatalf("Connect: %v", err)
}
return s
}
func TestName(t *testing.T) {
if stub.New().Name() != "stub" {
t.Error("Name() should return 'stub'")
}
}
func TestListSignals(t *testing.T) {
s := newStub(t)
sigs, err := s.ListSignals(context.Background())
if err != nil {
t.Fatalf("ListSignals: %v", err)
}
if len(sigs) == 0 {
t.Fatal("expected at least one signal")
}
names := make(map[string]bool)
for _, m := range sigs {
names[m.Name] = true
}
for _, want := range []string{"sine_1hz", "ramp_10s", "toggle_1hz", "noise", "setpoint"} {
if !names[want] {
t.Errorf("signal %q not in ListSignals", want)
}
}
}
func TestGetMetadata(t *testing.T) {
s := newStub(t)
m, err := s.GetMetadata(context.Background(), "sine_1hz")
if err != nil {
t.Fatalf("GetMetadata: %v", err)
}
if m.Type != datasource.TypeFloat64 {
t.Errorf("Type = %v, want TypeFloat64", m.Type)
}
if m.Unit == "" {
t.Error("expected non-empty Unit")
}
}
func TestGetMetadataNotFound(t *testing.T) {
s := newStub(t)
_, err := s.GetMetadata(context.Background(), "no:such:pv")
if err != datasource.ErrNotFound {
t.Errorf("want ErrNotFound, got %v", err)
}
}
func TestSubscribeReceivesValues(t *testing.T) {
s := newStub(t)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
ch := make(chan datasource.Value, 4)
stop, err := s.Subscribe(ctx, "sine_1hz", ch)
if err != nil {
t.Fatalf("Subscribe: %v", err)
}
defer stop()
select {
case v := <-ch:
if v.Quality != datasource.QualityGood {
t.Errorf("Quality = %v, want Good", v.Quality)
}
if _, ok := v.Data.(float64); !ok {
t.Errorf("Data type = %T, want float64", v.Data)
}
if v.Timestamp.IsZero() {
t.Error("Timestamp is zero")
}
case <-ctx.Done():
t.Fatal("timeout waiting for first value")
}
}
func TestSubscribeUnknown(t *testing.T) {
s := newStub(t)
_, err := s.Subscribe(context.Background(), "no:such", make(chan datasource.Value, 1))
if err != datasource.ErrNotFound {
t.Errorf("want ErrNotFound, got %v", err)
}
}
func TestSubscribeCancel(t *testing.T) {
s := newStub(t)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
ch := make(chan datasource.Value, 4)
stop, err := s.Subscribe(ctx, "ramp_10s", ch)
if err != nil {
t.Fatalf("Subscribe: %v", err)
}
// Drain one value to confirm it's running.
select {
case <-ch:
case <-ctx.Done():
t.Fatal("timeout waiting for first value")
}
stop() // cancel subscription
// Drain remaining buffered values then confirm channel goes quiet.
time.Sleep(200 * time.Millisecond)
for len(ch) > 0 {
<-ch
}
time.Sleep(300 * time.Millisecond)
if len(ch) > 0 {
t.Error("channel still receiving after stop()")
}
}
func TestWriteSetpoint(t *testing.T) {
s := newStub(t)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := s.Write(context.Background(), "setpoint", float64(99.0)); err != nil {
t.Fatalf("Write: %v", err)
}
ch := make(chan datasource.Value, 4)
stop, err := s.Subscribe(ctx, "setpoint", ch)
if err != nil {
t.Fatalf("Subscribe: %v", err)
}
defer stop()
select {
case v := <-ch:
f, ok := v.Data.(float64)
if !ok {
t.Fatalf("Data type = %T, want float64", v.Data)
}
if f != 99.0 {
t.Errorf("setpoint value = %g, want 99.0", f)
}
case <-ctx.Done():
t.Fatal("timeout waiting for setpoint value")
}
}
func TestWriteReadOnly(t *testing.T) {
s := newStub(t)
err := s.Write(context.Background(), "sine_1hz", float64(0))
if err != datasource.ErrNotWritable {
t.Errorf("want ErrNotWritable, got %v", err)
}
}
func TestWriteNotFound(t *testing.T) {
s := newStub(t)
err := s.Write(context.Background(), "no:such", float64(0))
if err != datasource.ErrNotFound {
t.Errorf("want ErrNotFound, got %v", err)
}
}
func TestHistory(t *testing.T) {
s := newStub(t)
_, err := s.History(context.Background(), "sine_1hz", time.Now().Add(-time.Hour), time.Now(), 100)
if err != datasource.ErrHistoryUnavailable {
t.Errorf("want ErrHistoryUnavailable, got %v", err)
}
}
func TestToggleBool(t *testing.T) {
s := newStub(t)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
ch := make(chan datasource.Value, 4)
stop, err := s.Subscribe(ctx, "toggle_1hz", ch)
if err != nil {
t.Fatalf("Subscribe: %v", err)
}
defer stop()
select {
case v := <-ch:
if _, ok := v.Data.(bool); !ok {
t.Errorf("toggle_1hz Data type = %T, want bool", v.Data)
}
case <-ctx.Done():
t.Fatal("timeout")
}
}