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>
101 lines
2.2 KiB
Go
101 lines
2.2 KiB
Go
package dcf
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"testing"
|
|
)
|
|
|
|
// newMountPoint returns a temporary directory holding the given directories.
|
|
func newMountPoint(t *testing.T, directories ...string) string {
|
|
t.Helper()
|
|
|
|
root := t.TempDir()
|
|
|
|
for _, directory := range directories {
|
|
err := os.MkdirAll(filepath.Join(root, directory), 0o750)
|
|
if err != nil {
|
|
t.Fatalf("MkdirAll: %v", err)
|
|
}
|
|
}
|
|
|
|
return root
|
|
}
|
|
|
|
func TestIsDCFMountPoint(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := map[string]bool{
|
|
"DCIM": true,
|
|
"PRIVATE/M4ROOT": true,
|
|
"M4ROOT": false,
|
|
"PRIVATE": false,
|
|
}
|
|
|
|
for directory, want := range cases {
|
|
got := isDCFMountPoint(newMountPoint(t, directory))
|
|
if got != want {
|
|
t.Errorf("isDCFMountPoint with only %q = %v, want %v",
|
|
directory, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsDCFMountPointIgnoresPlainFile(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
root := t.TempDir()
|
|
|
|
err := os.WriteFile(filepath.Join(root, "DCIM"), nil, 0o600)
|
|
if err != nil {
|
|
t.Fatalf("WriteFile: %v", err)
|
|
}
|
|
|
|
if isDCFMountPoint(root) {
|
|
t.Fatal("isDCFMountPoint counted a plain file named DCIM as a directory")
|
|
}
|
|
}
|
|
|
|
func TestIsDCFMountPointIgnoresSymlink(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
outside := newMountPoint(t, "M4ROOT")
|
|
|
|
for _, link := range []string{dcimDirectory, privateDirectory, m4RootDirectory} {
|
|
root := newMountPoint(t, filepath.Dir(link))
|
|
addSymlink(t, root, link, outside)
|
|
|
|
if isDCFMountPoint(root) {
|
|
t.Errorf("isDCFMountPoint counted a symlink at %s as a directory", link)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFilterDCFMountPointsReturnsRequestedCount(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
first := newMountPoint(t, "DCIM")
|
|
second := newMountPoint(t, "PRIVATE/M4ROOT")
|
|
third := newMountPoint(t, "DCIM")
|
|
|
|
// This one holds no DCF store, so it must not use up the requested count.
|
|
mountpoints := []string{newMountPoint(t), first, second, third}
|
|
|
|
cases := map[int][]string{
|
|
0: {first, second, third},
|
|
1: {first},
|
|
2: {first, second},
|
|
3: {first, second, third},
|
|
4: {first, second, third},
|
|
}
|
|
|
|
for requestedCount, want := range cases {
|
|
got := filterDCFMountPoints(mountpoints, requestedCount)
|
|
if !slices.Equal(got, want) {
|
|
t.Errorf("filterDCFMountPoints(%d) = %v, want %v",
|
|
requestedCount, got, want)
|
|
}
|
|
}
|
|
}
|