package testsuite import ( "context" "math" "math/rand" "os" "strconv" "testing" "time" "forge.cadoles.com/cadoles/go-emlid/reach" "forge.cadoles.com/cadoles/go-emlid/reach/client/protocol" "github.com/davecgh/go-spew/spew" "github.com/pkg/errors" ) type OperationsFactoryFunc func(addr string) (protocol.Operations, error) type operationTestCase struct { Name string Run func(t *testing.T, ops protocol.Operations) } var testCases = []operationTestCase{ { Name: "Connect to ReachView", Run: func(t *testing.T, ops protocol.Operations) { ctx := context.Background() if err := ops.Connect(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) return } defer func() { if err := ops.Close(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) } }() alive, err := ops.Alive(ctx) if err != nil { t.Errorf("%+v", errors.WithStack(err)) return } if e, g := true, alive; e != g { t.Errorf("alive: expected '%v', got '%v'", e, g) } }, }, { Name: "Retrieve ReachView version", Run: func(t *testing.T, ops protocol.Operations) { ctx := context.Background() if err := ops.Connect(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) return } defer func() { if err := ops.Close(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) } }() version, stable, err := ops.Version(ctx) if err != nil { t.Errorf("%+v", errors.WithStack(err)) return } t.Logf("ReachView version: %s", version) t.Logf("ReachView stable channel: %v", stable) }, }, { Name: "Listen to ReachView 'broadcast' messages", Run: func(t *testing.T, ops protocol.Operations) { ctx := context.Background() if err := ops.Connect(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) return } defer func() { if err := ops.Close(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) } }() broadcastCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() messages, err := ops.On(broadcastCtx, "broadcast") if err != nil { t.Errorf("%+v", errors.WithStack(err)) return } count := 0 for m := range messages { t.Logf("new message: %s", spew.Sdump(m)) count++ } if e, g := 1, count; g < e { t.Errorf("expected total messages > %d, got %d", e, g) } }, }, { Name: "Retrieve module configuration", Run: func(t *testing.T, ops protocol.Operations) { ctx := context.Background() if err := ops.Connect(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) return } defer func() { if err := ops.Close(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) } }() config, err := ops.Configuration(ctx) if err != nil { t.Errorf("%+v", errors.WithStack(err)) return } t.Logf("Module configuration: %s", spew.Sdump(config)) }, }, { Name: "Set base", Run: func(t *testing.T, ops protocol.Operations) { ctx := context.Background() if err := ops.Connect(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) return } defer func() { if err := ops.Close(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) } }() latitude := -90 + rand.Float64()*180 longitude := -180 + rand.Float64()*360 height := rand.Float64() * 1000 antennaOffset := toFixed(rand.Float64()*2, 3) opts := []protocol.SetBaseOptionFunc{ protocol.WithBaseLatitude(latitude), protocol.WithBaseLongitude(longitude), protocol.WithBaseHeight(height), protocol.WithBaseAntennaOffset(antennaOffset), protocol.WithBaseMode("manual"), } t.Logf("setting base (latitude: %v, longitude: %v, height: %v, antennaOffset: %v", latitude, longitude, height, antennaOffset) if err := ops.SetBase(ctx, opts...); err != nil { t.Errorf("%+v", errors.WithStack(err)) return } baseInfo, err := ops.GetBaseInfo(ctx) if err != nil { t.Errorf("%+v", errors.WithStack(err)) return } t.Logf("base info: %v", spew.Sdump(baseInfo)) if e, g := latitude, baseInfo.Latitude; e != g { t.Errorf("baseInfo.Latitude: expected '%v', got '%v'", e, g) } if e, g := longitude, baseInfo.Longitude; e != g { t.Errorf("baseInfo.Longitude: expected '%v', got '%v'", e, g) } if e, g := height, baseInfo.Height; e != g { t.Errorf("baseInfo.Height: expected '%v', got '%v'", e, g) } if e, g := antennaOffset, baseInfo.AntennaOffset; e != g { t.Errorf("baseInfo.AntennaOffset: expected '%v', got '%v'", e, g) } }, }, { Name: "Reboot", Run: func(t *testing.T, ops protocol.Operations) { const doRebootEnvVar = "DO_REBOOT" rawDoReboot := os.Getenv(doRebootEnvVar) if rawDoReboot == "" { rawDoReboot = "false" } doReboot, err := strconv.ParseBool(rawDoReboot) if err != nil { t.Fatalf("could not parse %s environment variable: %+v", doRebootEnvVar, errors.WithStack(err)) return } if !doReboot { t.Skipf("Reboot test case disabled. To enable, set environment variable %s=true", doRebootEnvVar) return } ctx := context.Background() if err := ops.Connect(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) return } defer func() { if err := ops.Close(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) } }() if err := ops.Reboot(ctx); err != nil { t.Errorf("%+v", errors.WithStack(err)) return } }, }, } func TestOperations(t *testing.T, opsFactory OperationsFactoryFunc) { reach.AssertIntegrationTests(t) addr := os.Getenv("REACHRS_HOST") if addr == "" { addr = "192.168.42.1" t.Logf("Targeting '%s'. You can modify targeted host by specifying environment variable REACHRS_HOST", addr) } else { t.Logf("Targeting '%s'", addr) } for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { ops, err := opsFactory(addr) if err != nil { t.Fatalf("could not initialize protocol operations: %+v", errors.WithStack(err)) } tc.Run(t, ops) }) } } func toFixed(n float64, precision int) float64 { scale := math.Pow(10, float64(precision)) return math.Round(n*scale) / scale }