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  }