gobot.io/x/gobot/v2@v2.1.0/platforms/raspi/raspi_adaptor_test.go (about)

     1  package raspi
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  	"testing"
     8  
     9  	"runtime"
    10  	"strconv"
    11  	"sync"
    12  
    13  	"gobot.io/x/gobot/v2"
    14  	"gobot.io/x/gobot/v2/drivers/gpio"
    15  	"gobot.io/x/gobot/v2/drivers/i2c"
    16  	"gobot.io/x/gobot/v2/drivers/spi"
    17  	"gobot.io/x/gobot/v2/gobottest"
    18  	"gobot.io/x/gobot/v2/system"
    19  )
    20  
    21  // make sure that this Adaptor fulfills all the required interfaces
    22  var _ gobot.Adaptor = (*Adaptor)(nil)
    23  var _ gobot.DigitalPinnerProvider = (*Adaptor)(nil)
    24  var _ gobot.PWMPinnerProvider = (*Adaptor)(nil)
    25  var _ gpio.DigitalReader = (*Adaptor)(nil)
    26  var _ gpio.DigitalWriter = (*Adaptor)(nil)
    27  var _ gpio.PwmWriter = (*Adaptor)(nil)
    28  var _ gpio.ServoWriter = (*Adaptor)(nil)
    29  var _ i2c.Connector = (*Adaptor)(nil)
    30  var _ spi.Connector = (*Adaptor)(nil)
    31  
    32  func initTestAdaptorWithMockedFilesystem(mockPaths []string) (*Adaptor, *system.MockFilesystem) {
    33  	a := NewAdaptor()
    34  	fs := a.sys.UseMockFilesystem(mockPaths)
    35  	a.Connect()
    36  	return a, fs
    37  }
    38  
    39  func TestName(t *testing.T) {
    40  	a := NewAdaptor()
    41  
    42  	gobottest.Assert(t, strings.HasPrefix(a.Name(), "RaspberryPi"), true)
    43  	a.SetName("NewName")
    44  	gobottest.Assert(t, a.Name(), "NewName")
    45  }
    46  
    47  func TestGetDefaultBus(t *testing.T) {
    48  	const contentPattern = "Hardware        : BCM2708\n%sSerial          : 000000003bc748ea\n"
    49  	var tests = map[string]struct {
    50  		revisionPart string
    51  		wantRev      string
    52  		wantBus      int
    53  	}{
    54  		"no_revision": {
    55  			wantRev: "0",
    56  			wantBus: 0,
    57  		},
    58  		"rev_1": {
    59  			revisionPart: "Revision        : 0002\n",
    60  			wantRev:      "1",
    61  			wantBus:      0,
    62  		},
    63  		"rev_2": {
    64  			revisionPart: "Revision        : 000D\n",
    65  			wantRev:      "2",
    66  			wantBus:      1,
    67  		},
    68  		"rev_3": {
    69  			revisionPart: "Revision        : 0010\n",
    70  			wantRev:      "3",
    71  			wantBus:      1,
    72  		},
    73  	}
    74  	for name, tc := range tests {
    75  		t.Run(name, func(t *testing.T) {
    76  			// arrange
    77  			a := NewAdaptor()
    78  			fs := a.sys.UseMockFilesystem([]string{infoFile})
    79  			fs.Files[infoFile].Contents = fmt.Sprintf(contentPattern, tc.revisionPart)
    80  			gobottest.Assert(t, a.revision, "")
    81  			//act, will read and refresh the revision
    82  			gotBus := a.DefaultI2cBus()
    83  			//assert
    84  			gobottest.Assert(t, a.revision, tc.wantRev)
    85  			gobottest.Assert(t, gotBus, tc.wantBus)
    86  		})
    87  	}
    88  }
    89  
    90  func TestFinalize(t *testing.T) {
    91  	mockedPaths := []string{
    92  		"/sys/class/gpio/export",
    93  		"/sys/class/gpio/unexport",
    94  		"/dev/pi-blaster",
    95  		"/dev/i2c-1",
    96  		"/dev/i2c-0",
    97  		"/dev/spidev0.0",
    98  		"/dev/spidev0.1",
    99  	}
   100  	a, _ := initTestAdaptorWithMockedFilesystem(mockedPaths)
   101  
   102  	a.DigitalWrite("3", 1)
   103  	a.PwmWrite("7", 255)
   104  
   105  	a.GetI2cConnection(0xff, 0)
   106  	gobottest.Assert(t, a.Finalize(), nil)
   107  }
   108  
   109  func TestDigitalPWM(t *testing.T) {
   110  	mockedPaths := []string{"/dev/pi-blaster"}
   111  	a, fs := initTestAdaptorWithMockedFilesystem(mockedPaths)
   112  	a.PiBlasterPeriod = 20000000
   113  
   114  	gobottest.Assert(t, a.PwmWrite("7", 4), nil)
   115  
   116  	pin, _ := a.PWMPin("7")
   117  	period, _ := pin.Period()
   118  	gobottest.Assert(t, period, uint32(20000000))
   119  
   120  	gobottest.Assert(t, a.PwmWrite("7", 255), nil)
   121  
   122  	gobottest.Assert(t, strings.Split(fs.Files["/dev/pi-blaster"].Contents, "\n")[0], "4=1")
   123  
   124  	gobottest.Assert(t, a.ServoWrite("11", 90), nil)
   125  
   126  	gobottest.Assert(t, strings.Split(fs.Files["/dev/pi-blaster"].Contents, "\n")[0], "17=0.5")
   127  
   128  	gobottest.Assert(t, a.PwmWrite("notexist", 1), errors.New("Not a valid pin"))
   129  	gobottest.Assert(t, a.ServoWrite("notexist", 1), errors.New("Not a valid pin"))
   130  
   131  	pin, _ = a.PWMPin("12")
   132  	period, _ = pin.Period()
   133  	gobottest.Assert(t, period, uint32(20000000))
   134  
   135  	gobottest.Assert(t, pin.SetDutyCycle(1.5*1000*1000), nil)
   136  
   137  	gobottest.Assert(t, strings.Split(fs.Files["/dev/pi-blaster"].Contents, "\n")[0], "18=0.075")
   138  }
   139  
   140  func TestDigitalIO(t *testing.T) {
   141  	mockedPaths := []string{
   142  		"/sys/class/gpio/export",
   143  		"/sys/class/gpio/unexport",
   144  		"/sys/class/gpio/gpio4/value",
   145  		"/sys/class/gpio/gpio4/direction",
   146  		"/sys/class/gpio/gpio27/value",
   147  		"/sys/class/gpio/gpio27/direction",
   148  	}
   149  	a, fs := initTestAdaptorWithMockedFilesystem(mockedPaths)
   150  
   151  	err := a.DigitalWrite("7", 1)
   152  	gobottest.Assert(t, err, nil)
   153  	gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio4/value"].Contents, "1")
   154  
   155  	a.revision = "2"
   156  	err = a.DigitalWrite("13", 1)
   157  	gobottest.Assert(t, err, nil)
   158  
   159  	i, err := a.DigitalRead("13")
   160  	gobottest.Assert(t, err, nil)
   161  	gobottest.Assert(t, i, 1)
   162  
   163  	gobottest.Assert(t, a.DigitalWrite("notexist", 1), errors.New("Not a valid pin"))
   164  	gobottest.Assert(t, a.Finalize(), nil)
   165  }
   166  
   167  func TestDigitalPinConcurrency(t *testing.T) {
   168  	oldProcs := runtime.GOMAXPROCS(0)
   169  	runtime.GOMAXPROCS(8)
   170  	defer runtime.GOMAXPROCS(oldProcs)
   171  
   172  	for retry := 0; retry < 20; retry++ {
   173  
   174  		a := NewAdaptor()
   175  		var wg sync.WaitGroup
   176  
   177  		for i := 0; i < 20; i++ {
   178  			wg.Add(1)
   179  			pinAsString := strconv.Itoa(i)
   180  			go func(pin string) {
   181  				defer wg.Done()
   182  				a.DigitalPin(pin)
   183  			}(pinAsString)
   184  		}
   185  
   186  		wg.Wait()
   187  	}
   188  }
   189  
   190  func TestPWMPin(t *testing.T) {
   191  	a := NewAdaptor()
   192  	if err := a.Connect(); err != nil {
   193  		panic(err)
   194  	}
   195  
   196  	gobottest.Assert(t, len(a.pwmPins), 0)
   197  
   198  	a.revision = "3"
   199  	firstSysPin, err := a.PWMPin("35")
   200  	gobottest.Assert(t, err, nil)
   201  	gobottest.Assert(t, len(a.pwmPins), 1)
   202  
   203  	secondSysPin, err := a.PWMPin("35")
   204  
   205  	gobottest.Assert(t, err, nil)
   206  	gobottest.Assert(t, len(a.pwmPins), 1)
   207  	gobottest.Assert(t, firstSysPin, secondSysPin)
   208  
   209  	otherSysPin, err := a.PWMPin("36")
   210  
   211  	gobottest.Assert(t, err, nil)
   212  	gobottest.Assert(t, len(a.pwmPins), 2)
   213  	gobottest.Refute(t, firstSysPin, otherSysPin)
   214  }
   215  
   216  func TestPWMPinsReConnect(t *testing.T) {
   217  	// arrange
   218  	a := NewAdaptor()
   219  	a.revision = "3"
   220  	if err := a.Connect(); err != nil {
   221  		panic(err)
   222  	}
   223  
   224  	_, err := a.PWMPin("35")
   225  	gobottest.Assert(t, err, nil)
   226  	gobottest.Assert(t, len(a.pwmPins), 1)
   227  	gobottest.Assert(t, a.Finalize(), nil)
   228  	// act
   229  	err = a.Connect()
   230  	// assert
   231  	gobottest.Assert(t, err, nil)
   232  	gobottest.Assert(t, len(a.pwmPins), 0)
   233  	_, _ = a.PWMPin("35")
   234  	_, err = a.PWMPin("36")
   235  	gobottest.Assert(t, err, nil)
   236  	gobottest.Assert(t, len(a.pwmPins), 2)
   237  }
   238  
   239  func TestSpiDefaultValues(t *testing.T) {
   240  	a := NewAdaptor()
   241  
   242  	gobottest.Assert(t, a.SpiDefaultBusNumber(), 0)
   243  	gobottest.Assert(t, a.SpiDefaultChipNumber(), 0)
   244  	gobottest.Assert(t, a.SpiDefaultMode(), 0)
   245  	gobottest.Assert(t, a.SpiDefaultMaxSpeed(), int64(500000))
   246  }
   247  
   248  func TestI2cDefaultBus(t *testing.T) {
   249  	mockedPaths := []string{"/dev/i2c-1"}
   250  	a, _ := initTestAdaptorWithMockedFilesystem(mockedPaths)
   251  	a.sys.UseMockSyscall()
   252  
   253  	a.revision = "0"
   254  	gobottest.Assert(t, a.DefaultI2cBus(), 0)
   255  
   256  	a.revision = "2"
   257  	gobottest.Assert(t, a.DefaultI2cBus(), 1)
   258  }
   259  
   260  func TestI2cFinalizeWithErrors(t *testing.T) {
   261  	// arrange
   262  	a := NewAdaptor()
   263  	a.sys.UseMockSyscall()
   264  	fs := a.sys.UseMockFilesystem([]string{"/dev/i2c-1"})
   265  	gobottest.Assert(t, a.Connect(), nil)
   266  	con, err := a.GetI2cConnection(0xff, 1)
   267  	gobottest.Assert(t, err, nil)
   268  	_, err = con.Write([]byte{0xbf})
   269  	gobottest.Assert(t, err, nil)
   270  	fs.WithCloseError = true
   271  	// act
   272  	err = a.Finalize()
   273  	// assert
   274  	gobottest.Assert(t, strings.Contains(err.Error(), "close error"), true)
   275  }
   276  
   277  func Test_validateSpiBusNumber(t *testing.T) {
   278  	var tests = map[string]struct {
   279  		busNr   int
   280  		wantErr error
   281  	}{
   282  		"number_negative_error": {
   283  			busNr:   -1,
   284  			wantErr: fmt.Errorf("Bus number -1 out of range"),
   285  		},
   286  		"number_0_ok": {
   287  			busNr: 0,
   288  		},
   289  		"number_1_ok": {
   290  			busNr: 1,
   291  		},
   292  		"number_2_error": {
   293  			busNr:   2,
   294  			wantErr: fmt.Errorf("Bus number 2 out of range"),
   295  		},
   296  	}
   297  	for name, tc := range tests {
   298  		t.Run(name, func(t *testing.T) {
   299  			// arrange
   300  			a := NewAdaptor()
   301  			// act
   302  			err := a.validateSpiBusNumber(tc.busNr)
   303  			// assert
   304  			gobottest.Assert(t, err, tc.wantErr)
   305  		})
   306  	}
   307  }
   308  
   309  func Test_validateI2cBusNumber(t *testing.T) {
   310  	var tests = map[string]struct {
   311  		busNr   int
   312  		wantErr error
   313  	}{
   314  		"number_negative_error": {
   315  			busNr:   -1,
   316  			wantErr: fmt.Errorf("Bus number -1 out of range"),
   317  		},
   318  		"number_0_ok": {
   319  			busNr: 0,
   320  		},
   321  		"number_1_ok": {
   322  			busNr: 1,
   323  		},
   324  		"number_2_not_ok": {
   325  			busNr:   2,
   326  			wantErr: fmt.Errorf("Bus number 2 out of range"),
   327  		},
   328  	}
   329  	for name, tc := range tests {
   330  		t.Run(name, func(t *testing.T) {
   331  			// arrange
   332  			a := NewAdaptor()
   333  			// act
   334  			err := a.validateI2cBusNumber(tc.busNr)
   335  			// assert
   336  			gobottest.Assert(t, err, tc.wantErr)
   337  		})
   338  	}
   339  }