gobot.io/x/gobot/v2@v2.1.0/platforms/firmata/client/client_test.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "log" 6 "sync" 7 "testing" 8 "time" 9 10 "gobot.io/x/gobot/v2/gobottest" 11 ) 12 13 const semPublishWait = 10 * time.Millisecond 14 15 type readWriteCloser struct { 16 id string 17 } 18 19 var testWriteData = bytes.Buffer{} 20 var writeDataMutex sync.Mutex 21 22 // do not set this data directly, use always addTestReadData() 23 var testReadDataMap = make(map[string][]byte) 24 var rwDataMapMutex sync.Mutex 25 26 // arduino uno r3 protocol response "2.3" 27 var testDataProtocolResponse = []byte{249, 2, 3} 28 29 // arduino uno r3 firmware response "StandardFirmata.ino" 30 var testDataFirmwareResponse = []byte{240, 121, 2, 3, 83, 0, 116, 0, 97, 0, 110, 0, 100, 0, 97, 31 0, 114, 0, 100, 0, 70, 0, 105, 0, 114, 0, 109, 0, 97, 0, 116, 0, 97, 0, 46, 32 0, 105, 0, 110, 0, 111, 0, 247} 33 34 // arduino uno r3 capabilities response 35 var testDataCapabilitiesResponse = []byte{240, 108, 127, 127, 0, 1, 1, 1, 4, 14, 127, 0, 1, 1, 1, 3, 36 8, 4, 14, 127, 0, 1, 1, 1, 4, 14, 127, 0, 1, 1, 1, 3, 8, 4, 14, 127, 0, 1, 37 1, 1, 3, 8, 4, 14, 127, 0, 1, 1, 1, 4, 14, 127, 0, 1, 1, 1, 4, 14, 127, 0, 38 1, 1, 1, 3, 8, 4, 14, 127, 0, 1, 1, 1, 3, 8, 4, 14, 127, 0, 1, 1, 1, 3, 8, 39 4, 14, 127, 0, 1, 1, 1, 4, 14, 127, 0, 1, 1, 1, 4, 14, 127, 0, 1, 1, 1, 2, 40 10, 127, 0, 1, 1, 1, 2, 10, 127, 0, 1, 1, 1, 2, 10, 127, 0, 1, 1, 1, 2, 10, 41 127, 0, 1, 1, 1, 2, 10, 6, 1, 127, 0, 1, 1, 1, 2, 10, 6, 1, 127, 247} 42 43 // arduino uno r3 analog mapping response 44 var testDataAnalogMappingResponse = []byte{240, 106, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 45 127, 127, 127, 127, 0, 1, 2, 3, 4, 5, 247} 46 47 func (readWriteCloser) Write(p []byte) (int, error) { 48 writeDataMutex.Lock() 49 defer writeDataMutex.Unlock() 50 return testWriteData.Write(p) 51 } 52 53 func (rwc readWriteCloser) addTestReadData(d []byte) { 54 // concurrent read/change of map is not allowed 55 rwDataMapMutex.Lock() 56 defer rwDataMapMutex.Unlock() 57 58 data, ok := testReadDataMap[rwc.id] 59 if !ok { 60 data = []byte{} 61 } 62 63 data = append(data, d...) 64 testReadDataMap[rwc.id] = data 65 } 66 67 func (rwc readWriteCloser) Read(b []byte) (int, error) { 68 // concurrent change of map is not allowed 69 rwDataMapMutex.Lock() 70 defer rwDataMapMutex.Unlock() 71 72 data, ok := testReadDataMap[rwc.id] 73 if !ok { 74 // there was no content stored before to read 75 log.Printf("no content stored in %s", rwc.id) 76 return 0, nil 77 } 78 size := len(b) 79 if len(data) < size { 80 size = len(data) 81 } 82 copy(b, []byte(data)[:size]) 83 testReadDataMap[rwc.id] = data[size:] 84 return size, nil 85 } 86 87 func (readWriteCloser) Close() error { 88 return nil 89 } 90 91 func initTestFirmataWithReadWriteCloser(name string, data ...[]byte) (*Client, readWriteCloser) { 92 b := New() 93 rwc := readWriteCloser{id: name} 94 b.connection = rwc 95 96 for _, d := range data { 97 rwc.addTestReadData(d) 98 b.process() 99 } 100 101 b.setConnected(true) 102 return b, rwc 103 } 104 105 func TestPins(t *testing.T) { 106 b, _ := initTestFirmataWithReadWriteCloser(t.Name(), testDataCapabilitiesResponse, testDataAnalogMappingResponse) 107 gobottest.Assert(t, len(b.Pins()), 20) 108 gobottest.Assert(t, len(b.analogPins), 6) 109 } 110 111 func TestProtocolVersionQuery(t *testing.T) { 112 b, _ := initTestFirmataWithReadWriteCloser(t.Name()) 113 gobottest.Assert(t, b.ProtocolVersionQuery(), nil) 114 } 115 116 func TestFirmwareQuery(t *testing.T) { 117 b, _ := initTestFirmataWithReadWriteCloser(t.Name()) 118 gobottest.Assert(t, b.FirmwareQuery(), nil) 119 } 120 121 func TestPinStateQuery(t *testing.T) { 122 b, _ := initTestFirmataWithReadWriteCloser(t.Name()) 123 gobottest.Assert(t, b.PinStateQuery(1), nil) 124 } 125 126 func TestProcessProtocolVersion(t *testing.T) { 127 sem := make(chan bool) 128 b, rwc := initTestFirmataWithReadWriteCloser(t.Name()) 129 rwc.addTestReadData(testDataProtocolResponse) 130 131 b.Once(b.Event("ProtocolVersion"), func(data interface{}) { 132 gobottest.Assert(t, data, "2.3") 133 sem <- true 134 }) 135 136 b.process() 137 138 select { 139 case <-sem: 140 case <-time.After(semPublishWait): 141 t.Errorf("ProtocolVersion was not published") 142 } 143 } 144 145 func TestProcessAnalogRead0(t *testing.T) { 146 sem := make(chan bool) 147 b, rwc := initTestFirmataWithReadWriteCloser(t.Name(), testDataCapabilitiesResponse, testDataAnalogMappingResponse) 148 rwc.addTestReadData([]byte{0xE0, 0x23, 0x05}) 149 150 b.Once(b.Event("AnalogRead0"), func(data interface{}) { 151 gobottest.Assert(t, data, 675) 152 sem <- true 153 }) 154 155 b.process() 156 157 select { 158 case <-sem: 159 case <-time.After(semPublishWait): 160 t.Errorf("AnalogRead0 was not published") 161 } 162 } 163 164 func TestProcessAnalogRead1(t *testing.T) { 165 sem := make(chan bool) 166 b, rwc := initTestFirmataWithReadWriteCloser(t.Name(), testDataCapabilitiesResponse, testDataAnalogMappingResponse) 167 rwc.addTestReadData([]byte{0xE1, 0x23, 0x06}) 168 169 b.Once(b.Event("AnalogRead1"), func(data interface{}) { 170 gobottest.Assert(t, data, 803) 171 sem <- true 172 }) 173 174 b.process() 175 176 select { 177 case <-sem: 178 case <-time.After(semPublishWait): 179 t.Errorf("AnalogRead1 was not published") 180 } 181 } 182 183 func TestProcessDigitalRead2(t *testing.T) { 184 sem := make(chan bool) 185 b, rwc := initTestFirmataWithReadWriteCloser(t.Name(), testDataCapabilitiesResponse) 186 b.pins[2].Mode = Input 187 rwc.addTestReadData([]byte{0x90, 0x04, 0x00}) 188 189 b.Once(b.Event("DigitalRead2"), func(data interface{}) { 190 gobottest.Assert(t, data, 1) 191 sem <- true 192 }) 193 194 b.process() 195 196 select { 197 case <-sem: 198 case <-time.After(semPublishWait): 199 t.Errorf("DigitalRead2 was not published") 200 } 201 } 202 203 func TestProcessDigitalRead4(t *testing.T) { 204 sem := make(chan bool) 205 b, rwc := initTestFirmataWithReadWriteCloser(t.Name(), testDataCapabilitiesResponse) 206 b.pins[4].Mode = Input 207 rwc.addTestReadData([]byte{0x90, 0x16, 0x00}) 208 209 b.Once(b.Event("DigitalRead4"), func(data interface{}) { 210 gobottest.Assert(t, data, 1) 211 sem <- true 212 }) 213 214 b.process() 215 216 select { 217 case <-sem: 218 case <-time.After(semPublishWait): 219 t.Errorf("DigitalRead4 was not published") 220 } 221 } 222 223 func TestDigitalWrite(t *testing.T) { 224 b, _ := initTestFirmataWithReadWriteCloser(t.Name(), testDataCapabilitiesResponse) 225 gobottest.Assert(t, b.DigitalWrite(13, 0), nil) 226 } 227 228 func TestSetPinMode(t *testing.T) { 229 b, _ := initTestFirmataWithReadWriteCloser(t.Name(), testDataCapabilitiesResponse) 230 gobottest.Assert(t, b.SetPinMode(13, Output), nil) 231 } 232 233 func TestAnalogWrite(t *testing.T) { 234 b, _ := initTestFirmataWithReadWriteCloser(t.Name(), testDataCapabilitiesResponse) 235 gobottest.Assert(t, b.AnalogWrite(0, 128), nil) 236 } 237 238 func TestReportAnalog(t *testing.T) { 239 b, _ := initTestFirmataWithReadWriteCloser(t.Name()) 240 gobottest.Assert(t, b.ReportAnalog(0, 1), nil) 241 gobottest.Assert(t, b.ReportAnalog(0, 0), nil) 242 } 243 244 func TestProcessPinState13(t *testing.T) { 245 sem := make(chan bool) 246 b, rwc := initTestFirmataWithReadWriteCloser(t.Name(), testDataCapabilitiesResponse, testDataAnalogMappingResponse) 247 rwc.addTestReadData([]byte{240, 110, 13, 1, 1, 247}) 248 249 b.Once(b.Event("PinState13"), func(data interface{}) { 250 gobottest.Assert(t, data, Pin{[]int{0, 1, 4}, 1, 0, 1, 127}) 251 sem <- true 252 }) 253 254 b.process() 255 256 select { 257 case <-sem: 258 case <-time.After(semPublishWait): 259 t.Errorf("PinState13 was not published") 260 } 261 } 262 263 func TestI2cConfig(t *testing.T) { 264 b, _ := initTestFirmataWithReadWriteCloser(t.Name()) 265 gobottest.Assert(t, b.I2cConfig(100), nil) 266 } 267 268 func TestI2cWrite(t *testing.T) { 269 b, _ := initTestFirmataWithReadWriteCloser(t.Name()) 270 gobottest.Assert(t, b.I2cWrite(0x00, []byte{0x01, 0x02}), nil) 271 } 272 273 func TestI2cRead(t *testing.T) { 274 b, _ := initTestFirmataWithReadWriteCloser(t.Name()) 275 gobottest.Assert(t, b.I2cRead(0x00, 10), nil) 276 } 277 278 func TestWriteSysex(t *testing.T) { 279 b, _ := initTestFirmataWithReadWriteCloser(t.Name()) 280 gobottest.Assert(t, b.WriteSysex([]byte{0x01, 0x02}), nil) 281 } 282 283 func TestProcessI2cReply(t *testing.T) { 284 sem := make(chan bool) 285 b, rwc := initTestFirmataWithReadWriteCloser(t.Name()) 286 rwc.addTestReadData([]byte{240, 119, 9, 0, 0, 0, 24, 1, 1, 0, 26, 1, 247}) 287 288 b.Once(b.Event("I2cReply"), func(data interface{}) { 289 gobottest.Assert(t, data, I2cReply{ 290 Address: 9, 291 Register: 0, 292 Data: []byte{152, 1, 154}, 293 }) 294 sem <- true 295 }) 296 297 b.process() 298 299 select { 300 case <-sem: 301 case <-time.After(semPublishWait): 302 t.Errorf("I2cReply was not published") 303 } 304 } 305 306 func TestProcessFirmwareQuery(t *testing.T) { 307 sem := make(chan bool) 308 b, rwc := initTestFirmataWithReadWriteCloser(t.Name()) 309 rwc.addTestReadData(testDataFirmwareResponse) 310 311 b.Once(b.Event("FirmwareQuery"), func(data interface{}) { 312 gobottest.Assert(t, data, "StandardFirmata.ino") 313 sem <- true 314 }) 315 316 b.process() 317 318 select { 319 case <-sem: 320 case <-time.After(semPublishWait): 321 t.Errorf("FirmwareQuery was not published") 322 } 323 } 324 325 func TestProcessStringData(t *testing.T) { 326 sem := make(chan bool) 327 b, rwc := initTestFirmataWithReadWriteCloser(t.Name()) 328 rwc.addTestReadData(append([]byte{240, 0x71}, append([]byte("Hello Firmata!"), 247)...)) 329 330 b.Once(b.Event("StringData"), func(data interface{}) { 331 gobottest.Assert(t, data, "Hello Firmata!") 332 sem <- true 333 }) 334 335 b.process() 336 337 select { 338 case <-sem: 339 case <-time.After(semPublishWait): 340 t.Errorf("StringData was not published") 341 } 342 } 343 344 func TestConnect(t *testing.T) { 345 b, rwc := initTestFirmataWithReadWriteCloser(t.Name()) 346 b.setConnected(false) 347 348 rwc.addTestReadData(testDataProtocolResponse) 349 350 b.Once(b.Event("ProtocolVersion"), func(data interface{}) { 351 rwc.addTestReadData(testDataFirmwareResponse) 352 }) 353 354 b.Once(b.Event("FirmwareQuery"), func(data interface{}) { 355 rwc.addTestReadData(testDataCapabilitiesResponse) 356 }) 357 358 b.Once(b.Event("CapabilityQuery"), func(data interface{}) { 359 rwc.addTestReadData(testDataAnalogMappingResponse) 360 }) 361 362 b.Once(b.Event("AnalogMappingQuery"), func(data interface{}) { 363 rwc.addTestReadData(testDataProtocolResponse) 364 }) 365 366 gobottest.Assert(t, b.Connect(rwc), nil) 367 time.Sleep(150 * time.Millisecond) 368 gobottest.Assert(t, b.Disconnect(), nil) 369 } 370 371 func TestServoConfig(t *testing.T) { 372 b := New() 373 b.connection = readWriteCloser{} 374 375 tests := []struct { 376 description string 377 arguments [3]int 378 expected []byte 379 result error 380 }{ 381 { 382 description: "Min values for min & max", 383 arguments: [3]int{9, 0, 0}, 384 expected: []byte{0xF0, 0x70, 9, 0, 0, 0, 0, 0xF7}, 385 }, 386 { 387 description: "Max values for min & max", 388 arguments: [3]int{9, 0x3FFF, 0x3FFF}, 389 expected: []byte{0xF0, 0x70, 9, 0x7F, 0x7F, 0x7F, 0x7F, 0xF7}, 390 }, 391 { 392 description: "Clipped max values for min & max", 393 arguments: [3]int{9, 0xFFFF, 0xFFFF}, 394 expected: []byte{0xF0, 0x70, 9, 0x7F, 0x7F, 0x7F, 0x7F, 0xF7}, 395 }, 396 } 397 398 for _, test := range tests { 399 writeDataMutex.Lock() 400 testWriteData.Reset() 401 writeDataMutex.Unlock() 402 err := b.ServoConfig(test.arguments[0], test.arguments[1], test.arguments[2]) 403 writeDataMutex.Lock() 404 gobottest.Assert(t, testWriteData.Bytes(), test.expected) 405 gobottest.Assert(t, err, test.result) 406 writeDataMutex.Unlock() 407 } 408 } 409 410 func TestProcessSysexData(t *testing.T) { 411 sem := make(chan bool) 412 b, rwc := initTestFirmataWithReadWriteCloser(t.Name()) 413 rwc.addTestReadData([]byte{240, 17, 1, 2, 3, 247}) 414 415 b.Once("SysexResponse", func(data interface{}) { 416 gobottest.Assert(t, data, []byte{240, 17, 1, 2, 3, 247}) 417 sem <- true 418 }) 419 420 b.process() 421 422 select { 423 case <-sem: 424 case <-time.After(semPublishWait): 425 t.Errorf("SysexResponse was not published") 426 } 427 }