gobot.io/x/gobot@v1.16.0/platforms/firmata/client/client_test.go (about)

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