gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/ccs811_driver_test.go (about)

     1  package i2c
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  	"testing"
     7  
     8  	"gobot.io/x/gobot/v2"
     9  	"gobot.io/x/gobot/v2/gobottest"
    10  )
    11  
    12  // this ensures that the implementation is based on i2c.Driver, which implements the gobot.Driver
    13  // and tests all implementations, so no further tests needed here for gobot.Driver interface
    14  var _ gobot.Driver = (*CCS811Driver)(nil)
    15  
    16  func initTestCCS811WithStubbedAdaptor() (*CCS811Driver, *i2cTestAdaptor) {
    17  	a := newI2cTestAdaptor()
    18  	return NewCCS811Driver(a), a
    19  }
    20  
    21  func TestNewCCS811Driver(t *testing.T) {
    22  	var di interface{} = NewCCS811Driver(newI2cTestAdaptor())
    23  	d, ok := di.(*CCS811Driver)
    24  	if !ok {
    25  		t.Errorf("NewCCS811Driver() should have returned a *CCS811Driver")
    26  	}
    27  	gobottest.Refute(t, d.Driver, nil)
    28  	gobottest.Assert(t, strings.HasPrefix(d.Name(), "CCS811"), true)
    29  	gobottest.Assert(t, d.defaultAddress, 0x5A)
    30  	gobottest.Refute(t, d.measMode, nil)
    31  	gobottest.Assert(t, d.ntcResistanceValue, uint32(100000))
    32  }
    33  
    34  func TestCCS811Options(t *testing.T) {
    35  	// This is a general test, that options are applied in constructor by using the common WithBus() option and
    36  	// least one of this driver. Further tests for options can also be done by call of "WithOption(val)(d)".
    37  	d := NewCCS811Driver(newI2cTestAdaptor(), WithBus(2), WithAddress(0xFF), WithCCS811NTCResistance(0xFF))
    38  	gobottest.Assert(t, d.GetBusOrDefault(1), 2)
    39  	gobottest.Assert(t, d.GetAddressOrDefault(0x5a), 0xFF)
    40  	gobottest.Assert(t, d.ntcResistanceValue, uint32(0xFF))
    41  }
    42  
    43  func TestCCS811WithCCS811MeasMode(t *testing.T) {
    44  	d := NewCCS811Driver(newI2cTestAdaptor(), WithCCS811MeasMode(CCS811DriveMode10Sec))
    45  	gobottest.Assert(t, d.measMode.driveMode, CCS811DriveMode(CCS811DriveMode10Sec))
    46  }
    47  
    48  func TestCCS811GetGasData(t *testing.T) {
    49  	var tests = map[string]struct {
    50  		readReturn func([]byte) (int, error)
    51  		eco2       uint16
    52  		tvoc       uint16
    53  		err        error
    54  	}{
    55  		"ideal values taken from the bus": {
    56  			readReturn: func(b []byte) (int, error) {
    57  				copy(b, []byte{1, 156, 0, 86})
    58  				return 4, nil
    59  			},
    60  			eco2: 412,
    61  			tvoc: 86,
    62  			err:  nil,
    63  		},
    64  		"max values possible taken from the bus": {
    65  			readReturn: func(b []byte) (int, error) {
    66  				copy(b, []byte{255, 255, 255, 255})
    67  				return 4, nil
    68  			},
    69  			eco2: 65535,
    70  			tvoc: 65535,
    71  			err:  nil,
    72  		},
    73  		"error when the i2c operation fails": {
    74  			readReturn: func(b []byte) (int, error) {
    75  				copy(b, []byte{255, 255, 255, 255})
    76  				return 4, errors.New("Error")
    77  			},
    78  			eco2: 0,
    79  			tvoc: 0,
    80  			err:  errors.New("Error"),
    81  		},
    82  	}
    83  	for name, tc := range tests {
    84  		t.Run(name, func(t *testing.T) {
    85  			// arrange
    86  			d, a := initTestCCS811WithStubbedAdaptor()
    87  			// Create stub function as it is needed by read submethod in driver code
    88  			a.i2cWriteImpl = func([]byte) (int, error) { return 0, nil }
    89  			d.Start()
    90  			a.i2cReadImpl = tc.readReturn
    91  			// act
    92  			eco2, tvoc, err := d.GetGasData()
    93  			// assert
    94  			gobottest.Assert(t, eco2, tc.eco2)
    95  			gobottest.Assert(t, tvoc, tc.tvoc)
    96  			gobottest.Assert(t, err, tc.err)
    97  		})
    98  	}
    99  }
   100  
   101  func TestCCS811GetTemperature(t *testing.T) {
   102  	var tests = map[string]struct {
   103  		readReturn func([]byte) (int, error)
   104  		temp       float32
   105  		err        error
   106  	}{
   107  		"ideal values taken from the bus": {
   108  			readReturn: func(b []byte) (int, error) {
   109  				copy(b, []byte{10, 197, 0, 248})
   110  				return 4, nil
   111  			},
   112  			temp: 27.811005,
   113  			err:  nil,
   114  		},
   115  		"without bus values overflowing": {
   116  			readReturn: func(b []byte) (int, error) {
   117  				copy(b, []byte{129, 197, 10, 248})
   118  				return 4, nil
   119  			},
   120  			temp: 29.48822,
   121  			err:  nil,
   122  		},
   123  		"negative temperature": {
   124  			readReturn: func(b []byte) (int, error) {
   125  				copy(b, []byte{255, 255, 255, 255})
   126  				return 4, nil
   127  			},
   128  			temp: -25.334152,
   129  			err:  nil,
   130  		},
   131  		"error if the i2c bus errors": {
   132  			readReturn: func(b []byte) (int, error) {
   133  				copy(b, []byte{129, 197, 0, 248})
   134  				return 4, errors.New("Error")
   135  			},
   136  			temp: 0,
   137  			err:  errors.New("Error"),
   138  		},
   139  	}
   140  	for name, tc := range tests {
   141  		t.Run(name, func(t *testing.T) {
   142  			// arrange
   143  			d, a := initTestCCS811WithStubbedAdaptor()
   144  			// Create stub function as it is needed by read submethod in driver code
   145  			a.i2cWriteImpl = func([]byte) (int, error) { return 0, nil }
   146  			d.Start()
   147  			a.i2cReadImpl = tc.readReturn
   148  			// act
   149  			temp, err := d.GetTemperature()
   150  			// assert
   151  			gobottest.Assert(t, temp, tc.temp)
   152  			gobottest.Assert(t, err, tc.err)
   153  		})
   154  	}
   155  }
   156  
   157  func TestCCS811HasData(t *testing.T) {
   158  	var tests = map[string]struct {
   159  		readReturn func([]byte) (int, error)
   160  		result     bool
   161  		err        error
   162  	}{
   163  		"true for HasError=0 and DataRdy=1": {
   164  			readReturn: func(b []byte) (int, error) {
   165  				copy(b, []byte{0x08})
   166  				return 1, nil
   167  			},
   168  			result: true,
   169  			err:    nil,
   170  		},
   171  		"false for HasError=1 and DataRdy=1": {
   172  			readReturn: func(b []byte) (int, error) {
   173  				copy(b, []byte{0x09})
   174  				return 1, nil
   175  			},
   176  			result: false,
   177  			err:    nil,
   178  		},
   179  		"false for HasError=1 and DataRdy=0": {
   180  			readReturn: func(b []byte) (int, error) {
   181  				copy(b, []byte{0x01})
   182  				return 1, nil
   183  			},
   184  			result: false,
   185  			err:    nil,
   186  		},
   187  		"false for HasError=0 and DataRdy=0": {
   188  			readReturn: func(b []byte) (int, error) {
   189  				copy(b, []byte{0x00})
   190  				return 1, nil
   191  			},
   192  			result: false,
   193  			err:    nil,
   194  		},
   195  		"error when the i2c read operation fails": {
   196  			readReturn: func(b []byte) (int, error) {
   197  				copy(b, []byte{0x00})
   198  				return 1, errors.New("Error")
   199  			},
   200  			result: false,
   201  			err:    errors.New("Error"),
   202  		},
   203  	}
   204  	for name, tc := range tests {
   205  		t.Run(name, func(t *testing.T) {
   206  			// arrange
   207  			d, a := initTestCCS811WithStubbedAdaptor()
   208  			// Create stub function as it is needed by read submethod in driver code
   209  			a.i2cWriteImpl = func([]byte) (int, error) { return 0, nil }
   210  			d.Start()
   211  			a.i2cReadImpl = tc.readReturn
   212  			// act
   213  			result, err := d.HasData()
   214  			// assert
   215  			gobottest.Assert(t, result, tc.result)
   216  			gobottest.Assert(t, err, tc.err)
   217  		})
   218  	}
   219  }
   220  
   221  func TestCCS811_initialize(t *testing.T) {
   222  	// sequence for initialization the device on Start()
   223  	// * write hardware ID register (0x20)
   224  	// * read the ID
   225  	// * prepare software reset register content: a sequence of four bytes must
   226  	//   be written to this register in a single I²C sequence: 0x11, 0xE5, 0x72, 0x8A
   227  	// * write software reset register content (0xFF)
   228  	// * write application start register (0xF4)
   229  	// * prepare measurement mode register content
   230  	//  * INT_THRESH = 0 (normal mode)
   231  	//  * INT_DATARDY = 0 (disable interrupt mode)
   232  	//  * DRIVE_MODE = 0x01 (constant power, value every 1 sec)
   233  	// * write measure mode register content (0x01)
   234  	//
   235  	// arrange
   236  	d, a := initTestCCS811WithStubbedAdaptor()
   237  	a.written = []byte{} // reset writes of former test
   238  	const (
   239  		wantChipIDReg    = uint8(0x20)
   240  		wantChipIDRegVal = uint8(0x20)
   241  		wantResetReg     = uint8(0xFF)
   242  		wantAppStartReg  = uint8(0xF4)
   243  		wantMeasReg      = uint8(0x01)
   244  		wantMeasRegVal   = uint8(0x10)
   245  	)
   246  	wantResetRegSequence := []byte{0x11, 0xE5, 0x72, 0x8A}
   247  	// arrange reads
   248  	numCallsRead := 0
   249  	a.i2cReadImpl = func(b []byte) (int, error) {
   250  		numCallsRead++
   251  		// chip ID
   252  		b[0] = 0x81
   253  		return len(b), nil
   254  	}
   255  	// arrange, act - initialize() must be called on Start()
   256  	err := d.Start()
   257  	// assert
   258  	gobottest.Assert(t, err, nil)
   259  	gobottest.Assert(t, numCallsRead, 1)
   260  	gobottest.Assert(t, len(a.written), 9)
   261  	gobottest.Assert(t, a.written[0], wantChipIDReg)
   262  	gobottest.Assert(t, a.written[1], wantResetReg)
   263  	gobottest.Assert(t, a.written[2:6], wantResetRegSequence)
   264  	gobottest.Assert(t, a.written[6], wantAppStartReg)
   265  	gobottest.Assert(t, a.written[7], wantMeasReg)
   266  	gobottest.Assert(t, a.written[8], wantMeasRegVal)
   267  }