gobot.io/x/gobot/v2@v2.1.0/platforms/tinkerboard/adaptor_test.go (about) 1 package tinkerboard 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "gobot.io/x/gobot/v2" 9 "gobot.io/x/gobot/v2/drivers/gpio" 10 "gobot.io/x/gobot/v2/drivers/i2c" 11 "gobot.io/x/gobot/v2/gobottest" 12 "gobot.io/x/gobot/v2/system" 13 ) 14 15 const ( 16 gpio17Path = "/sys/class/gpio/gpio17/" 17 gpio160Path = "/sys/class/gpio/gpio160/" 18 ) 19 20 const ( 21 pwmDir = "/sys/devices/platform/ff680020.pwm/pwm/pwmchip2/" 22 pwmPwmDir = pwmDir + "pwm0/" 23 pwmExportPath = pwmDir + "export" 24 pwmUnexportPath = pwmDir + "unexport" 25 pwmEnablePath = pwmPwmDir + "enable" 26 pwmPeriodPath = pwmPwmDir + "period" 27 pwmDutyCyclePath = pwmPwmDir + "duty_cycle" 28 pwmPolarityPath = pwmPwmDir + "polarity" 29 ) 30 31 var pwmMockPaths = []string{ 32 pwmExportPath, 33 pwmUnexportPath, 34 pwmEnablePath, 35 pwmPeriodPath, 36 pwmDutyCyclePath, 37 pwmPolarityPath, 38 } 39 40 var gpioMockPaths = []string{ 41 "/sys/class/gpio/export", 42 "/sys/class/gpio/unexport", 43 gpio17Path + "value", 44 gpio17Path + "direction", 45 gpio160Path + "value", 46 gpio160Path + "direction", 47 } 48 49 // make sure that this Adaptor fulfills all the required interfaces 50 var _ gobot.Adaptor = (*Adaptor)(nil) 51 var _ gobot.DigitalPinnerProvider = (*Adaptor)(nil) 52 var _ gobot.PWMPinnerProvider = (*Adaptor)(nil) 53 var _ gpio.DigitalReader = (*Adaptor)(nil) 54 var _ gpio.DigitalWriter = (*Adaptor)(nil) 55 var _ gpio.PwmWriter = (*Adaptor)(nil) 56 var _ gpio.ServoWriter = (*Adaptor)(nil) 57 var _ i2c.Connector = (*Adaptor)(nil) 58 59 func preparePwmFs(fs *system.MockFilesystem) { 60 fs.Files[pwmEnablePath].Contents = "0" 61 fs.Files[pwmPeriodPath].Contents = "0" 62 fs.Files[pwmDutyCyclePath].Contents = "0" 63 fs.Files[pwmPolarityPath].Contents = pwmInvertedIdentifier 64 } 65 66 func initTestAdaptorWithMockedFilesystem(mockPaths []string) (*Adaptor, *system.MockFilesystem) { 67 a := NewAdaptor() 68 fs := a.sys.UseMockFilesystem(mockPaths) 69 if err := a.Connect(); err != nil { 70 panic(err) 71 } 72 return a, fs 73 } 74 75 func TestName(t *testing.T) { 76 a := NewAdaptor() 77 gobottest.Assert(t, strings.HasPrefix(a.Name(), "Tinker Board"), true) 78 a.SetName("NewName") 79 gobottest.Assert(t, a.Name(), "NewName") 80 } 81 82 func TestDigitalIO(t *testing.T) { 83 // only basic tests needed, further tests are done in "digitalpinsadaptor.go" 84 a, fs := initTestAdaptorWithMockedFilesystem(gpioMockPaths) 85 86 a.DigitalWrite("7", 1) 87 gobottest.Assert(t, fs.Files[gpio17Path+"value"].Contents, "1") 88 89 fs.Files[gpio160Path+"value"].Contents = "1" 90 i, _ := a.DigitalRead("10") 91 gobottest.Assert(t, i, 1) 92 93 gobottest.Assert(t, a.DigitalWrite("99", 1), fmt.Errorf("'99' is not a valid id for a digital pin")) 94 gobottest.Assert(t, a.Finalize(), nil) 95 } 96 97 func TestInvalidPWMPin(t *testing.T) { 98 a, fs := initTestAdaptorWithMockedFilesystem(pwmMockPaths) 99 preparePwmFs(fs) 100 101 err := a.PwmWrite("666", 42) 102 gobottest.Assert(t, err.Error(), "'666' is not a valid id for a PWM pin") 103 104 err = a.ServoWrite("666", 120) 105 gobottest.Assert(t, err.Error(), "'666' is not a valid id for a PWM pin") 106 107 err = a.PwmWrite("3", 42) 108 gobottest.Assert(t, err.Error(), "'3' is not a valid id for a PWM pin") 109 110 err = a.ServoWrite("3", 120) 111 gobottest.Assert(t, err.Error(), "'3' is not a valid id for a PWM pin") 112 } 113 114 func TestPwmWrite(t *testing.T) { 115 a, fs := initTestAdaptorWithMockedFilesystem(pwmMockPaths) 116 preparePwmFs(fs) 117 118 err := a.PwmWrite("33", 100) 119 gobottest.Assert(t, err, nil) 120 121 gobottest.Assert(t, fs.Files[pwmExportPath].Contents, "0") 122 gobottest.Assert(t, fs.Files[pwmEnablePath].Contents, "1") 123 gobottest.Assert(t, fs.Files[pwmPeriodPath].Contents, fmt.Sprintf("%d", 10000000)) 124 gobottest.Assert(t, fs.Files[pwmDutyCyclePath].Contents, "3921568") 125 gobottest.Assert(t, fs.Files[pwmPolarityPath].Contents, "normal") 126 127 err = a.ServoWrite("33", 0) 128 gobottest.Assert(t, err, nil) 129 130 gobottest.Assert(t, fs.Files[pwmDutyCyclePath].Contents, "500000") 131 132 err = a.ServoWrite("33", 180) 133 gobottest.Assert(t, err, nil) 134 135 gobottest.Assert(t, fs.Files[pwmDutyCyclePath].Contents, "2000000") 136 gobottest.Assert(t, a.Finalize(), nil) 137 } 138 139 func TestSetPeriod(t *testing.T) { 140 // arrange 141 a, fs := initTestAdaptorWithMockedFilesystem(pwmMockPaths) 142 preparePwmFs(fs) 143 144 newPeriod := uint32(2550000) 145 // act 146 err := a.SetPeriod("33", newPeriod) 147 // assert 148 gobottest.Assert(t, err, nil) 149 gobottest.Assert(t, fs.Files[pwmExportPath].Contents, "0") 150 gobottest.Assert(t, fs.Files[pwmEnablePath].Contents, "1") 151 gobottest.Assert(t, fs.Files[pwmPeriodPath].Contents, fmt.Sprintf("%d", newPeriod)) 152 gobottest.Assert(t, fs.Files[pwmDutyCyclePath].Contents, "0") 153 gobottest.Assert(t, fs.Files[pwmPolarityPath].Contents, "normal") 154 155 // arrange test for automatic adjustment of duty cycle to lower value 156 err = a.PwmWrite("33", 127) // 127 is a little bit smaller than 50% of period 157 gobottest.Assert(t, err, nil) 158 gobottest.Assert(t, fs.Files[pwmDutyCyclePath].Contents, fmt.Sprintf("%d", 1270000)) 159 newPeriod = newPeriod / 10 160 161 // act 162 err = a.SetPeriod("33", newPeriod) 163 164 // assert 165 gobottest.Assert(t, err, nil) 166 gobottest.Assert(t, fs.Files[pwmDutyCyclePath].Contents, fmt.Sprintf("%d", 127000)) 167 168 // arrange test for automatic adjustment of duty cycle to higher value 169 newPeriod = newPeriod * 20 170 171 // act 172 err = a.SetPeriod("33", newPeriod) 173 174 // assert 175 gobottest.Assert(t, err, nil) 176 gobottest.Assert(t, fs.Files[pwmDutyCyclePath].Contents, fmt.Sprintf("%d", 2540000)) 177 } 178 179 func TestFinalizeErrorAfterGPIO(t *testing.T) { 180 a, fs := initTestAdaptorWithMockedFilesystem(gpioMockPaths) 181 182 gobottest.Assert(t, a.DigitalWrite("7", 1), nil) 183 184 fs.WithWriteError = true 185 186 err := a.Finalize() 187 gobottest.Assert(t, strings.Contains(err.Error(), "write error"), true) 188 } 189 190 func TestFinalizeErrorAfterPWM(t *testing.T) { 191 a, fs := initTestAdaptorWithMockedFilesystem(pwmMockPaths) 192 preparePwmFs(fs) 193 194 gobottest.Assert(t, a.PwmWrite("33", 1), nil) 195 196 fs.WithWriteError = true 197 198 err := a.Finalize() 199 gobottest.Assert(t, strings.Contains(err.Error(), "write error"), true) 200 } 201 202 func TestSpiDefaultValues(t *testing.T) { 203 a := NewAdaptor() 204 205 gobottest.Assert(t, a.SpiDefaultBusNumber(), 0) 206 gobottest.Assert(t, a.SpiDefaultChipNumber(), 0) 207 gobottest.Assert(t, a.SpiDefaultMode(), 0) 208 gobottest.Assert(t, a.SpiDefaultBitCount(), 8) 209 gobottest.Assert(t, a.SpiDefaultMaxSpeed(), int64(500000)) 210 } 211 212 func TestI2cDefaultBus(t *testing.T) { 213 a := NewAdaptor() 214 gobottest.Assert(t, a.DefaultI2cBus(), 1) 215 } 216 217 func TestI2cFinalizeWithErrors(t *testing.T) { 218 // arrange 219 a := NewAdaptor() 220 a.sys.UseMockSyscall() 221 fs := a.sys.UseMockFilesystem([]string{"/dev/i2c-4"}) 222 gobottest.Assert(t, a.Connect(), nil) 223 con, err := a.GetI2cConnection(0xff, 4) 224 gobottest.Assert(t, err, nil) 225 _, err = con.Write([]byte{0xbf}) 226 gobottest.Assert(t, err, nil) 227 fs.WithCloseError = true 228 // act 229 err = a.Finalize() 230 // assert 231 gobottest.Assert(t, strings.Contains(err.Error(), "close error"), true) 232 } 233 234 func Test_validateSpiBusNumber(t *testing.T) { 235 var tests = map[string]struct { 236 busNr int 237 wantErr error 238 }{ 239 "number_negative_error": { 240 busNr: -1, 241 wantErr: fmt.Errorf("Bus number -1 out of range"), 242 }, 243 "number_0_ok": { 244 busNr: 0, 245 }, 246 "number_1_error": { 247 busNr: 1, 248 wantErr: fmt.Errorf("Bus number 1 out of range"), 249 }, 250 "number_2_ok": { 251 busNr: 2, 252 }, 253 "number_3_error": { 254 busNr: 3, 255 wantErr: fmt.Errorf("Bus number 3 out of range"), 256 }, 257 } 258 for name, tc := range tests { 259 t.Run(name, func(t *testing.T) { 260 // arrange 261 a := NewAdaptor() 262 // act 263 err := a.validateSpiBusNumber(tc.busNr) 264 // assert 265 gobottest.Assert(t, err, tc.wantErr) 266 }) 267 } 268 } 269 270 func Test_validateI2cBusNumber(t *testing.T) { 271 var tests = map[string]struct { 272 busNr int 273 wantErr error 274 }{ 275 "number_negative_error": { 276 busNr: -1, 277 wantErr: fmt.Errorf("Bus number -1 out of range"), 278 }, 279 "number_0_ok": { 280 busNr: 0, 281 }, 282 "number_1_ok": { 283 busNr: 1, 284 }, 285 "number_2_ok": { 286 busNr: 2, 287 }, 288 "number_3_ok": { 289 busNr: 3, 290 }, 291 "number_4_ok": { 292 busNr: 4, 293 }, 294 "number_5_error": { 295 busNr: 5, 296 wantErr: fmt.Errorf("Bus number 5 out of range"), 297 }, 298 } 299 for name, tc := range tests { 300 t.Run(name, func(t *testing.T) { 301 // arrange 302 a := NewAdaptor() 303 // act 304 err := a.validateI2cBusNumber(tc.busNr) 305 // assert 306 gobottest.Assert(t, err, tc.wantErr) 307 }) 308 } 309 } 310 311 func Test_translateDigitalPin(t *testing.T) { 312 var tests = map[string]struct { 313 access string 314 pin string 315 wantChip string 316 wantLine int 317 wantErr error 318 }{ 319 "cdev_ok": { 320 access: "cdev", 321 pin: "7", 322 wantChip: "gpiochip0", 323 wantLine: 17, 324 }, 325 "sysfs_ok": { 326 access: "sysfs", 327 pin: "7", 328 wantChip: "", 329 wantLine: 17, 330 }, 331 "unknown_pin": { 332 pin: "99", 333 wantChip: "", 334 wantLine: -1, 335 wantErr: fmt.Errorf("'99' is not a valid id for a digital pin"), 336 }, 337 } 338 for name, tc := range tests { 339 t.Run(name, func(t *testing.T) { 340 // arrange 341 a := NewAdaptor() 342 a.sys.UseDigitalPinAccessWithMockFs(tc.access, []string{}) 343 // act 344 chip, line, err := a.translateDigitalPin(tc.pin) 345 // assert 346 gobottest.Assert(t, err, tc.wantErr) 347 gobottest.Assert(t, chip, tc.wantChip) 348 gobottest.Assert(t, line, tc.wantLine) 349 }) 350 } 351 } 352 353 func Test_translatePWMPin(t *testing.T) { 354 basePaths := []string{ 355 "/sys/devices/platform/ff680020.pwm/pwm/", 356 "/sys/devices/platform/ff680030.pwm/pwm/", 357 } 358 var tests = map[string]struct { 359 pin string 360 chip string 361 wantDir string 362 wantChannel int 363 wantErr error 364 }{ 365 "32_chip0": { 366 pin: "32", 367 chip: "pwmchip0", 368 wantDir: "/sys/devices/platform/ff680030.pwm/pwm/pwmchip0", 369 wantChannel: 0, 370 }, 371 "32_chip1": { 372 pin: "32", 373 chip: "pwmchip1", 374 wantDir: "/sys/devices/platform/ff680030.pwm/pwm/pwmchip1", 375 wantChannel: 0, 376 }, 377 "32_chip2": { 378 pin: "32", 379 chip: "pwmchip2", 380 wantDir: "/sys/devices/platform/ff680030.pwm/pwm/pwmchip2", 381 wantChannel: 0, 382 }, 383 "32_chip3": { 384 pin: "32", 385 chip: "pwmchip3", 386 wantDir: "/sys/devices/platform/ff680030.pwm/pwm/pwmchip3", 387 wantChannel: 0, 388 }, 389 "33_chip0": { 390 pin: "33", 391 chip: "pwmchip0", 392 wantDir: "/sys/devices/platform/ff680020.pwm/pwm/pwmchip0", 393 wantChannel: 0, 394 }, 395 "33_chip1": { 396 pin: "33", 397 chip: "pwmchip1", 398 wantDir: "/sys/devices/platform/ff680020.pwm/pwm/pwmchip1", 399 wantChannel: 0, 400 }, 401 "33_chip2": { 402 pin: "33", 403 chip: "pwmchip2", 404 wantDir: "/sys/devices/platform/ff680020.pwm/pwm/pwmchip2", 405 wantChannel: 0, 406 }, 407 "invalid_pin": { 408 pin: "7", 409 wantDir: "", 410 wantChannel: -1, 411 wantErr: fmt.Errorf("'7' is not a valid id for a PWM pin"), 412 }, 413 } 414 for name, tc := range tests { 415 t.Run(name, func(t *testing.T) { 416 // arrange 417 mockedPaths := []string{} 418 for _, base := range basePaths { 419 mockedPaths = append(mockedPaths, base+tc.chip+"/") 420 } 421 a, _ := initTestAdaptorWithMockedFilesystem(mockedPaths) 422 // act 423 dir, channel, err := a.translatePWMPin(tc.pin) 424 // assert 425 gobottest.Assert(t, err, tc.wantErr) 426 gobottest.Assert(t, dir, tc.wantDir) 427 gobottest.Assert(t, channel, tc.wantChannel) 428 }) 429 } 430 }