github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/image/gif/reader_test.go (about) 1 package gif 2 3 import ( 4 "bytes" 5 "compress/lzw" 6 "image" 7 "image/color" 8 "reflect" 9 "testing" 10 ) 11 12 // header, palette and trailer are parts of a valid 2x1 GIF image. 13 const ( 14 headerStr = "GIF89a" + 15 "\x02\x00\x01\x00" + // width=2, height=1 16 "\x80\x00\x00" // headerFields=(a color map of 2 pixels), backgroundIndex, aspect 17 paletteStr = "\x10\x20\x30\x40\x50\x60" // the color map, also known as a palette 18 trailerStr = "\x3b" 19 ) 20 21 func TestDecode(t *testing.T) { 22 // lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes. 23 lzwEncode := func(n int) []byte { 24 b := &bytes.Buffer{} 25 w := lzw.NewWriter(b, lzw.LSB, 2) 26 w.Write(make([]byte, n)) 27 w.Close() 28 return b.Bytes() 29 } 30 31 testCases := []struct { 32 nPix int // The number of pixels in the image data. 33 extra bool // Whether to write an extra block after the LZW-encoded data. 34 wantErr error 35 }{ 36 {0, false, errNotEnough}, 37 {1, false, errNotEnough}, 38 {2, false, nil}, 39 {2, true, errTooMuch}, 40 {3, false, errTooMuch}, 41 } 42 for _, tc := range testCases { 43 b := &bytes.Buffer{} 44 b.WriteString(headerStr) 45 b.WriteString(paletteStr) 46 // Write an image with bounds 2x1 but tc.nPix pixels. If tc.nPix != 2 47 // then this should result in an invalid GIF image. First, write a 48 // magic 0x2c (image descriptor) byte, bounds=(0,0)-(2,1), a flags 49 // byte, and 2-bit LZW literals. 50 b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") 51 if tc.nPix > 0 { 52 enc := lzwEncode(tc.nPix) 53 if len(enc) > 0xff { 54 t.Errorf("nPix=%d, extra=%t: compressed length %d is too large", tc.nPix, tc.extra, len(enc)) 55 continue 56 } 57 b.WriteByte(byte(len(enc))) 58 b.Write(enc) 59 } 60 if tc.extra { 61 b.WriteString("\x01\x02") // A 1-byte payload with an 0x02 byte. 62 } 63 b.WriteByte(0x00) // An empty block signifies the end of the image data. 64 b.WriteString(trailerStr) 65 66 got, err := Decode(b) 67 if err != tc.wantErr { 68 t.Errorf("nPix=%d, extra=%t\ngot %v\nwant %v", tc.nPix, tc.extra, err, tc.wantErr) 69 } 70 71 if tc.wantErr != nil { 72 continue 73 } 74 want := &image.Paletted{ 75 Pix: []uint8{0, 0}, 76 Stride: 2, 77 Rect: image.Rect(0, 0, 2, 1), 78 Palette: color.Palette{ 79 color.RGBA{0x10, 0x20, 0x30, 0xff}, 80 color.RGBA{0x40, 0x50, 0x60, 0xff}, 81 }, 82 } 83 if !reflect.DeepEqual(got, want) { 84 t.Errorf("nPix=%d, extra=%t\ngot %v\nwant %v", tc.nPix, tc.extra, got, want) 85 } 86 } 87 } 88 89 // testGIF is a simple GIF that we can modify to test different scenarios. 90 var testGIF = []byte{ 91 'G', 'I', 'F', '8', '9', 'a', 92 1, 0, 1, 0, // w=1, h=1 (6) 93 128, 0, 0, // headerFields, bg, aspect (10) 94 0, 0, 0, 1, 1, 1, // color map and graphics control (13) 95 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00, // (19) 96 // frame 1 (0,0 - 1,1) 97 0x2c, 98 0x00, 0x00, 0x00, 0x00, 99 0x01, 0x00, 0x01, 0x00, // (32) 100 0x00, 101 0x02, 0x02, 0x4c, 0x01, 0x00, // lzw pixels 102 // trailer 103 0x3b, 104 } 105 106 func try(t *testing.T, b []byte, want string) { 107 _, err := DecodeAll(bytes.NewReader(b)) 108 var got string 109 if err != nil { 110 got = err.Error() 111 } 112 if got != want { 113 t.Fatalf("got %v, want %v", got, want) 114 } 115 } 116 117 func TestBounds(t *testing.T) { 118 // Make a local copy of testGIF. 119 gif := make([]byte, len(testGIF)) 120 copy(gif, testGIF) 121 // Make the bounds too big, just by one. 122 gif[32] = 2 123 want := "gif: frame bounds larger than image bounds" 124 try(t, gif, want) 125 126 // Make the bounds too small; does not trigger bounds 127 // check, but now there's too much data. 128 gif[32] = 0 129 want = "gif: too much image data" 130 try(t, gif, want) 131 gif[32] = 1 132 133 // Make the bounds really big, expect an error. 134 want = "gif: frame bounds larger than image bounds" 135 for i := 0; i < 4; i++ { 136 gif[32+i] = 0xff 137 } 138 try(t, gif, want) 139 } 140 141 func TestNoPalette(t *testing.T) { 142 b := &bytes.Buffer{} 143 144 // Manufacture a GIF with no palette, so any pixel at all 145 // will be invalid. 146 b.WriteString(headerStr[:len(headerStr)-3]) 147 b.WriteString("\x00\x00\x00") // No global palette. 148 149 // Image descriptor: 2x1, no local palette. 150 b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") 151 152 // Encode the pixels: neither is in range, because there is no palette. 153 pix := []byte{0, 128} 154 enc := &bytes.Buffer{} 155 w := lzw.NewWriter(enc, lzw.LSB, 2) 156 w.Write(pix) 157 w.Close() 158 b.WriteByte(byte(len(enc.Bytes()))) 159 b.Write(enc.Bytes()) 160 b.WriteByte(0x00) // An empty block signifies the end of the image data. 161 162 b.WriteString(trailerStr) 163 164 try(t, b.Bytes(), "gif: invalid pixel value") 165 } 166 167 func TestPixelOutsidePaletteRange(t *testing.T) { 168 for _, pval := range []byte{0, 1, 2, 3, 255} { 169 b := &bytes.Buffer{} 170 171 // Manufacture a GIF with a 2 color palette. 172 b.WriteString(headerStr) 173 b.WriteString(paletteStr) 174 175 // Image descriptor: 2x1, no local palette. 176 b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") 177 178 // Encode the pixels; some pvals trigger the expected error. 179 pix := []byte{pval, pval} 180 enc := &bytes.Buffer{} 181 w := lzw.NewWriter(enc, lzw.LSB, 2) 182 w.Write(pix) 183 w.Close() 184 b.WriteByte(byte(len(enc.Bytes()))) 185 b.Write(enc.Bytes()) 186 b.WriteByte(0x00) // An empty block signifies the end of the image data. 187 188 b.WriteString(trailerStr) 189 190 // No error expected, unless the pixels are beyond the 2 color palette. 191 want := "" 192 if pval >= 2 { 193 want = "gif: invalid pixel value" 194 } 195 try(t, b.Bytes(), want) 196 } 197 }