package tello import ( "bytes" "encoding/binary" "fmt" "io" "sync" "testing" "time" "gobot.io/x/gobot" "gobot.io/x/gobot/gobottest" ) var _ gobot.Driver = (*Driver)(nil) type WriteCloserDoNothing struct{} func (w *WriteCloserDoNothing) Write(p []byte) (n int, err error) { return 0, nil } func (w *WriteCloserDoNothing) Close() error { return nil } func TestTelloDriver(t *testing.T) { d := NewDriver("8888") gobottest.Assert(t, d.respPort, "8888") } func statusMessage(msgType uint16, msgAfter7 ...byte) []byte { msg := make([]byte, 7, len(msgAfter7)+7) msg[0] = messageStart binary.LittleEndian.PutUint16(msg[5:7], msgType) msg = append(msg, msgAfter7...) return msg } func TestHandleResponse(t *testing.T) { cc := []struct { name string msg io.Reader events []gobot.Event err error }{ { name: "[empty messsage]", msg: bytes.NewReader(nil), err: io.EOF, }, { name: "wifiMessage", msg: bytes.NewReader(statusMessage(wifiMessage)), events: []gobot.Event{{Name: WifiDataEvent}}, }, { name: "lightMessage", msg: bytes.NewReader(statusMessage(lightMessage)), events: []gobot.Event{{Name: LightStrengthEvent}}, }, { name: "logMessage", msg: bytes.NewReader(statusMessage(logMessage)), events: []gobot.Event{{Name: LogEvent}}, }, { name: "timeCommand", msg: bytes.NewReader(statusMessage(timeCommand)), events: []gobot.Event{{Name: TimeEvent}}, }, { name: "bounceCommand", msg: bytes.NewReader(statusMessage(bounceCommand)), events: []gobot.Event{{Name: BounceEvent}}, }, { name: "takeoffCommand", msg: bytes.NewReader(statusMessage(takeoffCommand)), events: []gobot.Event{{Name: TakeoffEvent}}, }, { name: "landCommand", msg: bytes.NewReader(statusMessage(landCommand)), events: []gobot.Event{{Name: LandingEvent}}, }, { name: "palmLandCommand", msg: bytes.NewReader(statusMessage(palmLandCommand)), events: []gobot.Event{{Name: PalmLandingEvent}}, }, { name: "flipCommand", msg: bytes.NewReader(statusMessage(flipCommand)), events: []gobot.Event{{Name: FlipEvent}}, }, { name: "flightMessage", msg: bytes.NewReader(statusMessage(flightMessage)), events: []gobot.Event{{Name: FlightDataEvent}}, }, { name: "exposureCommand", msg: bytes.NewReader(statusMessage(exposureCommand)), events: []gobot.Event{{Name: SetExposureEvent}}, }, { name: "videoEncoderRateCommand", msg: bytes.NewReader(statusMessage(videoEncoderRateCommand)), events: []gobot.Event{{Name: SetVideoEncoderRateEvent}}, }, { name: "ConnectedEvent", msg: bytes.NewReader([]byte{0x63, 0x6f, 0x6e}), events: []gobot.Event{{Name: ConnectedEvent}}, }, } for _, c := range cc { t.Run(c.name, func(t *testing.T) { d := NewDriver("8888") events := d.Subscribe() err := d.handleResponse(c.msg) if c.err != err { t.Errorf("expected '%v' error, got: %v", c.err, err) } for i, cev := range c.events { t.Run(fmt.Sprintf("event %d", i), func(t *testing.T) { t.Logf("expect: %#v", cev) select { case ev, ok := <-events: if !ok { t.Error("subscription channel is closed") } if ev.Name != cev.Name { t.Errorf("got: %s", ev.Name) } case <-time.After(time.Millisecond): t.Error("subscription channel seems empty") } }) } }) } } func TestHaltShouldTerminateAllTheRelatedGoroutines(t *testing.T) { d := NewDriver("8888") d.cmdConn = &WriteCloserDoNothing{} var wg sync.WaitGroup wg.Add(3) d.addDoneChReaderCount(1) go func() { defer d.addDoneChReaderCount(-1) <-d.doneCh wg.Done() fmt.Println("Done routine 1.") }() d.addDoneChReaderCount(1) go func() { defer d.addDoneChReaderCount(-1) <-d.doneCh wg.Done() fmt.Println("Done routine 2.") }() d.addDoneChReaderCount(1) go func() { defer d.addDoneChReaderCount(-1) <-d.doneCh wg.Done() fmt.Println("Done routine 3.") }() d.Halt() wg.Wait() gobottest.Assert(t, d.doneChReaderCount, int32(0)) } func TestHaltNotWaitForeverWhenCalledMultipleTimes(t *testing.T) { d := NewDriver("8888") d.cmdConn = &WriteCloserDoNothing{} d.Halt() d.Halt() d.Halt() }