gobot.io/x/gobot/v2@v2.1.0/system/digitalpin_gpiod_test.go (about) 1 package system 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "gobot.io/x/gobot/v2" 9 "gobot.io/x/gobot/v2/gobottest" 10 ) 11 12 var _ gobot.DigitalPinner = (*digitalPinGpiod)(nil) 13 var _ gobot.DigitalPinValuer = (*digitalPinGpiod)(nil) 14 var _ gobot.DigitalPinOptioner = (*digitalPinGpiod)(nil) 15 var _ gobot.DigitalPinOptionApplier = (*digitalPinGpiod)(nil) 16 17 func Test_newDigitalPinGpiod(t *testing.T) { 18 // arrange 19 const ( 20 chip = "gpiochip0" 21 pin = 17 22 label = "gobotio17" 23 ) 24 // act 25 d := newDigitalPinGpiod(chip, pin) 26 // assert 27 gobottest.Refute(t, d, nil) 28 gobottest.Assert(t, d.chipName, chip) 29 gobottest.Assert(t, d.pin, pin) 30 gobottest.Assert(t, d.label, label) 31 gobottest.Assert(t, d.direction, IN) 32 gobottest.Assert(t, d.outInitialState, 0) 33 } 34 35 func Test_newDigitalPinGpiodWithOptions(t *testing.T) { 36 // This is a general test, that options are applied by using "newDigitalPinGpiod" with the WithPinLabel() option. 37 // All other configuration options will be tested in tests for "digitalPinConfig". 38 // 39 // arrange 40 const label = "my own label" 41 // act 42 dp := newDigitalPinGpiod("", 9, WithPinLabel(label)) 43 // assert 44 gobottest.Assert(t, dp.label, label) 45 } 46 47 func TestApplyOptions(t *testing.T) { 48 var tests = map[string]struct { 49 changed []bool 50 simErr error 51 wantReconfigured int 52 wantErr error 53 }{ 54 "both_changed": { 55 changed: []bool{true, true}, 56 wantReconfigured: 1, 57 }, 58 "first_changed": { 59 changed: []bool{true, false}, 60 wantReconfigured: 1, 61 }, 62 "second_changed": { 63 changed: []bool{false, true}, 64 wantReconfigured: 1, 65 }, 66 "none_changed": { 67 changed: []bool{false, false}, 68 simErr: fmt.Errorf("error not raised"), 69 wantReconfigured: 0, 70 }, 71 "error_on_change": { 72 changed: []bool{false, true}, 73 simErr: fmt.Errorf("error raised"), 74 wantReconfigured: 1, 75 wantErr: fmt.Errorf("error raised"), 76 }, 77 } 78 for name, tc := range tests { 79 t.Run(name, func(t *testing.T) { 80 // currently the gpiod.Chip has no interface for RequestLine(), 81 // so we can only test without trigger of real reconfigure 82 // arrange 83 orgReconf := digitalPinGpiodReconfigure 84 defer func() { digitalPinGpiodReconfigure = orgReconf }() 85 86 inputForced := true 87 reconfigured := 0 88 digitalPinGpiodReconfigure = func(d *digitalPinGpiod, forceInput bool) error { 89 inputForced = forceInput 90 reconfigured++ 91 return tc.simErr 92 } 93 d := &digitalPinGpiod{digitalPinConfig: &digitalPinConfig{direction: "in"}} 94 optionFunction1 := func(gobot.DigitalPinOptioner) bool { 95 d.digitalPinConfig.direction = "test" 96 return tc.changed[0] 97 } 98 optionFunction2 := func(gobot.DigitalPinOptioner) bool { 99 d.digitalPinConfig.drive = 15 100 return tc.changed[1] 101 } 102 // act 103 err := d.ApplyOptions(optionFunction1, optionFunction2) 104 // assert 105 gobottest.Assert(t, err, tc.wantErr) 106 gobottest.Assert(t, d.digitalPinConfig.direction, "test") 107 gobottest.Assert(t, d.digitalPinConfig.drive, 15) 108 gobottest.Assert(t, reconfigured, tc.wantReconfigured) 109 if reconfigured > 0 { 110 gobottest.Assert(t, inputForced, false) 111 } 112 }) 113 } 114 } 115 116 func TestExport(t *testing.T) { 117 var tests = map[string]struct { 118 simErr error 119 wantReconfigured int 120 wantErr error 121 }{ 122 "no_err": { 123 wantReconfigured: 1, 124 }, 125 "error": { 126 wantReconfigured: 1, 127 simErr: fmt.Errorf("reconfigure error"), 128 wantErr: fmt.Errorf("gpiod.Export(): reconfigure error"), 129 }, 130 } 131 for name, tc := range tests { 132 t.Run(name, func(t *testing.T) { 133 // currently the gpiod.Chip has no interface for RequestLine(), 134 // so we can only test without trigger of real reconfigure 135 // arrange 136 orgReconf := digitalPinGpiodReconfigure 137 defer func() { digitalPinGpiodReconfigure = orgReconf }() 138 139 inputForced := true 140 reconfigured := 0 141 digitalPinGpiodReconfigure = func(d *digitalPinGpiod, forceInput bool) error { 142 inputForced = forceInput 143 reconfigured++ 144 return tc.simErr 145 } 146 d := &digitalPinGpiod{} 147 // act 148 err := d.Export() 149 // assert 150 gobottest.Assert(t, err, tc.wantErr) 151 gobottest.Assert(t, inputForced, false) 152 gobottest.Assert(t, reconfigured, tc.wantReconfigured) 153 }) 154 } 155 } 156 157 func TestUnexport(t *testing.T) { 158 var tests = map[string]struct { 159 simNoLine bool 160 simReconfErr error 161 simCloseErr error 162 wantReconfigured int 163 wantErr error 164 }{ 165 "no_line_no_err": { 166 simNoLine: true, 167 wantReconfigured: 0, 168 }, 169 "no_line_with_err": { 170 simNoLine: true, 171 simReconfErr: fmt.Errorf("reconfigure error"), 172 wantReconfigured: 0, 173 }, 174 "no_err": { 175 wantReconfigured: 1, 176 }, 177 "error_reconfigure": { 178 wantReconfigured: 1, 179 simReconfErr: fmt.Errorf("reconfigure error"), 180 wantErr: fmt.Errorf("reconfigure error"), 181 }, 182 "error_close": { 183 wantReconfigured: 1, 184 simCloseErr: fmt.Errorf("close error"), 185 wantErr: fmt.Errorf("gpiod.Unexport()-line.Close(): close error"), 186 }, 187 } 188 for name, tc := range tests { 189 t.Run(name, func(t *testing.T) { 190 // currently the gpiod.Chip has no interface for RequestLine(), 191 // so we can only test without trigger of real reconfigure 192 // arrange 193 orgReconf := digitalPinGpiodReconfigure 194 defer func() { digitalPinGpiodReconfigure = orgReconf }() 195 196 inputForced := false 197 reconfigured := 0 198 digitalPinGpiodReconfigure = func(d *digitalPinGpiod, forceInput bool) error { 199 inputForced = forceInput 200 reconfigured++ 201 return tc.simReconfErr 202 } 203 dp := newDigitalPinGpiod("", 4) 204 if !tc.simNoLine { 205 dp.line = &lineMock{simCloseErr: tc.simCloseErr} 206 } 207 // act 208 err := dp.Unexport() 209 // assert 210 gobottest.Assert(t, err, tc.wantErr) 211 gobottest.Assert(t, reconfigured, tc.wantReconfigured) 212 if reconfigured > 0 { 213 gobottest.Assert(t, inputForced, true) 214 } 215 }) 216 } 217 } 218 219 func TestWrite(t *testing.T) { 220 var tests = map[string]struct { 221 val int 222 simErr error 223 want int 224 wantErr []string 225 }{ 226 "write_zero": { 227 val: 0, 228 want: 0, 229 }, 230 "write_one": { 231 val: 1, 232 want: 1, 233 }, 234 "write_minus_one": { 235 val: -1, 236 want: 0, 237 }, 238 "write_two": { 239 val: 2, 240 want: 1, 241 }, 242 "write_with_err": { 243 simErr: fmt.Errorf("a write err"), 244 wantErr: []string{"a write err", "gpiod.Write"}, 245 }, 246 } 247 for name, tc := range tests { 248 t.Run(name, func(t *testing.T) { 249 // arrange 250 dp := newDigitalPinGpiod("", 4) 251 lm := &lineMock{lastVal: 10, simSetErr: tc.simErr} 252 dp.line = lm 253 // act 254 err := dp.Write(tc.val) 255 // assert 256 if tc.wantErr != nil { 257 for _, want := range tc.wantErr { 258 gobottest.Assert(t, strings.Contains(err.Error(), want), true) 259 } 260 } else { 261 gobottest.Assert(t, err, nil) 262 } 263 gobottest.Assert(t, lm.lastVal, tc.want) 264 }) 265 } 266 } 267 268 func TestRead(t *testing.T) { 269 var tests = map[string]struct { 270 simVal int 271 simErr error 272 wantErr []string 273 }{ 274 "read_ok": { 275 simVal: 3, 276 }, 277 "write_with_err": { 278 simErr: fmt.Errorf("a read err"), 279 wantErr: []string{"a read err", "gpiod.Read"}, 280 }, 281 } 282 for name, tc := range tests { 283 t.Run(name, func(t *testing.T) { 284 // arrange 285 dp := newDigitalPinGpiod("", 4) 286 lm := &lineMock{lastVal: tc.simVal, simValueErr: tc.simErr} 287 dp.line = lm 288 // act 289 got, err := dp.Read() 290 // assert 291 if tc.wantErr != nil { 292 for _, want := range tc.wantErr { 293 gobottest.Assert(t, strings.Contains(err.Error(), want), true) 294 } 295 } else { 296 gobottest.Assert(t, err, nil) 297 } 298 gobottest.Assert(t, tc.simVal, got) 299 }) 300 } 301 } 302 303 type lineMock struct { 304 lastVal int 305 simSetErr error 306 simValueErr error 307 simCloseErr error 308 } 309 310 func (lm *lineMock) SetValue(value int) error { lm.lastVal = value; return lm.simSetErr } 311 func (lm *lineMock) Value() (int, error) { return lm.lastVal, lm.simValueErr } 312 func (lm *lineMock) Close() error { return lm.simCloseErr }