github.com/simpleiot/simpleiot@v0.18.3/client/cobs-wrapper_test.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "fmt" 6 "reflect" 7 "testing" 8 "time" 9 10 "github.com/dim13/cobs" 11 "github.com/simpleiot/simpleiot/test" 12 ) 13 14 func TestCobs(t *testing.T) { 15 // we expect COBS encoding to work as detailed here: 16 // https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing 17 testCases := []struct { 18 dec, enc []byte 19 }{ 20 {[]byte{0x0}, []byte{0x1, 0x1, 0x0}}, 21 {[]byte{0x0, 0x0}, []byte{0x1, 0x1, 0x1, 0x0}}, 22 {[]byte{0x0, 0x11, 0x0}, []byte{0x1, 0x2, 0x11, 0x1, 0x0}}, 23 {[]byte{0x11, 0x22, 0x00, 0x33}, []byte{0x3, 0x11, 0x22, 0x2, 0x33, 0x00}}, 24 {[]byte{0x11, 0x22, 0x33, 0x44}, []byte{0x5, 0x11, 0x22, 0x33, 0x44, 0x00}}, 25 {[]byte{0x11, 0x00, 0x00, 0x00}, []byte{0x2, 0x11, 0x1, 0x1, 0x1, 0x00}}, 26 } 27 28 for _, tc := range testCases { 29 e := cobs.Encode(tc.dec) 30 31 fmt.Printf("Encoding %v -> %v\n", test.HexDump(tc.dec), test.HexDump(e)) 32 33 if !bytes.Equal(tc.enc, e) { 34 t.Fatalf("enc failed for %v, got: %v, exp: %v", 35 test.HexDump(tc.dec), test.HexDump(e), test.HexDump(tc.enc)) 36 } 37 38 c, err := cobsDecodeInplace(e) 39 40 if err != nil { 41 t.Fatal("Error decoding: ", err) 42 } 43 44 e = e[:c] 45 46 if !bytes.Equal(tc.dec, e) { 47 t.Fatalf("Decode failed: %v -> %v", 48 test.HexDump(tc.dec), test.HexDump(e)) 49 } 50 } 51 } 52 53 func TestCobsLong(t *testing.T) { 54 b := make([]byte, 300) 55 for i := range b { 56 b[i] = 5 57 } 58 59 e := cobs.Encode(b) 60 61 c, err := cobsDecodeInplace(e) 62 if err != nil { 63 t.Fatal("Error decoding: ", err) 64 } 65 66 e = e[:c] 67 68 if !bytes.Equal(b, e) { 69 t.Fatalf("Decode failed: %v", test.HexDump(e)) 70 } 71 } 72 73 func TestCobsRead(t *testing.T) { 74 d := []byte{1, 2, 3, 0, 4, 5, 6} 75 76 a, b := test.NewIoSim() 77 78 cw := NewCobsWrapper(b, 500) 79 80 _, _ = a.Write(append([]byte{0}, cobs.Encode(d)...)) 81 82 buf := make([]byte, 500) 83 84 chBuf := make(chan struct{}) 85 86 go func() { 87 c, err := cw.Read(buf) 88 if err != nil { 89 fmt.Println("Error reading cw: ", err) 90 } 91 buf = buf[0:c] 92 chBuf <- struct{}{} 93 }() 94 95 select { 96 case <-chBuf: 97 // all is well 98 case <-time.After(time.Second): 99 t.Fatal("Timeout waiting for data") 100 } 101 102 if !reflect.DeepEqual(buf, d) { 103 t.Fatal("Read data does not match") 104 } 105 } 106 107 func TestCobsReadNoLeadingNull(t *testing.T) { 108 d := []byte{1, 2, 3, 0, 4, 5, 6} 109 110 a, b := test.NewIoSim() 111 112 cw := NewCobsWrapper(b, 500) 113 114 _, _ = a.Write(cobs.Encode(d)) 115 116 buf := make([]byte, 500) 117 118 chBuf := make(chan struct{}) 119 120 go func() { 121 c, err := cw.Read(buf) 122 if err != nil { 123 fmt.Println("Error reading cw: ", err) 124 } 125 buf = buf[0:c] 126 chBuf <- struct{}{} 127 }() 128 129 select { 130 case <-chBuf: 131 // all is well 132 case <-time.After(time.Second): 133 t.Fatal("Timeout waiting for data") 134 } 135 136 if !reflect.DeepEqual(buf, d) { 137 t.Fatal("Read data does not match") 138 } 139 } 140 141 func TestCobsWrite(t *testing.T) { 142 d := []byte{1, 2, 3, 0, 4, 5, 6} 143 144 a, b := test.NewIoSim() 145 146 cw := NewCobsWrapper(b, 500) 147 148 _, err := cw.Write(d) 149 if err != nil { 150 t.Fatal("Error write: ", err) 151 } 152 153 buf := make([]byte, 500) 154 155 chBuf := make(chan struct{}) 156 157 go func() { 158 c, err := a.Read(buf) 159 if err != nil { 160 fmt.Println("Error reading cw: ", err) 161 } 162 buf = buf[0:c] 163 chBuf <- struct{}{} 164 }() 165 166 select { 167 case <-chBuf: 168 // all is well 169 case <-time.After(time.Second): 170 t.Fatal("Timeout waiting for data") 171 } 172 173 if buf[0] != 0 { 174 t.Fatal("COBS encoded packet must start with 0") 175 } 176 177 if !reflect.DeepEqual(cobs.Decode(buf[1:]), d) { 178 t.Fatal("cw.Write, buf is not same") 179 } 180 } 181 182 func TestCobsWrapperPartialPacket(t *testing.T) { 183 d := []byte{1, 2, 3, 0, 4, 5, 6} 184 185 a, b := test.NewIoSim() 186 187 cw := NewCobsWrapper(b, 500) 188 189 de := cobs.Encode(d) 190 191 // write part of packet 192 _, _ = a.Write(de[0:4]) 193 194 // start reader 195 readData := make(chan []byte) 196 errCh := make(chan error) 197 198 go func() { 199 buf := make([]byte, 500) 200 c, err := cw.Read(buf) 201 if err != nil { 202 errCh <- err 203 } 204 buf = buf[0:c] 205 readData <- buf 206 }() 207 208 // should time out as we don't have entire packet to decode yet 209 select { 210 case <-readData: 211 t.Fatal("should not have read data yet") 212 case err := <-errCh: 213 t.Fatal("Read failed when it should have blocked: ", err) 214 case <-time.After(time.Millisecond * 10): 215 // all is well 216 } 217 218 // write the rest of the packet 219 _, _ = a.Write(de[4:]) 220 221 // now look for packet 222 select { 223 case ret := <-readData: 224 if !reflect.DeepEqual(ret, d) { 225 t.Fatal("Read data does not match") 226 } 227 case err := <-errCh: 228 t.Fatal("Read failed: ", err) 229 case <-time.After(time.Millisecond * 10): 230 t.Fatal("Timeout reading packet") 231 } 232 } 233 234 func TestCobsMultipleLeadingNull(t *testing.T) { 235 d := []byte{1, 2, 3, 0, 4, 5, 6} 236 a, b := test.NewIoSim() 237 238 cw := NewCobsWrapper(b, 500) 239 240 _, _ = a.Write(append([]byte{0, 0, 0, 0}, cobs.Encode(d)...)) 241 242 buf := make([]byte, 500) 243 244 chBuf := make(chan struct{}) 245 246 go func() { 247 c, err := cw.Read(buf) 248 if err != nil { 249 fmt.Println("Error reading cw: ", err) 250 } 251 buf = buf[0:c] 252 chBuf <- struct{}{} 253 }() 254 255 select { 256 case <-chBuf: 257 // all is well 258 case <-time.After(time.Second): 259 t.Fatal("Timeout waiting for data") 260 } 261 262 if !reflect.DeepEqual(buf, d) { 263 t.Fatal("Read data does not match") 264 } 265 } 266 267 func TestCobsPartialThenNew(t *testing.T) { 268 d := []byte{1, 2, 3, 0, 4, 5, 6} 269 a, b := test.NewIoSim() 270 271 cw := NewCobsWrapper(b, 500) 272 273 de := append([]byte{0}, cobs.Encode(d)...) 274 275 // write partial packet 276 _, _ = a.Write(de[0:4]) 277 278 // then start new packet 279 _, _ = a.Write(de) 280 281 buf := make([]byte, 500) 282 c, err := cw.Read(buf) 283 if err == nil { 284 dump := buf[:c] 285 t.Fatal("should have gotten an error reading partial packet, data: ", test.HexDump(dump)) 286 } 287 288 c, err = cw.Read(buf) 289 if err != nil { 290 t.Fatal("got error reading full packet: ", err) 291 } 292 buf = buf[0:c] 293 294 if !reflect.DeepEqual(buf, d) { 295 t.Fatalf("Read data does not match, exp: %v, got: %v", test.HexDump(d), test.HexDump(buf)) 296 } 297 } 298 299 func TestCobsWriteTwoThenRead(t *testing.T) { 300 d := []byte{1, 2, 3, 0, 4, 5, 6} 301 a, b := test.NewIoSim() 302 303 cw := NewCobsWrapper(b, 500) 304 305 de := cobs.Encode(d) 306 307 // write two packets 308 _, _ = a.Write(append(de, de...)) 309 310 for i := 2; i < 2; i++ { 311 buf := make([]byte, 500) 312 c, err := cw.Read(buf) 313 if err != nil { 314 t.Fatal("got error reading full packet: ", i, err) 315 } 316 buf = buf[0:c] 317 318 if !reflect.DeepEqual(buf, d) { 319 t.Fatal("Read data does not match, iter: ", i) 320 } 321 } 322 }