gobot.io/x/gobot/v2@v2.1.0/platforms/nanopi/nanopi_adaptor_test.go (about) 1 package nanopi 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 gpio203Path = "/sys/class/gpio/gpio203/" 17 gpio199Path = "/sys/class/gpio/gpio199/" 18 ) 19 20 const ( 21 pwmDir = "/sys/devices/platform/soc/1c21400.pwm/pwm/pwmchip0/" 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 gpio203Path + "value", 44 gpio203Path + "direction", 45 gpio199Path + "value", 46 gpio199Path + "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 := NewNeoAdaptor() 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 := NewNeoAdaptor() 77 gobottest.Assert(t, strings.HasPrefix(a.Name(), "NanoPi NEO 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[gpio203Path+"value"].Contents, "1") 88 89 fs.Files[gpio199Path+"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("PWM", 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("PWM", 0) 128 gobottest.Assert(t, err, nil) 129 130 gobottest.Assert(t, fs.Files[pwmDutyCyclePath].Contents, "500000") 131 132 err = a.ServoWrite("PWM", 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("PWM", 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("PWM", 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("PWM", 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("PWM", 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("PWM", 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 := NewNeoAdaptor() 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 := NewNeoAdaptor() 214 gobottest.Assert(t, a.DefaultI2cBus(), 0) 215 } 216 217 func TestI2cFinalizeWithErrors(t *testing.T) { 218 // arrange 219 a := NewNeoAdaptor() 220 a.sys.UseMockSyscall() 221 fs := a.sys.UseMockFilesystem([]string{"/dev/i2c-1"}) 222 gobottest.Assert(t, a.Connect(), nil) 223 con, err := a.GetI2cConnection(0xff, 1) 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 } 251 for name, tc := range tests { 252 t.Run(name, func(t *testing.T) { 253 // arrange 254 a := NewNeoAdaptor() 255 // act 256 err := a.validateSpiBusNumber(tc.busNr) 257 // assert 258 gobottest.Assert(t, err, tc.wantErr) 259 }) 260 } 261 } 262 263 func Test_validateI2cBusNumber(t *testing.T) { 264 var tests = map[string]struct { 265 busNr int 266 wantErr error 267 }{ 268 "number_negative_error": { 269 busNr: -1, 270 wantErr: fmt.Errorf("Bus number -1 out of range"), 271 }, 272 "number_0_ok": { 273 busNr: 0, 274 }, 275 "number_1_ok": { 276 busNr: 1, 277 }, 278 "number_2_ok": { 279 busNr: 2, 280 }, 281 "number_3_error": { 282 busNr: 3, 283 wantErr: fmt.Errorf("Bus number 3 out of range"), 284 }, 285 } 286 for name, tc := range tests { 287 t.Run(name, func(t *testing.T) { 288 // arrange 289 a := NewNeoAdaptor() 290 // act 291 err := a.validateI2cBusNumber(tc.busNr) 292 // assert 293 gobottest.Assert(t, err, tc.wantErr) 294 }) 295 } 296 } 297 298 func Test_translateDigitalPin(t *testing.T) { 299 var tests = map[string]struct { 300 access string 301 pin string 302 wantChip string 303 wantLine int 304 wantErr error 305 }{ 306 "cdev_ok": { 307 access: "cdev", 308 pin: "7", 309 wantChip: "gpiochip0", 310 wantLine: 203, 311 }, 312 "sysfs_ok": { 313 access: "sysfs", 314 pin: "7", 315 wantChip: "", 316 wantLine: 203, 317 }, 318 "unknown_pin": { 319 pin: "99", 320 wantChip: "", 321 wantLine: -1, 322 wantErr: fmt.Errorf("'99' is not a valid id for a digital pin"), 323 }, 324 } 325 for name, tc := range tests { 326 t.Run(name, func(t *testing.T) { 327 // arrange 328 a := NewNeoAdaptor() 329 a.sys.UseDigitalPinAccessWithMockFs(tc.access, []string{}) 330 // act 331 chip, line, err := a.translateDigitalPin(tc.pin) 332 // assert 333 gobottest.Assert(t, err, tc.wantErr) 334 gobottest.Assert(t, chip, tc.wantChip) 335 gobottest.Assert(t, line, tc.wantLine) 336 }) 337 } 338 } 339 340 func Test_translatePWMPin(t *testing.T) { 341 basePaths := []string{"/sys/devices/platform/soc/1c21400.pwm/pwm/"} 342 var tests = map[string]struct { 343 pin string 344 chip string 345 wantDir string 346 wantChannel int 347 wantErr error 348 }{ 349 "33_chip0": { 350 pin: "PWM", 351 chip: "pwmchip0", 352 wantDir: "/sys/devices/platform/soc/1c21400.pwm/pwm/pwmchip0", 353 wantChannel: 0, 354 }, 355 "invalid_pin": { 356 pin: "7", 357 wantDir: "", 358 wantChannel: -1, 359 wantErr: fmt.Errorf("'7' is not a valid id for a PWM pin"), 360 }, 361 } 362 for name, tc := range tests { 363 t.Run(name, func(t *testing.T) { 364 // arrange 365 mockedPaths := []string{} 366 for _, base := range basePaths { 367 mockedPaths = append(mockedPaths, base+tc.chip+"/") 368 } 369 a, _ := initTestAdaptorWithMockedFilesystem(mockedPaths) 370 // act 371 dir, channel, err := a.translatePWMPin(tc.pin) 372 // assert 373 gobottest.Assert(t, err, tc.wantErr) 374 gobottest.Assert(t, dir, tc.wantDir) 375 gobottest.Assert(t, channel, tc.wantChannel) 376 }) 377 } 378 }