gobot.io/x/gobot/v2@v2.1.0/platforms/tinkerboard/adaptor_test.go (about)

     1  package tinkerboard
     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  	gpio17Path  = "/sys/class/gpio/gpio17/"
    17  	gpio160Path = "/sys/class/gpio/gpio160/"
    18  )
    19  
    20  const (
    21  	pwmDir           = "/sys/devices/platform/ff680020.pwm/pwm/pwmchip2/"
    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  	gpio17Path + "value",
    44  	gpio17Path + "direction",
    45  	gpio160Path + "value",
    46  	gpio160Path + "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 := NewAdaptor()
    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 := NewAdaptor()
    77  	gobottest.Assert(t, strings.HasPrefix(a.Name(), "Tinker 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[gpio17Path+"value"].Contents, "1")
    88  
    89  	fs.Files[gpio160Path+"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("33", 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("33", 0)
   128  	gobottest.Assert(t, err, nil)
   129  
   130  	gobottest.Assert(t, fs.Files[pwmDutyCyclePath].Contents, "500000")
   131  
   132  	err = a.ServoWrite("33", 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("33", 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("33", 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("33", 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("33", 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("33", 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 := NewAdaptor()
   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 := NewAdaptor()
   214  	gobottest.Assert(t, a.DefaultI2cBus(), 1)
   215  }
   216  
   217  func TestI2cFinalizeWithErrors(t *testing.T) {
   218  	// arrange
   219  	a := NewAdaptor()
   220  	a.sys.UseMockSyscall()
   221  	fs := a.sys.UseMockFilesystem([]string{"/dev/i2c-4"})
   222  	gobottest.Assert(t, a.Connect(), nil)
   223  	con, err := a.GetI2cConnection(0xff, 4)
   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  		"number_2_ok": {
   251  			busNr: 2,
   252  		},
   253  		"number_3_error": {
   254  			busNr:   3,
   255  			wantErr: fmt.Errorf("Bus number 3 out of range"),
   256  		},
   257  	}
   258  	for name, tc := range tests {
   259  		t.Run(name, func(t *testing.T) {
   260  			// arrange
   261  			a := NewAdaptor()
   262  			// act
   263  			err := a.validateSpiBusNumber(tc.busNr)
   264  			// assert
   265  			gobottest.Assert(t, err, tc.wantErr)
   266  		})
   267  	}
   268  }
   269  
   270  func Test_validateI2cBusNumber(t *testing.T) {
   271  	var tests = map[string]struct {
   272  		busNr   int
   273  		wantErr error
   274  	}{
   275  		"number_negative_error": {
   276  			busNr:   -1,
   277  			wantErr: fmt.Errorf("Bus number -1 out of range"),
   278  		},
   279  		"number_0_ok": {
   280  			busNr: 0,
   281  		},
   282  		"number_1_ok": {
   283  			busNr: 1,
   284  		},
   285  		"number_2_ok": {
   286  			busNr: 2,
   287  		},
   288  		"number_3_ok": {
   289  			busNr: 3,
   290  		},
   291  		"number_4_ok": {
   292  			busNr: 4,
   293  		},
   294  		"number_5_error": {
   295  			busNr:   5,
   296  			wantErr: fmt.Errorf("Bus number 5 out of range"),
   297  		},
   298  	}
   299  	for name, tc := range tests {
   300  		t.Run(name, func(t *testing.T) {
   301  			// arrange
   302  			a := NewAdaptor()
   303  			// act
   304  			err := a.validateI2cBusNumber(tc.busNr)
   305  			// assert
   306  			gobottest.Assert(t, err, tc.wantErr)
   307  		})
   308  	}
   309  }
   310  
   311  func Test_translateDigitalPin(t *testing.T) {
   312  	var tests = map[string]struct {
   313  		access   string
   314  		pin      string
   315  		wantChip string
   316  		wantLine int
   317  		wantErr  error
   318  	}{
   319  		"cdev_ok": {
   320  			access:   "cdev",
   321  			pin:      "7",
   322  			wantChip: "gpiochip0",
   323  			wantLine: 17,
   324  		},
   325  		"sysfs_ok": {
   326  			access:   "sysfs",
   327  			pin:      "7",
   328  			wantChip: "",
   329  			wantLine: 17,
   330  		},
   331  		"unknown_pin": {
   332  			pin:      "99",
   333  			wantChip: "",
   334  			wantLine: -1,
   335  			wantErr:  fmt.Errorf("'99' is not a valid id for a digital pin"),
   336  		},
   337  	}
   338  	for name, tc := range tests {
   339  		t.Run(name, func(t *testing.T) {
   340  			// arrange
   341  			a := NewAdaptor()
   342  			a.sys.UseDigitalPinAccessWithMockFs(tc.access, []string{})
   343  			// act
   344  			chip, line, err := a.translateDigitalPin(tc.pin)
   345  			// assert
   346  			gobottest.Assert(t, err, tc.wantErr)
   347  			gobottest.Assert(t, chip, tc.wantChip)
   348  			gobottest.Assert(t, line, tc.wantLine)
   349  		})
   350  	}
   351  }
   352  
   353  func Test_translatePWMPin(t *testing.T) {
   354  	basePaths := []string{
   355  		"/sys/devices/platform/ff680020.pwm/pwm/",
   356  		"/sys/devices/platform/ff680030.pwm/pwm/",
   357  	}
   358  	var tests = map[string]struct {
   359  		pin         string
   360  		chip        string
   361  		wantDir     string
   362  		wantChannel int
   363  		wantErr     error
   364  	}{
   365  		"32_chip0": {
   366  			pin:         "32",
   367  			chip:        "pwmchip0",
   368  			wantDir:     "/sys/devices/platform/ff680030.pwm/pwm/pwmchip0",
   369  			wantChannel: 0,
   370  		},
   371  		"32_chip1": {
   372  			pin:         "32",
   373  			chip:        "pwmchip1",
   374  			wantDir:     "/sys/devices/platform/ff680030.pwm/pwm/pwmchip1",
   375  			wantChannel: 0,
   376  		},
   377  		"32_chip2": {
   378  			pin:         "32",
   379  			chip:        "pwmchip2",
   380  			wantDir:     "/sys/devices/platform/ff680030.pwm/pwm/pwmchip2",
   381  			wantChannel: 0,
   382  		},
   383  		"32_chip3": {
   384  			pin:         "32",
   385  			chip:        "pwmchip3",
   386  			wantDir:     "/sys/devices/platform/ff680030.pwm/pwm/pwmchip3",
   387  			wantChannel: 0,
   388  		},
   389  		"33_chip0": {
   390  			pin:         "33",
   391  			chip:        "pwmchip0",
   392  			wantDir:     "/sys/devices/platform/ff680020.pwm/pwm/pwmchip0",
   393  			wantChannel: 0,
   394  		},
   395  		"33_chip1": {
   396  			pin:         "33",
   397  			chip:        "pwmchip1",
   398  			wantDir:     "/sys/devices/platform/ff680020.pwm/pwm/pwmchip1",
   399  			wantChannel: 0,
   400  		},
   401  		"33_chip2": {
   402  			pin:         "33",
   403  			chip:        "pwmchip2",
   404  			wantDir:     "/sys/devices/platform/ff680020.pwm/pwm/pwmchip2",
   405  			wantChannel: 0,
   406  		},
   407  		"invalid_pin": {
   408  			pin:         "7",
   409  			wantDir:     "",
   410  			wantChannel: -1,
   411  			wantErr:     fmt.Errorf("'7' is not a valid id for a PWM pin"),
   412  		},
   413  	}
   414  	for name, tc := range tests {
   415  		t.Run(name, func(t *testing.T) {
   416  			// arrange
   417  			mockedPaths := []string{}
   418  			for _, base := range basePaths {
   419  				mockedPaths = append(mockedPaths, base+tc.chip+"/")
   420  			}
   421  			a, _ := initTestAdaptorWithMockedFilesystem(mockedPaths)
   422  			// act
   423  			dir, channel, err := a.translatePWMPin(tc.pin)
   424  			// assert
   425  			gobottest.Assert(t, err, tc.wantErr)
   426  			gobottest.Assert(t, dir, tc.wantDir)
   427  			gobottest.Assert(t, channel, tc.wantChannel)
   428  		})
   429  	}
   430  }