github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/resources/images/exif/exif_test.go (about) 1 // Copyright 2019 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package exif 15 16 import ( 17 "encoding/json" 18 "math/big" 19 "os" 20 "path/filepath" 21 "testing" 22 "time" 23 24 "github.com/gohugoio/hugo/htesting/hqt" 25 "github.com/google/go-cmp/cmp" 26 27 qt "github.com/frankban/quicktest" 28 ) 29 30 func TestExif(t *testing.T) { 31 c := qt.New(t) 32 f, err := os.Open(filepath.FromSlash("../../testdata/sunset.jpg")) 33 c.Assert(err, qt.IsNil) 34 defer f.Close() 35 36 d, err := NewDecoder(IncludeFields("Lens|Date")) 37 c.Assert(err, qt.IsNil) 38 x, err := d.Decode(f) 39 c.Assert(err, qt.IsNil) 40 c.Assert(x.Date.Format("2006-01-02"), qt.Equals, "2017-10-27") 41 42 // Malaga: https://goo.gl/taazZy 43 c.Assert(x.Lat, qt.Equals, float64(36.59744166666667)) 44 c.Assert(x.Long, qt.Equals, float64(-4.50846)) 45 46 v, found := x.Tags["LensModel"] 47 c.Assert(found, qt.Equals, true) 48 lensModel, ok := v.(string) 49 c.Assert(ok, qt.Equals, true) 50 c.Assert(lensModel, qt.Equals, "smc PENTAX-DA* 16-50mm F2.8 ED AL [IF] SDM") 51 52 v, found = x.Tags["DateTime"] 53 c.Assert(found, qt.Equals, true) 54 c.Assert(v, hqt.IsSameType, time.Time{}) 55 56 // Verify that it survives a round-trip to JSON and back. 57 data, err := json.Marshal(x) 58 c.Assert(err, qt.IsNil) 59 x2 := &ExifInfo{} 60 err = json.Unmarshal(data, x2) 61 62 c.Assert(x2, eq, x) 63 } 64 65 func TestExifPNG(t *testing.T) { 66 c := qt.New(t) 67 68 f, err := os.Open(filepath.FromSlash("../../testdata/gohugoio.png")) 69 c.Assert(err, qt.IsNil) 70 defer f.Close() 71 72 d, err := NewDecoder() 73 c.Assert(err, qt.IsNil) 74 _, err = d.Decode(f) 75 c.Assert(err, qt.Not(qt.IsNil)) 76 } 77 78 func TestIssue8079(t *testing.T) { 79 c := qt.New(t) 80 81 f, err := os.Open(filepath.FromSlash("../../testdata/iss8079.jpg")) 82 c.Assert(err, qt.IsNil) 83 defer f.Close() 84 85 d, err := NewDecoder() 86 c.Assert(err, qt.IsNil) 87 x, err := d.Decode(f) 88 c.Assert(err, qt.IsNil) 89 c.Assert(x.Tags["ImageDescription"], qt.Equals, "Città del Vaticano #nanoblock #vatican #vaticancity") 90 } 91 92 func TestNullString(t *testing.T) { 93 c := qt.New(t) 94 95 for _, test := range []struct { 96 in string 97 expect string 98 }{ 99 {"foo", "foo"}, 100 {"\x20", "\x20"}, 101 {"\xc4\x81", "\xc4\x81"}, // \u0101 102 {"\u0160", "\u0160"}, // non-breaking space 103 } { 104 res := nullString([]byte(test.in)) 105 c.Assert(res, qt.Equals, test.expect) 106 } 107 } 108 109 func BenchmarkDecodeExif(b *testing.B) { 110 c := qt.New(b) 111 f, err := os.Open(filepath.FromSlash("../../testdata/sunset.jpg")) 112 c.Assert(err, qt.IsNil) 113 defer f.Close() 114 115 d, err := NewDecoder() 116 c.Assert(err, qt.IsNil) 117 118 b.ResetTimer() 119 for i := 0; i < b.N; i++ { 120 _, err = d.Decode(f) 121 c.Assert(err, qt.IsNil) 122 f.Seek(0, 0) 123 } 124 } 125 126 var eq = qt.CmpEquals( 127 cmp.Comparer( 128 func(v1, v2 *big.Rat) bool { 129 return v1.RatString() == v2.RatString() 130 }, 131 ), 132 cmp.Comparer(func(v1, v2 time.Time) bool { 133 return v1.Unix() == v2.Unix() 134 }), 135 ) 136 137 func TestIssue10738(t *testing.T) { 138 139 c := qt.New(t) 140 141 testFunc := func(path, include string) any { 142 f, err := os.Open(filepath.FromSlash(path)) 143 c.Assert(err, qt.IsNil) 144 defer f.Close() 145 146 d, err := NewDecoder(IncludeFields(include)) 147 c.Assert(err, qt.IsNil) 148 x, err := d.Decode(f) 149 c.Assert(err, qt.IsNil) 150 151 // Verify that it survives a round-trip to JSON and back. 152 data, err := json.Marshal(x) 153 c.Assert(err, qt.IsNil) 154 x2 := &ExifInfo{} 155 err = json.Unmarshal(data, x2) 156 157 c.Assert(x2, eq, x) 158 159 v, found := x.Tags["ExposureTime"] 160 c.Assert(found, qt.Equals, true) 161 return v 162 } 163 164 type args struct { 165 path string // imagePath 166 include string // includeFields 167 } 168 169 type want struct { 170 vN int64 // numerator 171 vD int64 // denominator 172 } 173 174 type testCase struct { 175 name string 176 args args 177 want want 178 } 179 180 tests := []testCase{ 181 { 182 "canon_cr2_fraction", args{ 183 path: "../../testdata/issue10738/canon_cr2_fraction.jpg", 184 include: "Lens|Date|ExposureTime", 185 }, want{ 186 1, 187 500, 188 }, 189 }, 190 { 191 "canon_cr2_integer", args{ 192 path: "../../testdata/issue10738/canon_cr2_integer.jpg", 193 include: "Lens|Date|ExposureTime", 194 }, want{ 195 10, 196 0, 197 }, 198 }, 199 { 200 "dji_dng_fraction", args{ 201 path: "../../testdata/issue10738/dji_dng_fraction.jpg", 202 include: "Lens|Date|ExposureTime", 203 }, want{ 204 1, 205 4000, 206 }, 207 }, 208 { 209 "fuji_raf_fraction", args{ 210 path: "../../testdata/issue10738/fuji_raf_fraction.jpg", 211 include: "Lens|Date|ExposureTime", 212 }, want{ 213 1, 214 250, 215 }, 216 }, 217 { 218 "fuji_raf_integer", args{ 219 path: "../../testdata/issue10738/fuji_raf_integer.jpg", 220 include: "Lens|Date|ExposureTime", 221 }, want{ 222 1, 223 0, 224 }, 225 }, 226 { 227 "leica_dng_fraction", args{ 228 path: "../../testdata/issue10738/leica_dng_fraction.jpg", 229 include: "Lens|Date|ExposureTime", 230 }, want{ 231 1, 232 100, 233 }, 234 }, 235 { 236 "lumix_rw2_fraction", args{ 237 path: "../../testdata/issue10738/lumix_rw2_fraction.jpg", 238 include: "Lens|Date|ExposureTime", 239 }, want{ 240 1, 241 400, 242 }, 243 }, 244 { 245 "nikon_nef_d5600", args{ 246 path: "../../testdata/issue10738/nikon_nef_d5600.jpg", 247 include: "Lens|Date|ExposureTime", 248 }, want{ 249 1, 250 1000, 251 }, 252 }, 253 { 254 "nikon_nef_fraction", args{ 255 path: "../../testdata/issue10738/nikon_nef_fraction.jpg", 256 include: "Lens|Date|ExposureTime", 257 }, want{ 258 1, 259 640, 260 }, 261 }, 262 { 263 "nikon_nef_integer", args{ 264 path: "../../testdata/issue10738/nikon_nef_integer.jpg", 265 include: "Lens|Date|ExposureTime", 266 }, want{ 267 30, 268 0, 269 }, 270 }, 271 { 272 "nikon_nef_fraction_2", args{ 273 path: "../../testdata/issue10738/nikon_nef_fraction_2.jpg", 274 include: "Lens|Date|ExposureTime", 275 }, want{ 276 1, 277 6400, 278 }, 279 }, 280 { 281 "sony_arw_fraction", args{ 282 path: "../../testdata/issue10738/sony_arw_fraction.jpg", 283 include: "Lens|Date|ExposureTime", 284 }, want{ 285 1, 286 160, 287 }, 288 }, 289 { 290 "sony_arw_integer", args{ 291 path: "../../testdata/issue10738/sony_arw_integer.jpg", 292 include: "Lens|Date|ExposureTime", 293 }, want{ 294 4, 295 0, 296 }, 297 }, 298 } 299 300 for _, tt := range tests { 301 c.Run(tt.name, func(c *qt.C) { 302 got := testFunc(tt.args.path, tt.args.include) 303 switch got.(type) { 304 case float64: 305 eTime, ok := got.(float64) 306 c.Assert(ok, qt.Equals, true) 307 c.Assert(eTime, qt.Equals, float64(tt.want.vN)) 308 case *big.Rat: 309 eTime, ok := got.(*big.Rat) 310 c.Assert(ok, qt.Equals, true) 311 c.Assert(eTime, eq, big.NewRat(tt.want.vN, tt.want.vD)) 312 } 313 }) 314 } 315 }