1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-05-11 19:29:20 +08:00

Merge branch 'dev' into nats

This commit is contained in:
Michael Hope 2017-04-21 20:30:46 +02:00
commit fa0b82d84c
25 changed files with 645 additions and 329 deletions

View File

@ -56,7 +56,7 @@ func TestAnalogSensorDriverStart(t *testing.T) {
select {
case <-sem:
case <-time.After(10 * time.Second):
case <-time.After(1 * time.Second):
t.Errorf("AnalogSensor Event \"Data\" was not published")
}
@ -74,7 +74,7 @@ func TestAnalogSensorDriverStart(t *testing.T) {
select {
case <-sem:
case <-time.After(10 * time.Second):
case <-time.After(1 * time.Second):
t.Errorf("AnalogSensor Event \"Error\" was not published")
}
@ -93,7 +93,7 @@ func TestAnalogSensorDriverStart(t *testing.T) {
select {
case <-sem:
t.Errorf("AnalogSensor Event should not published")
case <-time.After(100 * time.Millisecond):
case <-time.After(1 * time.Second):
}
}
@ -107,7 +107,7 @@ func TestAnalogSensorDriverHalt(t *testing.T) {
gobottest.Assert(t, d.Halt(), nil)
select {
case <-done:
case <-time.After(time.Millisecond):
case <-time.After(100 * time.Millisecond):
t.Errorf("AnalogSensor was not halted")
}
}

View File

@ -38,7 +38,7 @@ func TestGroveTempSensorPublishesTemperatureInCelsius(t *testing.T) {
select {
case <-sem:
case <-time.After(10 * time.Second):
case <-time.After(1 * time.Second):
t.Errorf("Grove Temperature Sensor Event \"Data\" was not published")
}
@ -66,7 +66,7 @@ func TestGroveTempSensorPublishesError(t *testing.T) {
select {
case <-sem:
case <-time.After(time.Second):
case <-time.After(1 * time.Second):
t.Errorf("Grove Temperature Sensor Event \"Error\" was not published")
}
}
@ -81,7 +81,7 @@ func TestGroveTempSensorHalt(t *testing.T) {
gobottest.Assert(t, d.Halt(), nil)
select {
case <-done:
case <-time.After(time.Millisecond):
case <-time.After(100 * time.Millisecond):
t.Errorf("Grove Temperature Sensor was not halted")
}
}

View File

@ -12,7 +12,7 @@ import (
var _ gobot.Driver = (*ButtonDriver)(nil)
const BUTTON_TEST_DELAY = 150
const buttonTestDelay = 250
func initTestButtonDriver() *ButtonDriver {
return NewButtonDriver(newGpioTestAdaptor(), "1")
@ -52,7 +52,7 @@ func TestButtonDriverStart(t *testing.T) {
select {
case <-sem:
case <-time.After(BUTTON_TEST_DELAY * time.Millisecond):
case <-time.After(buttonTestDelay * time.Millisecond):
t.Errorf("Button Event \"Push\" was not published")
}
@ -68,40 +68,40 @@ func TestButtonDriverStart(t *testing.T) {
select {
case <-sem:
case <-time.After(BUTTON_TEST_DELAY * time.Millisecond):
case <-time.After(buttonTestDelay * time.Millisecond):
t.Errorf("Button Event \"Release\" was not published")
}
d.Once(Error, func(data interface{}) {
sem <- true
})
a.TestAdaptorDigitalRead(func() (val int, err error) {
err = errors.New("digital read error")
return
})
d.Once(Error, func(data interface{}) {
sem <- true
})
select {
case <-sem:
case <-time.After(BUTTON_TEST_DELAY * time.Millisecond):
case <-time.After(buttonTestDelay * time.Millisecond):
t.Errorf("Button Event \"Error\" was not published")
}
d.Once(ButtonPush, func(data interface{}) {
sem <- true
})
a.TestAdaptorDigitalRead(func() (val int, err error) {
val = 1
return
})
d.Once(ButtonPush, func(data interface{}) {
sem <- true
})
d.halt <- true
select {
case <-sem:
t.Errorf("Button Event \"Press\" should not published")
case <-time.After(BUTTON_TEST_DELAY * time.Millisecond):
case <-time.After(buttonTestDelay * time.Millisecond):
}
}

View File

@ -71,9 +71,6 @@ func TestLedDriverToggle(t *testing.T) {
}
func TestLedDriverBrightness(t *testing.T) {
// d := initTestLedDriver(&gpioTestDigitalWriter{})
// gobottest.Assert(t, d.Brightness(150), ErrPwmWriteUnsupported)
a := newGpioTestAdaptor()
d := NewLedDriver(a, "1")
a.testAdaptorPwmWrite = func() (err error) {

View File

@ -11,7 +11,7 @@ import (
var _ gobot.Driver = (*MakeyButtonDriver)(nil)
const MAKEY_TEST_DELAY = 30
const makeyTestDelay = 250
func initTestMakeyButtonDriver() *MakeyButtonDriver {
return NewMakeyButtonDriver(newGpioTestAdaptor(), "1")
@ -27,7 +27,7 @@ func TestMakeyButtonDriverHalt(t *testing.T) {
gobottest.Assert(t, d.Halt(), nil)
select {
case <-done:
case <-time.After(time.Millisecond):
case <-time.After(makeyTestDelay * time.Millisecond):
t.Errorf("MakeyButton was not halted")
}
}
@ -39,7 +39,7 @@ func TestMakeyButtonDriver(t *testing.T) {
gobottest.Assert(t, d.interval, 10*time.Millisecond)
d = NewMakeyButtonDriver(newGpioTestAdaptor(), "1", 30*time.Second)
gobottest.Assert(t, d.interval, MAKEY_TEST_DELAY*time.Second)
gobottest.Assert(t, d.interval, 30*time.Second)
}
func TestMakeyButtonDriverStart(t *testing.T) {
@ -49,51 +49,51 @@ func TestMakeyButtonDriverStart(t *testing.T) {
gobottest.Assert(t, d.Start(), nil)
a.TestAdaptorDigitalRead(func() (val int, err error) {
val = 0
return
})
d.Once(ButtonPush, func(data interface{}) {
gobottest.Assert(t, d.Active, true)
sem <- true
})
select {
case <-sem:
case <-time.After(MAKEY_TEST_DELAY * time.Millisecond):
t.Errorf("MakeyButton Event \"Push\" was not published")
}
a.TestAdaptorDigitalRead(func() (val int, err error) {
val = 1
val = 0
return
})
select {
case <-sem:
case <-time.After(makeyTestDelay * time.Millisecond):
t.Errorf("MakeyButton Event \"Push\" was not published")
}
d.Once(ButtonRelease, func(data interface{}) {
gobottest.Assert(t, d.Active, false)
sem <- true
})
select {
case <-sem:
case <-time.After(MAKEY_TEST_DELAY * time.Millisecond):
t.Errorf("MakeyButton Event \"Release\" was not published")
}
a.TestAdaptorDigitalRead(func() (val int, err error) {
err = errors.New("digital read error")
val = 1
return
})
select {
case <-sem:
case <-time.After(makeyTestDelay * time.Millisecond):
t.Errorf("MakeyButton Event \"Release\" was not published")
}
d.Once(Error, func(data interface{}) {
gobottest.Assert(t, data.(error).Error(), "digital read error")
sem <- true
})
a.TestAdaptorDigitalRead(func() (val int, err error) {
err = errors.New("digital read error")
return
})
select {
case <-sem:
case <-time.After(MAKEY_TEST_DELAY * time.Millisecond):
case <-time.After(makeyTestDelay * time.Millisecond):
t.Errorf("MakeyButton Event \"Error\" was not published")
}
@ -112,6 +112,6 @@ func TestMakeyButtonDriverStart(t *testing.T) {
select {
case <-sem:
t.Errorf("MakeyButton Event should not have been published")
case <-time.After(MAKEY_TEST_DELAY * time.Millisecond):
case <-time.After(makeyTestDelay * time.Millisecond):
}
}

View File

@ -12,7 +12,7 @@ import (
var _ gobot.Driver = (*PIRMotionDriver)(nil)
const MOTION_TEST_DELAY = 150
const motionTestDelay = 150
func initTestPIRMotionDriver() *PIRMotionDriver {
return NewPIRMotionDriver(newGpioTestAdaptor(), "1")
@ -53,7 +53,7 @@ func TestPIRMotionDriverStart(t *testing.T) {
select {
case <-sem:
case <-time.After(MOTION_TEST_DELAY * time.Millisecond):
case <-time.After(motionTestDelay * time.Millisecond):
t.Errorf("PIRMotionDriver Event \"MotionDetected\" was not published")
}
@ -69,22 +69,22 @@ func TestPIRMotionDriverStart(t *testing.T) {
select {
case <-sem:
case <-time.After(MOTION_TEST_DELAY * time.Millisecond):
case <-time.After(motionTestDelay * time.Millisecond):
t.Errorf("PIRMotionDriver Event \"MotionStopped\" was not published")
}
d.Once(Error, func(data interface{}) {
sem <- true
})
a.TestAdaptorDigitalRead(func() (val int, err error) {
err = errors.New("digital read error")
return
})
d.Once(Error, func(data interface{}) {
sem <- true
})
select {
case <-sem:
case <-time.After(MOTION_TEST_DELAY * time.Millisecond):
case <-time.After(motionTestDelay * time.Millisecond):
t.Errorf("PIRMotionDriver Event \"Error\" was not published")
}
}

View File

@ -19,10 +19,10 @@ func main() {
led := gpio.NewLedDriver(firmataAdaptor, "13")
work := func() {
mqttAdaptor.On("lights/on", func(data []byte) {
mqttAdaptor.On("lights/on", func(msg mqtt.Message) {
led.On()
})
mqttAdaptor.On("lights/off", func(data []byte) {
mqttAdaptor.On("lights/off", func(msg mqtt.Message) {
led.Off()
})
data := []byte("")

View File

@ -16,10 +16,10 @@ func main() {
mqttAdaptor := mqtt.NewAdaptor("tcp://test.mosquitto.org:1883", "pinger")
work := func() {
mqttAdaptor.On("hello", func(data []byte) {
mqttAdaptor.On("hello", func(msg mqtt.Message) {
fmt.Println("hello")
})
mqttAdaptor.On("hola", func(data []byte) {
mqttAdaptor.On("hola", func(msg mqtt.Message) {
fmt.Println("hola")
})
data := []byte("o")

View File

@ -31,8 +31,8 @@ func main() {
work := func() {
ollie.SetRGB(255, 0, 255)
mqttAdaptor.On("sensores/dial", func(data []byte) {
val, _ := strconv.Atoi(string(data))
mqttAdaptor.On("sensores/dial", func(msg mqtt.Message) {
val, _ := strconv.Atoi(string(msg.Payload()))
if val > 2000 {
ollie.SetRGB(0, 255, 0)
@ -45,28 +45,28 @@ func main() {
ollie.SetRGB(255, 0, 0)
})
mqttAdaptor.On("rover/frente", func(data []byte) {
mqttAdaptor.On("rover/frente", func(msg mqtt.Message) {
ollie.Roll(40, FRENTE)
gobot.After(1*time.Second, func() {
ollie.Stop()
})
})
mqttAdaptor.On("rover/derecha", func(data []byte) {
mqttAdaptor.On("rover/derecha", func(msg mqtt.Message) {
ollie.Roll(40, DERECHA)
gobot.After(1*time.Second, func() {
ollie.Stop()
})
})
mqttAdaptor.On("rover/atras", func(data []byte) {
mqttAdaptor.On("rover/atras", func(msg mqtt.Message) {
ollie.Roll(40, ATRAS)
gobot.After(1*time.Second, func() {
ollie.Stop()
})
})
mqttAdaptor.On("rover/izquierda", func(data []byte) {
mqttAdaptor.On("rover/izquierda", func(msg mqtt.Message) {
ollie.Roll(40, IZQUIERDA)
gobot.After(1*time.Second, func() {
ollie.Stop()

View File

@ -12,36 +12,11 @@ import (
"gobot.io/x/gobot/sysfs"
)
func writeFile(path string, data []byte) (i int, err error) {
file, err := sysfs.OpenFile(path, os.O_WRONLY, 0644)
defer file.Close()
if err != nil {
return
}
return file.Write(data)
}
func readFile(path string) ([]byte, error) {
file, err := sysfs.OpenFile(path, os.O_RDONLY, 0644)
defer file.Close()
if err != nil {
return make([]byte, 0), err
}
buf := make([]byte, 200)
var i int
i, err = file.Read(buf)
if i == 0 {
return buf, err
}
return buf[:i], err
}
type mux struct {
pin int
value int
}
type sysfsPin struct {
pin int
resistor int
@ -57,26 +32,20 @@ type Adaptor struct {
pinmap map[string]sysfsPin
tristate sysfs.DigitalPin
digitalPins map[int]sysfs.DigitalPin
pwmPins map[int]*pwmPin
pwmPins map[int]*sysfs.PWMPin
i2cBus sysfs.I2cDevice
connect func(e *Adaptor) (err error)
}
// changePinMode writes pin mode to current_pinmux file
func changePinMode(pin, mode string) (err error) {
_, err = writeFile(
"/sys/kernel/debug/gpio_debug/gpio"+pin+"/current_pinmux",
[]byte("mode"+mode),
)
return
writeFile func(path string, data []byte) (i int, err error)
readFile func(path string) ([]byte, error)
}
// NewAdaptor returns a new Edison Adaptor
func NewAdaptor() *Adaptor {
return &Adaptor{
name: gobot.DefaultName("Edison"),
board: "arduino",
pinmap: arduinoPinMap,
name: gobot.DefaultName("Edison"),
pinmap: arduinoPinMap,
writeFile: writeFile,
readFile: readFile,
}
}
@ -95,7 +64,11 @@ func (e *Adaptor) SetBoard(n string) { e.board = n }
// Connect initializes the Edison for use with the Arduino beakout board
func (e *Adaptor) Connect() (err error) {
e.digitalPins = make(map[int]sysfs.DigitalPin)
e.pwmPins = make(map[int]*pwmPin)
e.pwmPins = make(map[int]*sysfs.PWMPin)
if e.board == "" && e.checkForArduino() {
e.board = "arduino"
}
switch e.Board() {
case "sparkfun":
@ -130,10 +103,10 @@ func (e *Adaptor) Finalize() (err error) {
}
for _, pin := range e.pwmPins {
if pin != nil {
if errs := pin.enable("0"); errs != nil {
if errs := pin.Enable("0"); errs != nil {
err = multierror.Append(err, errs)
}
if errs := pin.unexport(); errs != nil {
if errs := pin.Unexport(); errs != nil {
err = multierror.Append(err, errs)
}
}
@ -172,18 +145,18 @@ func (e *Adaptor) PwmWrite(pin string, val byte) (err error) {
if err = e.DigitalWrite(pin, 1); err != nil {
return
}
if err = changePinMode(strconv.Itoa(int(sysPin.pin)), "1"); err != nil {
if err = changePinMode(e, strconv.Itoa(int(sysPin.pin)), "1"); err != nil {
return
}
e.pwmPins[sysPin.pwmPin] = newPwmPin(sysPin.pwmPin)
if err = e.pwmPins[sysPin.pwmPin].export(); err != nil {
e.pwmPins[sysPin.pwmPin] = sysfs.NewPWMPin(sysPin.pwmPin)
if err = e.pwmPins[sysPin.pwmPin].Export(); err != nil {
return
}
if err = e.pwmPins[sysPin.pwmPin].enable("1"); err != nil {
if err = e.pwmPins[sysPin.pwmPin].Enable("1"); err != nil {
return
}
}
p, err := e.pwmPins[sysPin.pwmPin].period()
p, err := e.pwmPins[sysPin.pwmPin].Period()
if err != nil {
return err
}
@ -192,14 +165,14 @@ func (e *Adaptor) PwmWrite(pin string, val byte) (err error) {
return err
}
duty := gobot.FromScale(float64(val), 0, 255.0)
return e.pwmPins[sysPin.pwmPin].writeDuty(strconv.Itoa(int(float64(period) * duty)))
return e.pwmPins[sysPin.pwmPin].WriteDuty(strconv.Itoa(int(float64(period) * duty)))
}
return errors.New("Not a PWM pin")
}
// AnalogRead returns value from analog reading of specified pin
func (e *Adaptor) AnalogRead(pin string) (val int, err error) {
buf, err := readFile(
buf, err := e.readFile(
"/sys/bus/iio/devices/iio:device1/in_voltage" + pin + "_raw",
)
if err != nil {
@ -236,12 +209,29 @@ func (e *Adaptor) GetDefaultBus() int {
return 1
}
// arduinoSetup does needed setup for the Arduino compatible breakout board
func (e *Adaptor) arduinoSetup() (err error) {
// TODO: also check to see if device labels for
// /sys/class/gpio/gpiochip{200,216,232,248}/label == "pcal9555a"
func (e *Adaptor) checkForArduino() bool {
if err := e.exportTristatePin(); err != nil {
return false
}
return true
}
func (e *Adaptor) exportTristatePin() (err error) {
e.tristate = sysfs.NewDigitalPin(214)
if err = e.tristate.Export(); err != nil {
return err
}
return
}
// arduinoSetup does needed setup for the Arduino compatible breakout board
func (e *Adaptor) arduinoSetup() (err error) {
if err = e.exportTristatePin(); err != nil {
return err
}
if err = e.tristate.Direction(sysfs.OUT); err != nil {
return err
}
@ -262,13 +252,13 @@ func (e *Adaptor) arduinoSetup() (err error) {
}
for _, i := range []string{"111", "115", "114", "109"} {
if err = changePinMode(i, "1"); err != nil {
if err = changePinMode(e, i, "1"); err != nil {
return err
}
}
for _, i := range []string{"131", "129", "40"} {
if err = changePinMode(i, "0"); err != nil {
if err = changePinMode(e, i, "0"); err != nil {
return err
}
}
@ -302,7 +292,7 @@ func (e *Adaptor) arduinoI2CSetup() (err error) {
}
for _, i := range []string{"28", "27"} {
if err = changePinMode(i, "1"); err != nil {
if err = changePinMode(e, i, "1"); err != nil {
return
}
}
@ -427,3 +417,38 @@ func (e *Adaptor) newDigitalPin(i int, level int) (err error) {
err = io.Unexport()
return
}
func writeFile(path string, data []byte) (i int, err error) {
file, err := sysfs.OpenFile(path, os.O_WRONLY, 0644)
defer file.Close()
if err != nil {
return
}
return file.Write(data)
}
func readFile(path string) ([]byte, error) {
file, err := sysfs.OpenFile(path, os.O_RDONLY, 0644)
defer file.Close()
if err != nil {
return make([]byte, 0), err
}
buf := make([]byte, 200)
var i int
i, err = file.Read(buf)
if i == 0 {
return buf, err
}
return buf[:i], err
}
// changePinMode writes pin mode to current_pinmux file
func changePinMode(a *Adaptor, pin, mode string) (err error) {
_, err = a.writeFile(
"/sys/kernel/debug/gpio_debug/gpio"+pin+"/current_pinmux",
[]byte("mode"+mode),
)
return
}

View File

@ -93,7 +93,7 @@ func initTestAdaptor() (*Adaptor, *sysfs.MockFilesystem) {
"/dev/i2c-6",
})
sysfs.SetFilesystem(fs)
fs.Files["/sys/class/pwm/pwmchip0/pwm1/period"].Contents = "5000\n"
fs.Files["/sys/class/pwm/pwmchip0/pwm1/period"].Contents = "5000"
a.Connect()
return a, fs
}
@ -109,17 +109,29 @@ func TestAdaptorConnect(t *testing.T) {
a, _ := initTestAdaptor()
gobottest.Assert(t, a.Connect(), nil)
gobottest.Assert(t, a.GetDefaultBus(), 6)
gobottest.Assert(t, a.Board(), "arduino")
a = NewAdaptor()
sysfs.SetFilesystem(sysfs.NewMockFilesystem([]string{}))
gobottest.Refute(t, a.Connect(), nil)
}
func TestAdaptorConnectArduinoError(t *testing.T) {
a, _ := initTestAdaptor()
a.writeFile = func(string, []byte) (int, error) {
return 0, errors.New("write error")
}
err := a.Connect()
gobottest.Assert(t, strings.Contains(err.Error(), "write error"), true)
}
func TestAdaptorConnectSparkfun(t *testing.T) {
a, _ := initTestAdaptor()
a.SetBoard("sparkfun")
gobottest.Assert(t, a.Connect(), nil)
gobottest.Assert(t, a.GetDefaultBus(), 1)
gobottest.Assert(t, a.Board(), "sparkfun")
}
func TestAdaptorConnectMiniboard(t *testing.T) {
@ -127,6 +139,7 @@ func TestAdaptorConnectMiniboard(t *testing.T) {
a.SetBoard("miniboard")
gobottest.Assert(t, a.Connect(), nil)
gobottest.Assert(t, a.GetDefaultBus(), 1)
gobottest.Assert(t, a.Board(), "miniboard")
}
func TestAdaptorConnectUnknown(t *testing.T) {
@ -176,6 +189,12 @@ func TestAdaptorI2c(t *testing.T) {
gobottest.Assert(t, a.Finalize(), nil)
}
func TestAdaptorI2cInvalidBus(t *testing.T) {
a, _ := initTestAdaptor()
_, err := a.GetConnection(0xff, 3)
gobottest.Assert(t, err, errors.New("Unsupported I2C bus"))
}
func TestAdaptorPwm(t *testing.T) {
a, fs := initTestAdaptor()
@ -187,6 +206,17 @@ func TestAdaptorPwm(t *testing.T) {
gobottest.Assert(t, err, errors.New("Not a PWM pin"))
}
func TestAdaptorPwmError(t *testing.T) {
a, _ := initTestAdaptor()
a.writeFile = func(string, []byte) (int, error) {
return 0, errors.New("write error")
}
err := a.PwmWrite("5", 100)
gobottest.Assert(t, err, errors.New("write error"))
}
func TestAdaptorAnalog(t *testing.T) {
a, fs := initTestAdaptor()
@ -194,3 +224,13 @@ func TestAdaptorAnalog(t *testing.T) {
i, _ := a.AnalogRead("0")
gobottest.Assert(t, i, 250)
}
func TestAdaptorAnalogError(t *testing.T) {
a, _ := initTestAdaptor()
a.readFile = func(string) ([]byte, error) {
return nil, errors.New("read error")
}
_, err := a.AnalogRead("0")
gobottest.Assert(t, err, errors.New("read error"))
}

View File

@ -1,75 +0,0 @@
package edison
import "strconv"
// pwmPath returns pwm base path
func pwmPath() string {
return "/sys/class/pwm/pwmchip0"
}
// pwmExportPath returns export path
func pwmExportPath() string {
return pwmPath() + "/export"
}
// pwmUnExportPath returns unexport path
func pwmUnExportPath() string {
return pwmPath() + "/unexport"
}
// pwmDutyCyclePath returns duty_cycle path for specified pin
func pwmDutyCyclePath(pin string) string {
return pwmPath() + "/pwm" + pin + "/duty_cycle"
}
// pwmPeriodPath returns period path for specified pin
func pwmPeriodPath(pin string) string {
return pwmPath() + "/pwm" + pin + "/period"
}
// pwmEnablePath returns enable path for specified pin
func pwmEnablePath(pin string) string {
return pwmPath() + "/pwm" + pin + "/enable"
}
type pwmPin struct {
pin string
}
// newPwmPin returns an exported and enabled pwmPin
func newPwmPin(pin int) *pwmPin {
return &pwmPin{pin: strconv.Itoa(pin)}
}
// enable writes value to pwm enable path
func (p *pwmPin) enable(val string) (err error) {
_, err = writeFile(pwmEnablePath(p.pin), []byte(val))
return
}
// period reads from pwm period path and returns value
func (p *pwmPin) period() (period string, err error) {
buf, err := readFile(pwmPeriodPath(p.pin))
if err != nil {
return
}
return string(buf[0 : len(buf)-1]), nil
}
// writeDuty writes value to pwm duty cycle path
func (p *pwmPin) writeDuty(duty string) (err error) {
_, err = writeFile(pwmDutyCyclePath(p.pin), []byte(duty))
return
}
// export writes pin to pwm export path
func (p *pwmPin) export() (err error) {
_, err = writeFile(pwmExportPath(), []byte(p.pin))
return
}
// export writes pin to pwm unexport path
func (p *pwmPin) unexport() (err error) {
_, err = writeFile(pwmUnExportPath(), []byte(p.pin))
return
}

View File

@ -3,7 +3,6 @@ package joule
import (
"errors"
"fmt"
"os"
"strconv"
multierror "github.com/hashicorp/go-multierror"
@ -12,32 +11,6 @@ import (
"gobot.io/x/gobot/sysfs"
)
func writeFile(path string, data []byte) (i int, err error) {
file, err := sysfs.OpenFile(path, os.O_WRONLY, 0644)
defer file.Close()
if err != nil {
return
}
return file.Write(data)
}
func readFile(path string) ([]byte, error) {
file, err := sysfs.OpenFile(path, os.O_RDONLY, 0644)
defer file.Close()
if err != nil {
return make([]byte, 0), err
}
buf := make([]byte, 200)
var i int
i, err = file.Read(buf)
if i == 0 {
return buf, err
}
return buf[:i], err
}
type sysfsPin struct {
pin int
pwmPin int
@ -47,7 +20,7 @@ type sysfsPin struct {
type Adaptor struct {
name string
digitalPins map[int]sysfs.DigitalPin
pwmPins map[int]*pwmPin
pwmPins map[int]*sysfs.PWMPin
i2cBuses [3]sysfs.I2cDevice
connect func(e *Adaptor) (err error)
}
@ -71,7 +44,7 @@ func (e *Adaptor) SetName(n string) { e.name = n }
// Connect initializes the Joule for use with the Arduino beakout board
func (e *Adaptor) Connect() (err error) {
e.digitalPins = make(map[int]sysfs.DigitalPin)
e.pwmPins = make(map[int]*pwmPin)
e.pwmPins = make(map[int]*sysfs.PWMPin)
err = e.connect(e)
return
}
@ -87,10 +60,10 @@ func (e *Adaptor) Finalize() (err error) {
}
for _, pin := range e.pwmPins {
if pin != nil {
if errs := pin.enable("0"); errs != nil {
if errs := pin.Enable("0"); errs != nil {
err = multierror.Append(err, errs)
}
if errs := pin.unexport(); errs != nil {
if errs := pin.Unexport(); errs != nil {
err = multierror.Append(err, errs)
}
}
@ -153,15 +126,15 @@ func (e *Adaptor) PwmWrite(pin string, val byte) (err error) {
if err = e.DigitalWrite(pin, 1); err != nil {
return
}
e.pwmPins[sysPin.pwmPin] = newPwmPin(sysPin.pwmPin)
if err = e.pwmPins[sysPin.pwmPin].export(); err != nil {
e.pwmPins[sysPin.pwmPin] = sysfs.NewPWMPin(sysPin.pwmPin)
if err = e.pwmPins[sysPin.pwmPin].Export(); err != nil {
return
}
if err = e.pwmPins[sysPin.pwmPin].enable("1"); err != nil {
if err = e.pwmPins[sysPin.pwmPin].Enable("1"); err != nil {
return
}
}
p, err := e.pwmPins[sysPin.pwmPin].period()
p, err := e.pwmPins[sysPin.pwmPin].Period()
if err != nil {
return err
}
@ -170,7 +143,7 @@ func (e *Adaptor) PwmWrite(pin string, val byte) (err error) {
return err
}
duty := gobot.FromScale(float64(val), 0, 255.0)
return e.pwmPins[sysPin.pwmPin].writeDuty(strconv.Itoa(int(float64(period) * duty)))
return e.pwmPins[sysPin.pwmPin].WriteDuty(strconv.Itoa(int(float64(period) * duty)))
}
return errors.New("Not a PWM pin")
}

View File

@ -84,7 +84,7 @@ func initTestAdaptor() (*Adaptor, *sysfs.MockFilesystem) {
"/dev/i2c-0",
})
sysfs.SetFilesystem(fs)
fs.Files["/sys/class/pwm/pwmchip0/pwm0/period"].Contents = "5000\n"
fs.Files["/sys/class/pwm/pwmchip0/pwm0/period"].Contents = "5000"
a.Connect()
return a, fs
}

View File

@ -1,76 +0,0 @@
// Package joule pwm implementation
package joule
import "strconv"
// pwmPath returns pwm base path
func pwmPath() string {
return "/sys/class/pwm/pwmchip0"
}
// pwmExportPath returns export path
func pwmExportPath() string {
return pwmPath() + "/export"
}
// pwmUnExportPath returns unexport path
func pwmUnExportPath() string {
return pwmPath() + "/unexport"
}
// pwmDutyCyclePath returns duty_cycle path for specified pin
func pwmDutyCyclePath(pin string) string {
return pwmPath() + "/pwm" + pin + "/duty_cycle"
}
// pwmPeriodPath returns period path for specified pin
func pwmPeriodPath(pin string) string {
return pwmPath() + "/pwm" + pin + "/period"
}
// pwmEnablePath returns enable path for specified pin
func pwmEnablePath(pin string) string {
return pwmPath() + "/pwm" + pin + "/enable"
}
type pwmPin struct {
pin string
}
// newPwmPin returns an exported and enabled pwmPin
func newPwmPin(pin int) *pwmPin {
return &pwmPin{pin: strconv.Itoa(pin)}
}
// enable writes value to pwm enable path
func (p *pwmPin) enable(val string) (err error) {
_, err = writeFile(pwmEnablePath(p.pin), []byte(val))
return
}
// period reads from pwm period path and returns value
func (p *pwmPin) period() (period string, err error) {
buf, err := readFile(pwmPeriodPath(p.pin))
if err != nil {
return
}
return string(buf[0 : len(buf)-1]), nil
}
// writeDuty writes value to pwm duty cycle path
func (p *pwmPin) writeDuty(duty string) (err error) {
_, err = writeFile(pwmDutyCyclePath(p.pin), []byte(duty))
return
}
// export writes pin to pwm export path
func (p *pwmPin) export() (err error) {
_, err = writeFile(pwmExportPath(), []byte(p.pin))
return
}
// export writes pin to pwm unexport path
func (p *pwmPin) unexport() (err error) {
_, err = writeFile(pwmUnExportPath(), []byte(p.pin))
return
}

View File

@ -6,10 +6,16 @@ import (
common "gobot.io/x/gobot/platforms/mavlink/common"
)
type UDPConnection interface {
Close() error
ReadFromUDP([]byte) (int, *net.UDPAddr, error)
WriteTo([]byte, net.Addr) (int, error)
}
type UDPAdaptor struct {
name string
port string
sock *net.UDPConn
sock UDPConnection
}
var _ BaseAdaptor = (*UDPAdaptor)(nil)
@ -86,5 +92,10 @@ func (m *UDPAdaptor) ReadMAVLinkPacket() (*common.MAVLinkPacket, error) {
}
func (m *UDPAdaptor) Write(b []byte) (int, error) {
return m.sock.Write(b)
addr, err := net.ResolveUDPAddr("udp", m.Port())
if err != nil {
return 0, err
}
return m.sock.WriteTo(b, addr)
}

View File

@ -1,15 +1,51 @@
package mavlink
import (
"bytes"
"errors"
"net"
"strings"
"testing"
"gobot.io/x/gobot"
"gobot.io/x/gobot/gobottest"
mavlink "gobot.io/x/gobot/platforms/mavlink/common"
)
var _ gobot.Adaptor = (*UDPAdaptor)(nil)
type MockUDPConnection struct {
TestClose func() error
TestReadFromUDP func([]byte) (int, *net.UDPAddr, error)
TestWriteTo func([]byte, net.Addr) (int, error)
}
func (m *MockUDPConnection) Close() error {
return m.TestClose()
}
func (m *MockUDPConnection) ReadFromUDP(b []byte) (int, *net.UDPAddr, error) {
return m.TestReadFromUDP(b)
}
func (m *MockUDPConnection) WriteTo(b []byte, a net.Addr) (int, error) {
return m.TestWriteTo(b, a)
}
func NewMockUDPConnection() *MockUDPConnection {
return &MockUDPConnection{
TestClose: func() error {
return nil
},
TestReadFromUDP: func([]byte) (int, *net.UDPAddr, error) {
return 0, nil, nil
},
TestWriteTo: func([]byte, net.Addr) (int, error) {
return 0, nil
},
}
}
func initTestMavlinkUDPAdaptor() *UDPAdaptor {
m := NewUDPAdaptor(":14550")
return m
@ -27,12 +63,78 @@ func TestMavlinkUDPAdaptorName(t *testing.T) {
gobottest.Assert(t, a.Name(), "NewName")
}
func TestMavlinkUDPAdaptorConnect(t *testing.T) {
func TestMavlinkUDPAdaptorConnectAndFinalize(t *testing.T) {
a := initTestMavlinkUDPAdaptor()
gobottest.Assert(t, a.Connect(), nil)
}
func TestMavlinkUDPAdaptorFinalize(t *testing.T) {
a := initTestMavlinkUDPAdaptor()
gobottest.Assert(t, a.Finalize(), nil)
}
func TestMavlinkUDPAdaptorWrite(t *testing.T) {
a := initTestMavlinkUDPAdaptor()
a.Connect()
defer a.Finalize()
m := NewMockUDPConnection()
m.TestWriteTo = func([]byte, net.Addr) (int, error) {
return 3, nil
}
a.sock = m
i, err := a.Write([]byte{0x01, 0x02, 0x03})
gobottest.Assert(t, i, 3)
gobottest.Assert(t, err, nil)
}
func TestMavlinkReadMAVLinkReadDefaultPacket(t *testing.T) {
a := initTestMavlinkUDPAdaptor()
a.Connect()
defer a.Finalize()
m := NewMockUDPConnection()
m.TestReadFromUDP = func(b []byte) (int, *net.UDPAddr, error) {
buf := new(bytes.Buffer)
buf.Write([]byte{mavlink.MAVLINK_10_STX, 0x02, 0x03})
copy(b, buf.Bytes())
return buf.Len(), nil, nil
}
a.sock = m
p, _ := a.ReadMAVLinkPacket()
gobottest.Assert(t, p.Protocol, uint8(254))
}
func TestMavlinkReadMAVLinkPacketReadError(t *testing.T) {
a := initTestMavlinkUDPAdaptor()
a.Connect()
defer a.Finalize()
m := NewMockUDPConnection()
i := 0
m.TestReadFromUDP = func(b []byte) (int, *net.UDPAddr, error) {
switch i {
case 0:
i = 1
return 1, nil, nil
case 1:
i = 2
buf := new(bytes.Buffer)
buf.Write([]byte{0x01, 0x02, 0x03})
copy(b, buf.Bytes())
return buf.Len(), nil, nil
case 2:
i = 3
buf := new(bytes.Buffer)
buf.Write([]byte{mavlink.MAVLINK_10_STX, 255})
copy(b, buf.Bytes())
return buf.Len(), nil, nil
}
return 0, nil, errors.New("read error")
}
a.sock = m
_, err := a.ReadMAVLinkPacket()
gobottest.Assert(t, err, errors.New("read error"))
}

View File

@ -32,11 +32,11 @@ func main() {
mqttAdaptor := mqtt.NewAdaptor("tcp://0.0.0.0:1883", "pinger")
work := func() {
mqttAdaptor.On("hello", func(data []byte) {
fmt.Println("hello")
mqttAdaptor.On("hello", func(msg mqtt.Message) {
fmt.Println(msg)
})
mqttAdaptor.On("hola", func(data []byte) {
fmt.Println("hola")
mqttAdaptor.On("hola", func(msg mqtt.Message) {
fmt.Println(msg)
})
data := []byte("o")
gobot.Every(1*time.Second, func() {

View File

@ -11,6 +11,9 @@ import (
multierror "github.com/hashicorp/go-multierror"
)
// Message is a message received from the broker.
type Message paho.Message
// Adaptor is the Gobot Adaptor for MQTT
type Adaptor struct {
name string
@ -86,7 +89,7 @@ func (a *Adaptor) SetClientCert(val string) { a.clientCert = val }
// ClientKey returns the MQTT client SSL key file
func (a *Adaptor) ClientKey() string { return a.clientKey }
// SetClientCert sets the MQTT server SSL key file
// SetClientKey sets the MQTT client SSL key file
func (a *Adaptor) SetClientKey(val string) { a.clientKey = val }
// Connect returns true if connection to mqtt is established
@ -123,12 +126,12 @@ func (a *Adaptor) Publish(topic string, message []byte) bool {
}
// On subscribes to a topic, and then calls the message handler function when data is received
func (a *Adaptor) On(event string, f func(s []byte)) bool {
func (a *Adaptor) On(event string, f func(msg Message)) bool {
if a.client == nil {
return false
}
a.client.Subscribe(event, 0, func(client paho.Client, msg paho.Message) {
f(msg.Payload())
f(msg)
})
return true
}

View File

@ -14,7 +14,7 @@ import (
var _ gobot.Adaptor = (*Adaptor)(nil)
func initTestMqttAdaptor() *Adaptor {
return NewAdaptor("localhost:1883", "client")
return NewAdaptor("tcp://localhost:1883", "client")
}
func TestMqttAdaptorName(t *testing.T) {
@ -24,8 +24,65 @@ func TestMqttAdaptorName(t *testing.T) {
gobottest.Assert(t, a.Name(), "NewName")
}
func TestMqttAdaptorConnect(t *testing.T) {
func TestMqttAdaptorPort(t *testing.T) {
a := initTestMqttAdaptor()
gobottest.Assert(t, a.Port(), "tcp://localhost:1883")
}
func TestMqttAdaptorAutoReconnect(t *testing.T) {
a := initTestMqttAdaptor()
gobottest.Assert(t, a.AutoReconnect(), false)
a.SetAutoReconnect(true)
gobottest.Assert(t, a.AutoReconnect(), true)
}
func TestMqttAdaptorUseSSL(t *testing.T) {
a := initTestMqttAdaptor()
gobottest.Assert(t, a.UseSSL(), false)
a.SetUseSSL(true)
gobottest.Assert(t, a.UseSSL(), true)
}
func TestMqttAdaptorUseServerCert(t *testing.T) {
a := initTestMqttAdaptor()
gobottest.Assert(t, a.ServerCert(), "")
a.SetServerCert("/path/to/server.cert")
gobottest.Assert(t, a.ServerCert(), "/path/to/server.cert")
}
func TestMqttAdaptorUseClientCert(t *testing.T) {
a := initTestMqttAdaptor()
gobottest.Assert(t, a.ClientCert(), "")
a.SetClientCert("/path/to/client.cert")
gobottest.Assert(t, a.ClientCert(), "/path/to/client.cert")
}
func TestMqttAdaptorUseClientKey(t *testing.T) {
a := initTestMqttAdaptor()
gobottest.Assert(t, a.ClientKey(), "")
a.SetClientKey("/path/to/client.key")
gobottest.Assert(t, a.ClientKey(), "/path/to/client.key")
}
func TestMqttAdaptorConnectError(t *testing.T) {
a := initTestMqttAdaptor()
var expected error
expected = multierror.Append(expected, errors.New("Network Error : dial tcp 127.0.0.1:1883: getsockopt: connection refused"))
gobottest.Assert(t, a.Connect(), expected)
}
func TestMqttAdaptorConnectSSLError(t *testing.T) {
a := initTestMqttAdaptor()
a.SetUseSSL(true)
var expected error
expected = multierror.Append(expected, errors.New("Network Error : dial tcp 127.0.0.1:1883: getsockopt: connection refused"))
gobottest.Assert(t, a.Connect(), expected)
}
func TestMqttAdaptorConnectWithAuthError(t *testing.T) {
a := NewAdaptorWithAuth("localhost:1883", "client", "user", "pass")
var expected error
expected = multierror.Append(expected, errors.New("Network Error : Unknown protocol"))
@ -52,7 +109,7 @@ func TestMqttAdaptorPublishWhenConnected(t *testing.T) {
func TestMqttAdaptorCannotOnUnlessConnected(t *testing.T) {
a := initTestMqttAdaptor()
gobottest.Assert(t, a.On("hola", func(data []byte) {
gobottest.Assert(t, a.On("hola", func(msg Message) {
fmt.Println("hola")
}), false)
}
@ -60,7 +117,7 @@ func TestMqttAdaptorCannotOnUnlessConnected(t *testing.T) {
func TestMqttAdaptorOnWhenConnected(t *testing.T) {
a := initTestMqttAdaptor()
a.Connect()
gobottest.Assert(t, a.On("hola", func(data []byte) {
gobottest.Assert(t, a.On("hola", func(msg Message) {
fmt.Println("hola")
}), true)
}

View File

@ -74,10 +74,10 @@ func (m *Driver) Publish(data interface{}) bool {
// On subscribes to data updates for the current device topic,
// and then calls the message handler function when data is received
func (m *Driver) On(n string, f func(d interface{})) error {
func (m *Driver) On(n string, f func(msg interface{})) error {
// TODO: also be able to subscribe to Error updates
f1 := func(s []byte) {
f(s)
f1 := func(msg Message) {
f(msg)
}
m.adaptor().On(m.topic, f1)
return nil

View File

@ -26,3 +26,27 @@ func TestMqttDriverName(t *testing.T) {
d.SetName("NewName")
gobottest.Assert(t, d.Name(), "NewName")
}
func TestMqttDriverTopic(t *testing.T) {
d := NewDriver(initTestMqttAdaptor(), "/test/topic")
gobottest.Assert(t, d.Topic(), "/test/topic")
d.SetTopic("/test/newtopic")
gobottest.Assert(t, d.Topic(), "/test/newtopic")
}
func TestMqttDriverPublish(t *testing.T) {
a := initTestMqttAdaptor()
d := NewDriver(a, "/test/topic")
a.Connect()
d.Start()
defer d.Halt()
gobottest.Assert(t, d.Publish([]byte{0x01, 0x02, 0x03}), true)
}
func TestMqttDriverPublishError(t *testing.T) {
a := initTestMqttAdaptor()
d := NewDriver(a, "/test/topic")
d.Start()
defer d.Halt()
gobottest.Assert(t, d.Publish([]byte{0x01, 0x02, 0x03}), false)
}

125
sysfs/pwm_pin.go Normal file
View File

@ -0,0 +1,125 @@
package sysfs
import (
"os"
"strconv"
)
// PWMPin is the interface for sysfs PWM interactions
type PWMPinner interface {
// Export exports the pin for use by the operating system
Export() error
// Unexport unexports the pin and releases the pin from the operating system
Unexport() error
// Enable enables/disables the PWM pin
Enable(val string) (err error)
// Period returns the current PWM period for pin
Period() (period string, err error)
// WriteDuty writes the duty cycle to the pin
WriteDuty(duty string) (err error)
}
type PWMPin struct {
pin string
Chip string
write func(path string, data []byte) (i int, err error)
read func(path string) ([]byte, error)
}
// NewPwmPin returns a new pwmPin
func NewPWMPin(pin int) *PWMPin {
return &PWMPin{
pin: strconv.Itoa(pin),
Chip: "0",
read: readPwmFile,
write: writePwmFile}
}
// Export writes pin to pwm export path
func (p *PWMPin) Export() (err error) {
_, err = p.write(p.pwmExportPath(), []byte(p.pin))
return
}
// Unexport writes pin to pwm unexport path
func (p *PWMPin) Unexport() (err error) {
_, err = p.write(p.pwmUnexportPath(), []byte(p.pin))
return
}
// Enable writes value to pwm enable path
func (p *PWMPin) Enable(val string) (err error) {
_, err = p.write(p.pwmEnablePath(), []byte(val))
return
}
// Period reads from pwm period path and returns value
func (p *PWMPin) Period() (period string, err error) {
buf, err := p.read(p.pwmPeriodPath())
if err != nil {
return
}
return string(buf), nil
}
// WriteDuty writes value to pwm duty cycle path
func (p *PWMPin) WriteDuty(duty string) (err error) {
_, err = p.write(p.pwmDutyCyclePath(), []byte(duty))
return
}
// pwmPath returns pwm base path
func (p *PWMPin) pwmPath() string {
return "/sys/class/pwm/pwmchip" + p.Chip
}
// pwmExportPath returns export path
func (p *PWMPin) pwmExportPath() string {
return p.pwmPath() + "/export"
}
// pwmUnexportPath returns unexport path
func (p *PWMPin) pwmUnexportPath() string {
return p.pwmPath() + "/unexport"
}
// pwmDutyCyclePath returns duty_cycle path for specified pin
func (p *PWMPin) pwmDutyCyclePath() string {
return p.pwmPath() + "/pwm" + p.pin + "/duty_cycle"
}
// pwmPeriodPath returns period path for specified pin
func (p *PWMPin) pwmPeriodPath() string {
return p.pwmPath() + "/pwm" + p.pin + "/period"
}
// pwmEnablePath returns enable path for specified pin
func (p *PWMPin) pwmEnablePath() string {
return p.pwmPath() + "/pwm" + p.pin + "/enable"
}
func writePwmFile(path string, data []byte) (i int, err error) {
file, err := OpenFile(path, os.O_WRONLY, 0644)
defer file.Close()
if err != nil {
return
}
return file.Write(data)
}
func readPwmFile(path string) ([]byte, error) {
file, err := OpenFile(path, os.O_RDONLY, 0644)
defer file.Close()
if err != nil {
return make([]byte, 0), err
}
buf := make([]byte, 200)
var i int
i, err = file.Read(buf)
if i == 0 {
return buf, err
}
return buf[:i], err
}

104
sysfs/pwm_pin_test.go Normal file
View File

@ -0,0 +1,104 @@
package sysfs
import (
"os"
"syscall"
"testing"
"gobot.io/x/gobot/gobottest"
)
func TestPwmPin(t *testing.T) {
fs := NewMockFilesystem([]string{
"/sys/class/pwm/pwmchip0/export",
"/sys/class/pwm/pwmchip0/unexport",
"/sys/class/pwm/pwmchip0/pwm10/enable",
"/sys/class/pwm/pwmchip0/pwm10/period",
"/sys/class/pwm/pwmchip0/pwm10/duty_cycle",
})
SetFilesystem(fs)
pin := NewPWMPin(10)
gobottest.Assert(t, pin.pin, "10")
err := pin.Unexport()
gobottest.Assert(t, err, nil)
gobottest.Assert(t, fs.Files["/sys/class/pwm/pwmchip0/unexport"].Contents, "10")
err = pin.Export()
gobottest.Assert(t, err, nil)
gobottest.Assert(t, fs.Files["/sys/class/pwm/pwmchip0/export"].Contents, "10")
gobottest.Refute(t, fs.Files["/sys/class/pwm/pwmchip0/pwm10/enable"].Contents, "1")
err = pin.Enable("1")
gobottest.Assert(t, err, nil)
gobottest.Assert(t, fs.Files["/sys/class/pwm/pwmchip0/pwm10/enable"].Contents, "1")
fs.Files["/sys/class/pwm/pwmchip0/pwm10/period"].Contents = "6"
data, _ := pin.Period()
gobottest.Assert(t, data, "6")
gobottest.Refute(t, fs.Files["/sys/class/pwm/pwmchip0/pwm10/duty_cycle"].Contents, "1")
err = pin.WriteDuty("100")
gobottest.Assert(t, err, nil)
gobottest.Assert(t, fs.Files["/sys/class/pwm/pwmchip0/pwm10/duty_cycle"].Contents, "100")
}
func TestPwmPinExportError(t *testing.T) {
fs := NewMockFilesystem([]string{
"/sys/class/pwm/pwmchip0/export",
"/sys/class/pwm/pwmchip0/unexport",
"/sys/class/pwm/pwmchip0/pwm10/enable",
"/sys/class/pwm/pwmchip0/pwm10/period",
"/sys/class/pwm/pwmchip0/pwm10/duty_cycle",
})
SetFilesystem(fs)
pin := NewPWMPin(10)
pin.write = func(string, []byte) (int, error) {
return 0, &os.PathError{Err: syscall.EBUSY}
}
gobottest.Refute(t, pin.Export(), nil)
}
func TestPwmPinUnxportError(t *testing.T) {
fs := NewMockFilesystem([]string{
"/sys/class/pwm/pwmchip0/export",
"/sys/class/pwm/pwmchip0/unexport",
"/sys/class/pwm/pwmchip0/pwm10/enable",
"/sys/class/pwm/pwmchip0/pwm10/period",
"/sys/class/pwm/pwmchip0/pwm10/duty_cycle",
})
SetFilesystem(fs)
pin := NewPWMPin(10)
pin.write = func(string, []byte) (int, error) {
return 0, &os.PathError{Err: syscall.EBUSY}
}
gobottest.Refute(t, pin.Unexport(), nil)
}
func TestPwmPinPeriodError(t *testing.T) {
fs := NewMockFilesystem([]string{
"/sys/class/pwm/pwmchip0/export",
"/sys/class/pwm/pwmchip0/unexport",
"/sys/class/pwm/pwmchip0/pwm10/enable",
"/sys/class/pwm/pwmchip0/pwm10/period",
"/sys/class/pwm/pwmchip0/pwm10/duty_cycle",
})
SetFilesystem(fs)
pin := NewPWMPin(10)
pin.read = func(string) ([]byte, error) {
return nil, &os.PathError{Err: syscall.EBUSY}
}
_, err := pin.Period()
gobottest.Refute(t, err, nil)
}

View File

@ -1,6 +1,7 @@
package gobot
import (
"strings"
"testing"
"time"
@ -79,3 +80,8 @@ func TestRand(t *testing.T) {
t.Errorf("%v should not equal %v", a, b)
}
}
func TestDefaultName(t *testing.T) {
name := DefaultName("tester")
gobottest.Assert(t, strings.Contains(name, "tester"), true)
}