gobot.io/x/gobot/v2@v2.1.0/platforms/raspi/raspi_adaptor_test.go (about) 1 package raspi 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 "testing" 8 9 "runtime" 10 "strconv" 11 "sync" 12 13 "gobot.io/x/gobot/v2" 14 "gobot.io/x/gobot/v2/drivers/gpio" 15 "gobot.io/x/gobot/v2/drivers/i2c" 16 "gobot.io/x/gobot/v2/drivers/spi" 17 "gobot.io/x/gobot/v2/gobottest" 18 "gobot.io/x/gobot/v2/system" 19 ) 20 21 // make sure that this Adaptor fulfills all the required interfaces 22 var _ gobot.Adaptor = (*Adaptor)(nil) 23 var _ gobot.DigitalPinnerProvider = (*Adaptor)(nil) 24 var _ gobot.PWMPinnerProvider = (*Adaptor)(nil) 25 var _ gpio.DigitalReader = (*Adaptor)(nil) 26 var _ gpio.DigitalWriter = (*Adaptor)(nil) 27 var _ gpio.PwmWriter = (*Adaptor)(nil) 28 var _ gpio.ServoWriter = (*Adaptor)(nil) 29 var _ i2c.Connector = (*Adaptor)(nil) 30 var _ spi.Connector = (*Adaptor)(nil) 31 32 func initTestAdaptorWithMockedFilesystem(mockPaths []string) (*Adaptor, *system.MockFilesystem) { 33 a := NewAdaptor() 34 fs := a.sys.UseMockFilesystem(mockPaths) 35 a.Connect() 36 return a, fs 37 } 38 39 func TestName(t *testing.T) { 40 a := NewAdaptor() 41 42 gobottest.Assert(t, strings.HasPrefix(a.Name(), "RaspberryPi"), true) 43 a.SetName("NewName") 44 gobottest.Assert(t, a.Name(), "NewName") 45 } 46 47 func TestGetDefaultBus(t *testing.T) { 48 const contentPattern = "Hardware : BCM2708\n%sSerial : 000000003bc748ea\n" 49 var tests = map[string]struct { 50 revisionPart string 51 wantRev string 52 wantBus int 53 }{ 54 "no_revision": { 55 wantRev: "0", 56 wantBus: 0, 57 }, 58 "rev_1": { 59 revisionPart: "Revision : 0002\n", 60 wantRev: "1", 61 wantBus: 0, 62 }, 63 "rev_2": { 64 revisionPart: "Revision : 000D\n", 65 wantRev: "2", 66 wantBus: 1, 67 }, 68 "rev_3": { 69 revisionPart: "Revision : 0010\n", 70 wantRev: "3", 71 wantBus: 1, 72 }, 73 } 74 for name, tc := range tests { 75 t.Run(name, func(t *testing.T) { 76 // arrange 77 a := NewAdaptor() 78 fs := a.sys.UseMockFilesystem([]string{infoFile}) 79 fs.Files[infoFile].Contents = fmt.Sprintf(contentPattern, tc.revisionPart) 80 gobottest.Assert(t, a.revision, "") 81 //act, will read and refresh the revision 82 gotBus := a.DefaultI2cBus() 83 //assert 84 gobottest.Assert(t, a.revision, tc.wantRev) 85 gobottest.Assert(t, gotBus, tc.wantBus) 86 }) 87 } 88 } 89 90 func TestFinalize(t *testing.T) { 91 mockedPaths := []string{ 92 "/sys/class/gpio/export", 93 "/sys/class/gpio/unexport", 94 "/dev/pi-blaster", 95 "/dev/i2c-1", 96 "/dev/i2c-0", 97 "/dev/spidev0.0", 98 "/dev/spidev0.1", 99 } 100 a, _ := initTestAdaptorWithMockedFilesystem(mockedPaths) 101 102 a.DigitalWrite("3", 1) 103 a.PwmWrite("7", 255) 104 105 a.GetI2cConnection(0xff, 0) 106 gobottest.Assert(t, a.Finalize(), nil) 107 } 108 109 func TestDigitalPWM(t *testing.T) { 110 mockedPaths := []string{"/dev/pi-blaster"} 111 a, fs := initTestAdaptorWithMockedFilesystem(mockedPaths) 112 a.PiBlasterPeriod = 20000000 113 114 gobottest.Assert(t, a.PwmWrite("7", 4), nil) 115 116 pin, _ := a.PWMPin("7") 117 period, _ := pin.Period() 118 gobottest.Assert(t, period, uint32(20000000)) 119 120 gobottest.Assert(t, a.PwmWrite("7", 255), nil) 121 122 gobottest.Assert(t, strings.Split(fs.Files["/dev/pi-blaster"].Contents, "\n")[0], "4=1") 123 124 gobottest.Assert(t, a.ServoWrite("11", 90), nil) 125 126 gobottest.Assert(t, strings.Split(fs.Files["/dev/pi-blaster"].Contents, "\n")[0], "17=0.5") 127 128 gobottest.Assert(t, a.PwmWrite("notexist", 1), errors.New("Not a valid pin")) 129 gobottest.Assert(t, a.ServoWrite("notexist", 1), errors.New("Not a valid pin")) 130 131 pin, _ = a.PWMPin("12") 132 period, _ = pin.Period() 133 gobottest.Assert(t, period, uint32(20000000)) 134 135 gobottest.Assert(t, pin.SetDutyCycle(1.5*1000*1000), nil) 136 137 gobottest.Assert(t, strings.Split(fs.Files["/dev/pi-blaster"].Contents, "\n")[0], "18=0.075") 138 } 139 140 func TestDigitalIO(t *testing.T) { 141 mockedPaths := []string{ 142 "/sys/class/gpio/export", 143 "/sys/class/gpio/unexport", 144 "/sys/class/gpio/gpio4/value", 145 "/sys/class/gpio/gpio4/direction", 146 "/sys/class/gpio/gpio27/value", 147 "/sys/class/gpio/gpio27/direction", 148 } 149 a, fs := initTestAdaptorWithMockedFilesystem(mockedPaths) 150 151 err := a.DigitalWrite("7", 1) 152 gobottest.Assert(t, err, nil) 153 gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio4/value"].Contents, "1") 154 155 a.revision = "2" 156 err = a.DigitalWrite("13", 1) 157 gobottest.Assert(t, err, nil) 158 159 i, err := a.DigitalRead("13") 160 gobottest.Assert(t, err, nil) 161 gobottest.Assert(t, i, 1) 162 163 gobottest.Assert(t, a.DigitalWrite("notexist", 1), errors.New("Not a valid pin")) 164 gobottest.Assert(t, a.Finalize(), nil) 165 } 166 167 func TestDigitalPinConcurrency(t *testing.T) { 168 oldProcs := runtime.GOMAXPROCS(0) 169 runtime.GOMAXPROCS(8) 170 defer runtime.GOMAXPROCS(oldProcs) 171 172 for retry := 0; retry < 20; retry++ { 173 174 a := NewAdaptor() 175 var wg sync.WaitGroup 176 177 for i := 0; i < 20; i++ { 178 wg.Add(1) 179 pinAsString := strconv.Itoa(i) 180 go func(pin string) { 181 defer wg.Done() 182 a.DigitalPin(pin) 183 }(pinAsString) 184 } 185 186 wg.Wait() 187 } 188 } 189 190 func TestPWMPin(t *testing.T) { 191 a := NewAdaptor() 192 if err := a.Connect(); err != nil { 193 panic(err) 194 } 195 196 gobottest.Assert(t, len(a.pwmPins), 0) 197 198 a.revision = "3" 199 firstSysPin, err := a.PWMPin("35") 200 gobottest.Assert(t, err, nil) 201 gobottest.Assert(t, len(a.pwmPins), 1) 202 203 secondSysPin, err := a.PWMPin("35") 204 205 gobottest.Assert(t, err, nil) 206 gobottest.Assert(t, len(a.pwmPins), 1) 207 gobottest.Assert(t, firstSysPin, secondSysPin) 208 209 otherSysPin, err := a.PWMPin("36") 210 211 gobottest.Assert(t, err, nil) 212 gobottest.Assert(t, len(a.pwmPins), 2) 213 gobottest.Refute(t, firstSysPin, otherSysPin) 214 } 215 216 func TestPWMPinsReConnect(t *testing.T) { 217 // arrange 218 a := NewAdaptor() 219 a.revision = "3" 220 if err := a.Connect(); err != nil { 221 panic(err) 222 } 223 224 _, err := a.PWMPin("35") 225 gobottest.Assert(t, err, nil) 226 gobottest.Assert(t, len(a.pwmPins), 1) 227 gobottest.Assert(t, a.Finalize(), nil) 228 // act 229 err = a.Connect() 230 // assert 231 gobottest.Assert(t, err, nil) 232 gobottest.Assert(t, len(a.pwmPins), 0) 233 _, _ = a.PWMPin("35") 234 _, err = a.PWMPin("36") 235 gobottest.Assert(t, err, nil) 236 gobottest.Assert(t, len(a.pwmPins), 2) 237 } 238 239 func TestSpiDefaultValues(t *testing.T) { 240 a := NewAdaptor() 241 242 gobottest.Assert(t, a.SpiDefaultBusNumber(), 0) 243 gobottest.Assert(t, a.SpiDefaultChipNumber(), 0) 244 gobottest.Assert(t, a.SpiDefaultMode(), 0) 245 gobottest.Assert(t, a.SpiDefaultMaxSpeed(), int64(500000)) 246 } 247 248 func TestI2cDefaultBus(t *testing.T) { 249 mockedPaths := []string{"/dev/i2c-1"} 250 a, _ := initTestAdaptorWithMockedFilesystem(mockedPaths) 251 a.sys.UseMockSyscall() 252 253 a.revision = "0" 254 gobottest.Assert(t, a.DefaultI2cBus(), 0) 255 256 a.revision = "2" 257 gobottest.Assert(t, a.DefaultI2cBus(), 1) 258 } 259 260 func TestI2cFinalizeWithErrors(t *testing.T) { 261 // arrange 262 a := NewAdaptor() 263 a.sys.UseMockSyscall() 264 fs := a.sys.UseMockFilesystem([]string{"/dev/i2c-1"}) 265 gobottest.Assert(t, a.Connect(), nil) 266 con, err := a.GetI2cConnection(0xff, 1) 267 gobottest.Assert(t, err, nil) 268 _, err = con.Write([]byte{0xbf}) 269 gobottest.Assert(t, err, nil) 270 fs.WithCloseError = true 271 // act 272 err = a.Finalize() 273 // assert 274 gobottest.Assert(t, strings.Contains(err.Error(), "close error"), true) 275 } 276 277 func Test_validateSpiBusNumber(t *testing.T) { 278 var tests = map[string]struct { 279 busNr int 280 wantErr error 281 }{ 282 "number_negative_error": { 283 busNr: -1, 284 wantErr: fmt.Errorf("Bus number -1 out of range"), 285 }, 286 "number_0_ok": { 287 busNr: 0, 288 }, 289 "number_1_ok": { 290 busNr: 1, 291 }, 292 "number_2_error": { 293 busNr: 2, 294 wantErr: fmt.Errorf("Bus number 2 out of range"), 295 }, 296 } 297 for name, tc := range tests { 298 t.Run(name, func(t *testing.T) { 299 // arrange 300 a := NewAdaptor() 301 // act 302 err := a.validateSpiBusNumber(tc.busNr) 303 // assert 304 gobottest.Assert(t, err, tc.wantErr) 305 }) 306 } 307 } 308 309 func Test_validateI2cBusNumber(t *testing.T) { 310 var tests = map[string]struct { 311 busNr int 312 wantErr error 313 }{ 314 "number_negative_error": { 315 busNr: -1, 316 wantErr: fmt.Errorf("Bus number -1 out of range"), 317 }, 318 "number_0_ok": { 319 busNr: 0, 320 }, 321 "number_1_ok": { 322 busNr: 1, 323 }, 324 "number_2_not_ok": { 325 busNr: 2, 326 wantErr: fmt.Errorf("Bus number 2 out of range"), 327 }, 328 } 329 for name, tc := range tests { 330 t.Run(name, func(t *testing.T) { 331 // arrange 332 a := NewAdaptor() 333 // act 334 err := a.validateI2cBusNumber(tc.busNr) 335 // assert 336 gobottest.Assert(t, err, tc.wantErr) 337 }) 338 } 339 }