gobot.io/x/gobot@v1.16.0/platforms/intel-iot/edison/edison_adaptor_test.go (about)

     1  package edison
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  	"testing"
     7  
     8  	"gobot.io/x/gobot"
     9  	"gobot.io/x/gobot/drivers/aio"
    10  	"gobot.io/x/gobot/drivers/gpio"
    11  	"gobot.io/x/gobot/drivers/i2c"
    12  	"gobot.io/x/gobot/gobottest"
    13  	"gobot.io/x/gobot/sysfs"
    14  )
    15  
    16  // make sure that this Adaptor fullfills all the required interfaces
    17  var _ gobot.Adaptor = (*Adaptor)(nil)
    18  var _ gpio.DigitalReader = (*Adaptor)(nil)
    19  var _ gpio.DigitalWriter = (*Adaptor)(nil)
    20  var _ aio.AnalogReader = (*Adaptor)(nil)
    21  var _ gpio.PwmWriter = (*Adaptor)(nil)
    22  var _ sysfs.DigitalPinnerProvider = (*Adaptor)(nil)
    23  var _ sysfs.PWMPinnerProvider = (*Adaptor)(nil)
    24  var _ i2c.Connector = (*Adaptor)(nil)
    25  
    26  var testPinFiles = []string{
    27  	"/sys/bus/iio/devices/iio:device1/in_voltage0_raw",
    28  	"/sys/kernel/debug/gpio_debug/gpio111/current_pinmux",
    29  	"/sys/kernel/debug/gpio_debug/gpio115/current_pinmux",
    30  	"/sys/kernel/debug/gpio_debug/gpio114/current_pinmux",
    31  	"/sys/kernel/debug/gpio_debug/gpio109/current_pinmux",
    32  	"/sys/kernel/debug/gpio_debug/gpio131/current_pinmux",
    33  	"/sys/kernel/debug/gpio_debug/gpio129/current_pinmux",
    34  	"/sys/kernel/debug/gpio_debug/gpio40/current_pinmux",
    35  	"/sys/kernel/debug/gpio_debug/gpio13/current_pinmux",
    36  	"/sys/kernel/debug/gpio_debug/gpio28/current_pinmux",
    37  	"/sys/kernel/debug/gpio_debug/gpio27/current_pinmux",
    38  	"/sys/class/pwm/pwmchip0/export",
    39  	"/sys/class/pwm/pwmchip0/unexport",
    40  	"/sys/class/pwm/pwmchip0/pwm1/duty_cycle",
    41  	"/sys/class/pwm/pwmchip0/pwm1/period",
    42  	"/sys/class/pwm/pwmchip0/pwm1/enable",
    43  	"/sys/class/gpio/export",
    44  	"/sys/class/gpio/unexport",
    45  	"/sys/class/gpio/gpio13/value",
    46  	"/sys/class/gpio/gpio13/direction",
    47  	"/sys/class/gpio/gpio40/value",
    48  	"/sys/class/gpio/gpio40/direction",
    49  	"/sys/class/gpio/gpio128/value",
    50  	"/sys/class/gpio/gpio128/direction",
    51  	"/sys/class/gpio/gpio221/value",
    52  	"/sys/class/gpio/gpio221/direction",
    53  	"/sys/class/gpio/gpio243/value",
    54  	"/sys/class/gpio/gpio243/direction",
    55  	"/sys/class/gpio/gpio229/value",
    56  	"/sys/class/gpio/gpio229/direction",
    57  	"/sys/class/gpio/gpio253/value",
    58  	"/sys/class/gpio/gpio253/direction",
    59  	"/sys/class/gpio/gpio261/value",
    60  	"/sys/class/gpio/gpio261/direction",
    61  	"/sys/class/gpio/gpio214/value",
    62  	"/sys/class/gpio/gpio214/direction",
    63  	"/sys/class/gpio/gpio14/direction",
    64  	"/sys/class/gpio/gpio14/value",
    65  	"/sys/class/gpio/gpio165/direction",
    66  	"/sys/class/gpio/gpio165/value",
    67  	"/sys/class/gpio/gpio212/direction",
    68  	"/sys/class/gpio/gpio212/value",
    69  	"/sys/class/gpio/gpio213/direction",
    70  	"/sys/class/gpio/gpio213/value",
    71  	"/sys/class/gpio/gpio236/direction",
    72  	"/sys/class/gpio/gpio236/value",
    73  	"/sys/class/gpio/gpio237/direction",
    74  	"/sys/class/gpio/gpio237/value",
    75  	"/sys/class/gpio/gpio204/direction",
    76  	"/sys/class/gpio/gpio204/value",
    77  	"/sys/class/gpio/gpio205/direction",
    78  	"/sys/class/gpio/gpio205/value",
    79  	"/sys/class/gpio/gpio263/direction",
    80  	"/sys/class/gpio/gpio263/value",
    81  	"/sys/class/gpio/gpio262/direction",
    82  	"/sys/class/gpio/gpio262/value",
    83  	"/sys/class/gpio/gpio240/direction",
    84  	"/sys/class/gpio/gpio240/value",
    85  	"/sys/class/gpio/gpio241/direction",
    86  	"/sys/class/gpio/gpio241/value",
    87  	"/sys/class/gpio/gpio242/direction",
    88  	"/sys/class/gpio/gpio242/value",
    89  	"/sys/class/gpio/gpio218/direction",
    90  	"/sys/class/gpio/gpio218/value",
    91  	"/sys/class/gpio/gpio250/direction",
    92  	"/sys/class/gpio/gpio250/value",
    93  	"/dev/i2c-6",
    94  }
    95  
    96  func initTestAdaptor() (*Adaptor, *sysfs.MockFilesystem) {
    97  	a := NewAdaptor()
    98  	fs := sysfs.NewMockFilesystem(testPinFiles)
    99  	sysfs.SetFilesystem(fs)
   100  	fs.Files["/sys/class/pwm/pwmchip0/pwm1/period"].Contents = "5000"
   101  	a.Connect()
   102  	return a, fs
   103  }
   104  
   105  func TestEdisonAdaptorName(t *testing.T) {
   106  	a, _ := initTestAdaptor()
   107  	gobottest.Assert(t, strings.HasPrefix(a.Name(), "Edison"), true)
   108  	a.SetName("NewName")
   109  	gobottest.Assert(t, a.Name(), "NewName")
   110  }
   111  
   112  func TestAdaptorConnect(t *testing.T) {
   113  	a, _ := initTestAdaptor()
   114  	gobottest.Assert(t, a.Connect(), nil)
   115  	gobottest.Assert(t, a.GetDefaultBus(), 6)
   116  	gobottest.Assert(t, a.Board(), "arduino")
   117  
   118  	gobottest.Assert(t, a.Connect(), nil)
   119  }
   120  
   121  func TestAdaptorArduinoSetupFail263(t *testing.T) {
   122  	a, fs := initTestAdaptor()
   123  	delete(fs.Files, "/sys/class/gpio/gpio263/direction")
   124  
   125  	err := a.arduinoSetup()
   126  	gobottest.Assert(t, strings.Contains(err.Error(), "/sys/class/gpio/gpio263/direction: No such file"), true)
   127  }
   128  
   129  func TestAdaptorArduinoSetupFail240(t *testing.T) {
   130  	a, fs := initTestAdaptor()
   131  	delete(fs.Files, "/sys/class/gpio/gpio240/direction")
   132  
   133  	err := a.arduinoSetup()
   134  	gobottest.Assert(t, strings.Contains(err.Error(), "/sys/class/gpio/gpio240/direction: No such file"), true)
   135  }
   136  
   137  func TestAdaptorArduinoSetupFail111(t *testing.T) {
   138  	a, fs := initTestAdaptor()
   139  	delete(fs.Files, "/sys/kernel/debug/gpio_debug/gpio111/current_pinmux")
   140  
   141  	err := a.arduinoSetup()
   142  	gobottest.Assert(t, strings.Contains(err.Error(), "/sys/kernel/debug/gpio_debug/gpio111/current_pinmux: No such file"), true)
   143  }
   144  
   145  func TestAdaptorArduinoSetupFail131(t *testing.T) {
   146  	a, fs := initTestAdaptor()
   147  	delete(fs.Files, "/sys/kernel/debug/gpio_debug/gpio131/current_pinmux")
   148  
   149  	err := a.arduinoSetup()
   150  	gobottest.Assert(t, strings.Contains(err.Error(), "/sys/kernel/debug/gpio_debug/gpio131/current_pinmux: No such file"), true)
   151  }
   152  
   153  func TestAdaptorArduinoI2CSetupFailTristate(t *testing.T) {
   154  	a, fs := initTestAdaptor()
   155  
   156  	gobottest.Assert(t, a.arduinoSetup(), nil)
   157  
   158  	fs.WithWriteError = true
   159  	err := a.arduinoI2CSetup()
   160  	gobottest.Assert(t, err, errors.New("write error"))
   161  }
   162  
   163  func TestAdaptorArduinoI2CSetupFail14(t *testing.T) {
   164  	a, fs := initTestAdaptor()
   165  
   166  	gobottest.Assert(t, a.arduinoSetup(), nil)
   167  	delete(fs.Files, "/sys/class/gpio/gpio14/direction")
   168  
   169  	err := a.arduinoI2CSetup()
   170  	gobottest.Assert(t, strings.Contains(err.Error(), "/sys/class/gpio/gpio14/direction: No such file"), true)
   171  }
   172  
   173  func TestAdaptorArduinoI2CSetupUnexportFail(t *testing.T) {
   174  	a, fs := initTestAdaptor()
   175  
   176  	gobottest.Assert(t, a.arduinoSetup(), nil)
   177  	delete(fs.Files, "/sys/class/gpio/unexport")
   178  
   179  	err := a.arduinoI2CSetup()
   180  	gobottest.Assert(t, strings.Contains(err.Error(), "/sys/class/gpio/unexport: No such file"), true)
   181  }
   182  
   183  func TestAdaptorArduinoI2CSetupFail236(t *testing.T) {
   184  	a, fs := initTestAdaptor()
   185  
   186  	gobottest.Assert(t, a.arduinoSetup(), nil)
   187  	delete(fs.Files, "/sys/class/gpio/gpio236/direction")
   188  
   189  	err := a.arduinoI2CSetup()
   190  	gobottest.Assert(t, strings.Contains(err.Error(), "/sys/class/gpio/gpio236/direction: No such file"), true)
   191  }
   192  
   193  func TestAdaptorArduinoI2CSetupFail28(t *testing.T) {
   194  	a, fs := initTestAdaptor()
   195  
   196  	gobottest.Assert(t, a.arduinoSetup(), nil)
   197  	delete(fs.Files, "/sys/kernel/debug/gpio_debug/gpio28/current_pinmux")
   198  
   199  	err := a.arduinoI2CSetup()
   200  	gobottest.Assert(t, strings.Contains(err.Error(), "/sys/kernel/debug/gpio_debug/gpio28/current_pinmux: No such file"), true)
   201  }
   202  
   203  func TestAdaptorConnectArduinoError(t *testing.T) {
   204  	a, _ := initTestAdaptor()
   205  	a.writeFile = func(string, []byte) (int, error) {
   206  		return 0, errors.New("write error")
   207  	}
   208  
   209  	err := a.Connect()
   210  	gobottest.Assert(t, strings.Contains(err.Error(), "write error"), true)
   211  }
   212  
   213  func TestAdaptorConnectArduinoWriteError(t *testing.T) {
   214  	a, fs := initTestAdaptor()
   215  	fs.WithWriteError = true
   216  	err := a.Connect()
   217  	gobottest.Assert(t, strings.Contains(err.Error(), "write error"), true)
   218  }
   219  
   220  func TestAdaptorConnectSparkfun(t *testing.T) {
   221  	a, _ := initTestAdaptor()
   222  	a.SetBoard("sparkfun")
   223  	gobottest.Assert(t, a.Connect(), nil)
   224  	gobottest.Assert(t, a.GetDefaultBus(), 1)
   225  	gobottest.Assert(t, a.Board(), "sparkfun")
   226  }
   227  
   228  func TestAdaptorConnectMiniboard(t *testing.T) {
   229  	a, _ := initTestAdaptor()
   230  	a.SetBoard("miniboard")
   231  	gobottest.Assert(t, a.Connect(), nil)
   232  	gobottest.Assert(t, a.GetDefaultBus(), 1)
   233  	gobottest.Assert(t, a.Board(), "miniboard")
   234  }
   235  
   236  func TestAdaptorConnectUnknown(t *testing.T) {
   237  	a, _ := initTestAdaptor()
   238  	a.SetBoard("wha")
   239  	gobottest.Refute(t, a.Connect(), nil)
   240  }
   241  
   242  func TestAdaptorFinalize(t *testing.T) {
   243  	a, _ := initTestAdaptor()
   244  	a.DigitalWrite("3", 1)
   245  	a.PwmWrite("5", 100)
   246  
   247  	sysfs.SetSyscall(&sysfs.MockSyscall{})
   248  	a.GetConnection(0xff, 6)
   249  
   250  	gobottest.Assert(t, a.Finalize(), nil)
   251  
   252  	sysfs.SetFilesystem(sysfs.NewMockFilesystem([]string{}))
   253  	gobottest.Refute(t, a.Finalize(), nil)
   254  }
   255  
   256  func TestAdaptorFinalizeError(t *testing.T) {
   257  	a, fs := initTestAdaptor()
   258  	a.PwmWrite("5", 100)
   259  
   260  	fs.WithWriteError = true
   261  	gobottest.Refute(t, a.Finalize(), nil)
   262  }
   263  
   264  func TestAdaptorDigitalIO(t *testing.T) {
   265  	a, fs := initTestAdaptor()
   266  
   267  	a.DigitalWrite("13", 1)
   268  	gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio40/value"].Contents, "1")
   269  
   270  	a.DigitalWrite("2", 0)
   271  	i, err := a.DigitalRead("2")
   272  	gobottest.Assert(t, err, nil)
   273  	gobottest.Assert(t, i, 0)
   274  }
   275  
   276  func TestAdaptorDigitalPinInFileError(t *testing.T) {
   277  	a := NewAdaptor()
   278  	fs := sysfs.NewMockFilesystem([]string{
   279  		"/sys/kernel/debug/gpio_debug/gpio40/current_pinmux",
   280  		"/sys/class/gpio/export",
   281  		"/sys/class/gpio/unexport",
   282  		// "/sys/class/gpio/gpio40/value",
   283  		// "/sys/class/gpio/gpio40/direction",
   284  		"/sys/class/gpio/gpio229/value", // resistor
   285  		"/sys/class/gpio/gpio229/direction",
   286  		"/sys/class/gpio/gpio243/value",
   287  		"/sys/class/gpio/gpio243/direction",
   288  		"/sys/class/gpio/gpio261/value", // level shifter
   289  		"/sys/class/gpio/gpio261/direction",
   290  	})
   291  	sysfs.SetFilesystem(fs)
   292  
   293  	a.Connect()
   294  
   295  	_, err := a.DigitalPin("13", "in")
   296  	gobottest.Assert(t, strings.Contains(err.Error(), "No such file"), true)
   297  
   298  }
   299  
   300  func TestAdaptorDigitalPinInResistorFileError(t *testing.T) {
   301  	a := NewAdaptor()
   302  	fs := sysfs.NewMockFilesystem([]string{
   303  		"/sys/kernel/debug/gpio_debug/gpio40/current_pinmux",
   304  		"/sys/class/gpio/export",
   305  		"/sys/class/gpio/unexport",
   306  		"/sys/class/gpio/gpio40/value",
   307  		"/sys/class/gpio/gpio40/direction",
   308  		// "/sys/class/gpio/gpio229/value", // resistor
   309  		// "/sys/class/gpio/gpio229/direction",
   310  		"/sys/class/gpio/gpio243/value",
   311  		"/sys/class/gpio/gpio243/direction",
   312  		"/sys/class/gpio/gpio261/value", // level shifter
   313  		"/sys/class/gpio/gpio261/direction",
   314  	})
   315  	sysfs.SetFilesystem(fs)
   316  
   317  	a.Connect()
   318  
   319  	_, err := a.DigitalPin("13", "in")
   320  	gobottest.Assert(t, strings.Contains(err.Error(), "No such file"), true)
   321  }
   322  
   323  func TestAdaptorDigitalPinInLevelShifterFileError(t *testing.T) {
   324  	a := NewAdaptor()
   325  	fs := sysfs.NewMockFilesystem([]string{
   326  		"/sys/kernel/debug/gpio_debug/gpio40/current_pinmux",
   327  		"/sys/class/gpio/export",
   328  		"/sys/class/gpio/unexport",
   329  		"/sys/class/gpio/gpio40/value",
   330  		"/sys/class/gpio/gpio40/direction",
   331  		"/sys/class/gpio/gpio229/value", // resistor
   332  		"/sys/class/gpio/gpio229/direction",
   333  		"/sys/class/gpio/gpio243/value",
   334  		"/sys/class/gpio/gpio243/direction",
   335  		// "/sys/class/gpio/gpio261/value", // level shifter
   336  		// "/sys/class/gpio/gpio261/direction",
   337  	})
   338  	sysfs.SetFilesystem(fs)
   339  
   340  	a.Connect()
   341  
   342  	_, err := a.DigitalPin("13", "in")
   343  	gobottest.Assert(t, strings.Contains(err.Error(), "No such file"), true)
   344  }
   345  
   346  func TestAdaptorDigitalPinInMuxFileError(t *testing.T) {
   347  	a := NewAdaptor()
   348  	fs := sysfs.NewMockFilesystem([]string{
   349  		"/sys/kernel/debug/gpio_debug/gpio40/current_pinmux",
   350  		"/sys/class/gpio/export",
   351  		"/sys/class/gpio/unexport",
   352  		"/sys/class/gpio/gpio40/value",
   353  		"/sys/class/gpio/gpio40/direction",
   354  		"/sys/class/gpio/gpio229/value", // resistor
   355  		"/sys/class/gpio/gpio229/direction",
   356  		// "/sys/class/gpio/gpio243/value",
   357  		// "/sys/class/gpio/gpio243/direction",
   358  		"/sys/class/gpio/gpio261/value", // level shifter
   359  		"/sys/class/gpio/gpio261/direction",
   360  	})
   361  	sysfs.SetFilesystem(fs)
   362  
   363  	a.Connect()
   364  
   365  	_, err := a.DigitalPin("13", "in")
   366  	gobottest.Assert(t, strings.Contains(err.Error(), "No such file"), true)
   367  }
   368  
   369  func TestAdaptorDigitalWriteError(t *testing.T) {
   370  	a, fs := initTestAdaptor()
   371  	fs.WithWriteError = true
   372  
   373  	err := a.DigitalWrite("13", 1)
   374  	gobottest.Assert(t, err, errors.New("write error"))
   375  }
   376  
   377  func TestAdaptorDigitalReadWriteError(t *testing.T) {
   378  	a, fs := initTestAdaptor()
   379  	fs.WithWriteError = true
   380  
   381  	_, err := a.DigitalRead("13")
   382  	gobottest.Assert(t, err, errors.New("write error"))
   383  }
   384  
   385  func TestAdaptorI2c(t *testing.T) {
   386  	a, _ := initTestAdaptor()
   387  
   388  	sysfs.SetSyscall(&sysfs.MockSyscall{})
   389  	con, err := a.GetConnection(0xff, 6)
   390  	gobottest.Assert(t, err, nil)
   391  
   392  	con.Write([]byte{0x00, 0x01})
   393  	data := []byte{42, 42}
   394  	con.Read(data)
   395  	gobottest.Assert(t, data, []byte{0x00, 0x01})
   396  
   397  	gobottest.Assert(t, a.Finalize(), nil)
   398  }
   399  
   400  func TestAdaptorI2cInvalidBus(t *testing.T) {
   401  	a, _ := initTestAdaptor()
   402  	_, err := a.GetConnection(0xff, 3)
   403  	gobottest.Assert(t, err, errors.New("Unsupported I2C bus"))
   404  }
   405  
   406  func TestAdaptorPwm(t *testing.T) {
   407  	a, fs := initTestAdaptor()
   408  
   409  	err := a.PwmWrite("5", 100)
   410  	gobottest.Assert(t, err, nil)
   411  	gobottest.Assert(t, fs.Files["/sys/class/pwm/pwmchip0/pwm1/duty_cycle"].Contents, "1960")
   412  
   413  	err = a.PwmWrite("7", 100)
   414  	gobottest.Assert(t, err, errors.New("Not a PWM pin"))
   415  }
   416  
   417  func TestAdaptorPwmExportError(t *testing.T) {
   418  	a := NewAdaptor()
   419  	fs := sysfs.NewMockFilesystem([]string{
   420  		"/sys/kernel/debug/gpio_debug/gpio13/current_pinmux",
   421  		"/sys/class/gpio/export",
   422  		"/sys/class/gpio/gpio13/direction",
   423  		"/sys/class/gpio/gpio13/value",
   424  		"/sys/class/gpio/gpio221/direction",
   425  		"/sys/class/gpio/gpio221/value",
   426  		"/sys/class/gpio/gpio253/direction",
   427  		"/sys/class/gpio/gpio253/value",
   428  		//"/sys/class/pwm/pwmchip0/export",
   429  		"/sys/class/pwm/pwmchip0/unexport",
   430  		"/sys/class/pwm/pwmchip0/pwm1/duty_cycle",
   431  		"/sys/class/pwm/pwmchip0/pwm1/period",
   432  		"/sys/class/pwm/pwmchip0/pwm1/enable",
   433  	})
   434  	sysfs.SetFilesystem(fs)
   435  	a.Connect()
   436  
   437  	err := a.PwmWrite("5", 100)
   438  	gobottest.Assert(t, strings.Contains(err.Error(), "/sys/class/pwm/pwmchip0/export: No such file"), true)
   439  }
   440  
   441  func TestAdaptorPwmEnableError(t *testing.T) {
   442  	a := NewAdaptor()
   443  	fs := sysfs.NewMockFilesystem([]string{
   444  		"/sys/kernel/debug/gpio_debug/gpio13/current_pinmux",
   445  		"/sys/class/gpio/export",
   446  		"/sys/class/gpio/gpio13/direction",
   447  		"/sys/class/gpio/gpio13/value",
   448  		"/sys/class/gpio/gpio221/direction",
   449  		"/sys/class/gpio/gpio221/value",
   450  		"/sys/class/gpio/gpio253/direction",
   451  		"/sys/class/gpio/gpio253/value",
   452  		"/sys/class/pwm/pwmchip0/export",
   453  		"/sys/class/pwm/pwmchip0/unexport",
   454  		"/sys/class/pwm/pwmchip0/pwm1/duty_cycle",
   455  		"/sys/class/pwm/pwmchip0/pwm1/period",
   456  		//"/sys/class/pwm/pwmchip0/pwm1/enable",
   457  	})
   458  	sysfs.SetFilesystem(fs)
   459  	a.Connect()
   460  
   461  	err := a.PwmWrite("5", 100)
   462  	gobottest.Assert(t, strings.Contains(err.Error(), "/sys/class/pwm/pwmchip0/pwm1/enable: No such file"), true)
   463  }
   464  
   465  func TestAdaptorPwmWritePinError(t *testing.T) {
   466  	a, _ := initTestAdaptor()
   467  
   468  	a.writeFile = func(string, []byte) (int, error) {
   469  		return 0, errors.New("write error")
   470  	}
   471  
   472  	err := a.PwmWrite("5", 100)
   473  	gobottest.Assert(t, err, errors.New("write error"))
   474  }
   475  
   476  func TestAdaptorPwmWriteError(t *testing.T) {
   477  	a, fs := initTestAdaptor()
   478  
   479  	fs.WithWriteError = true
   480  
   481  	err := a.PwmWrite("5", 100)
   482  	gobottest.Assert(t, err, errors.New("write error"))
   483  }
   484  
   485  func TestAdaptorPwmReadError(t *testing.T) {
   486  	a, fs := initTestAdaptor()
   487  
   488  	fs.WithReadError = true
   489  
   490  	err := a.PwmWrite("5", 100)
   491  	gobottest.Assert(t, err, errors.New("read error"))
   492  }
   493  
   494  func TestAdaptorAnalog(t *testing.T) {
   495  	a, fs := initTestAdaptor()
   496  
   497  	fs.Files["/sys/bus/iio/devices/iio:device1/in_voltage0_raw"].Contents = "1000\n"
   498  	i, _ := a.AnalogRead("0")
   499  	gobottest.Assert(t, i, 250)
   500  }
   501  
   502  func TestAdaptorAnalogError(t *testing.T) {
   503  	a, _ := initTestAdaptor()
   504  
   505  	a.readFile = func(string) ([]byte, error) {
   506  		return nil, errors.New("read error")
   507  	}
   508  	_, err := a.AnalogRead("0")
   509  	gobottest.Assert(t, err, errors.New("read error"))
   510  }