A card is now read by the naming rules in CIPA DC-009-2010, not by looking for media extensions anywhere on the volume. Store.Directories holds the DCF directories and their objects, with directory and file numbers. Store.NonConforming lists everything else under DCIM, including symlinks and names fs.FS refuses. Media under PRIVATE/M4ROOT stays, with no DCF numbers. Media anywhere else is no longer part of the store. A symlink is never followed, and a read error names the card's mount point. Model: opus-5-5 Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
94 lines
2.3 KiB
Go
94 lines
2.3 KiB
Go
package dcf
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseStoresReadsEachMountPoint(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
first := newCardOnDisk(t, "DCIM/100ABCDE/ABCD0001.JPG")
|
|
second := newCardOnDisk(t, "PRIVATE/M4ROOT/CLIP/C0001.MP4")
|
|
|
|
stores, err := parseStores([]string{first, second})
|
|
if err != nil {
|
|
t.Fatalf("parseStores: %v", err)
|
|
}
|
|
|
|
if len(stores) != 2 {
|
|
t.Fatalf("parseStores returned %d stores, want 2", len(stores))
|
|
}
|
|
|
|
cases := map[string]Object{
|
|
filepath.Join(first, "DCIM/100ABCDE/ABCD0001.JPG"): stores[0].Images[0].Object,
|
|
filepath.Join(second, "PRIVATE/M4ROOT/CLIP/C0001.MP4"): stores[1].Videos[0].Object,
|
|
}
|
|
|
|
for want, object := range cases {
|
|
if got := object.FullFilePath(); got != want {
|
|
t.Errorf("FullFilePath() = %q, want %q", got, want)
|
|
}
|
|
|
|
got, err := object.Hash()
|
|
if err != nil {
|
|
t.Fatalf("Hash() returned error: %v", err)
|
|
}
|
|
|
|
// The published SHA-256 test vector for "abc".
|
|
wantHash := "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
|
if got != wantHash {
|
|
t.Errorf("Hash() = %q, want %q", got, wantHash)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseStoresReturnsErrorNamingTheCard(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Every lookup under a plain file fails.
|
|
mountpoint := filepath.Join(newCardOnDisk(t, "FILE"), "FILE")
|
|
|
|
_, err := parseStores([]string{mountpoint})
|
|
if err == nil || !strings.Contains(err.Error(), mountpoint) {
|
|
t.Fatalf("parseStores error = %v, want one naming %s", err, mountpoint)
|
|
}
|
|
}
|
|
|
|
func TestIsImageFile(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := map[string]bool{
|
|
"DCIM/100MSDCF/DSC00001.jpg": true,
|
|
"DCIM/100MSDCF/DSC00001.ARW": true,
|
|
"PRIVATE/M4ROOT/CLIP/C0001.MP4": false,
|
|
"DCIM/100MSDCF/notes.txt": false,
|
|
"DCIM/100MSDCF/noextension": false,
|
|
}
|
|
|
|
for path, want := range cases {
|
|
if got := isImageFile(path); got != want {
|
|
t.Errorf("isImageFile(%q) = %v, want %v", path, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsVideoFile(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := map[string]bool{
|
|
"PRIVATE/M4ROOT/CLIP/C0001.mp4": true,
|
|
"PRIVATE/M4ROOT/CLIP/C0001.MOV": true,
|
|
"DCIM/100MSDCF/DSC00001.JPG": false,
|
|
"DCIM/100MSDCF/notes.txt": false,
|
|
"DCIM/100MSDCF/noextension": false,
|
|
}
|
|
|
|
for path, want := range cases {
|
|
if got := isVideoFile(path); got != want {
|
|
t.Errorf("isVideoFile(%q) = %v, want %v", path, got, want)
|
|
}
|
|
}
|
|
}
|