internal/format/exif_test.go

95 lines · 2552 bytes

 1package format
 2
 3import (
 4	"testing"
 5	"time"
 6)
 7
 8func TestParseExifFull(t *testing.T) {
 9	data := []byte(`[{
10		"SourceFile": "a.jpg",
11		"ImageWidth": 6240, "ImageHeight": 4160, "Orientation": 1,
12		"DateTimeOriginal": "2025:03:14 23:10:05",
13		"Make": "FUJIFILM", "Model": "X-T5", "LensModel": "XF23mmF2 R WR",
14		"FNumber": 2, "ExposureTime": 0.008, "ISO": 1600
15	}]`)
16	m, err := parseExif(data)
17	if err != nil {
18		t.Fatal(err)
19	}
20	want := Meta{
21		Width: 6240, Height: 4160,
22		Taken:    time.Date(2025, 3, 14, 23, 10, 5, 0, time.UTC),
23		Camera:   "FUJIFILM X-T5",
24		Lens:     "XF23mmF2 R WR",
25		Aperture: "f/2",
26		Shutter:  "1/125",
27		ISO:      1600,
28	}
29	if m != want {
30		t.Fatalf("got %+v\nwant %+v", m, want)
31	}
32}
33
34func TestParseExifRotatedSwapsDimensions(t *testing.T) {
35	for _, o := range []string{"5", "6", "7", "8"} {
36		m, err := parseExif([]byte(`[{"ImageWidth": 300, "ImageHeight": 200, "Orientation": ` + o + `}]`))
37		if err != nil {
38			t.Fatal(err)
39		}
40		if m.Width != 200 || m.Height != 300 {
41			t.Errorf("orientation %s: got %dx%d, want 200x300", o, m.Width, m.Height)
42		}
43	}
44}
45
46func TestParseExifFallbacks(t *testing.T) {
47	m, err := parseExif([]byte(`[{
48		"ImageWidth": 10, "ImageHeight": 10,
49		"CreateDate": "2024:01:02 03:04:05+01:00",
50		"Make": "Canon", "Model": "Canon EOS R6",
51		"FNumber": 5.6, "ExposureTime": 2
52	}]`))
53	if err != nil {
54		t.Fatal(err)
55	}
56	if want := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC); !m.Taken.Equal(want) {
57		t.Errorf("Taken = %v, want %v", m.Taken, want)
58	}
59	if m.Camera != "Canon EOS R6" {
60		t.Errorf("Camera = %q", m.Camera)
61	}
62	if m.Aperture != "f/5.6" || m.Shutter != "2s" {
63		t.Errorf("Aperture %q Shutter %q", m.Aperture, m.Shutter)
64	}
65}
66
67func TestParseExifMinimal(t *testing.T) {
68	m, err := parseExif([]byte(`[{"ImageWidth": 120, "ImageHeight": 80}]`))
69	if err != nil {
70		t.Fatal(err)
71	}
72	if (m != Meta{Width: 120, Height: 80}) {
73		t.Fatalf("got %+v", m)
74	}
75}
76
77func TestParseExifErrors(t *testing.T) {
78	for _, in := range []string{`not json`, `[]`, `[{}, {}]`, `[{"ImageWidth": 10}]`} {
79		if _, err := parseExif([]byte(in)); err == nil {
80			t.Errorf("%s: want error", in)
81		}
82	}
83}
84
85func TestParseExifRoundsAperture(t *testing.T) {
86	for in, want := range map[string]string{"1.779999971": "f/1.8", "2": "f/2", "5.6": "f/5.6", "11": "f/11", "22": "f/22", "0.95": "f/0.95"} {
87		m, err := parseExif([]byte(`[{"ImageWidth": 1, "ImageHeight": 1, "FNumber": ` + in + `}]`))
88		if err != nil {
89			t.Fatal(err)
90		}
91		if m.Aperture != want {
92			t.Errorf("FNumber %s: got %q, want %q", in, m.Aperture, want)
93		}
94	}
95}