Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 15 additions & 47 deletions tests/fixtures/prepare_sd_card_crossplatform.m
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@
% - Format option: Windows (auto), Mac (with confirmation prompt)
% - ValidateDriveName: checks 'PATSD' on both Windows (vol) and Mac (mount path)
% - On Mac, formatting clears the FAT table, ensuring reliable dirIndex order
% - macOS dot-files (._*) are automatically cleaned from FAT32 volumes
% (AppleDouble resource forks corrupt G4.1 controller dirIndex ordering)
% - On macOS, extended attributes are stripped (xattr -c) before copying
% to FAT32, preventing AppleDouble ._* files that corrupt dirIndex ordering
%
% See also: prepare_sd_card, deploy_experiments_to_sd

Expand Down Expand Up @@ -407,6 +407,17 @@
end
end

%% Strip extended attributes (prevents macOS AppleDouble ._* files on FAT32)
if ismac
for i = 1:num_patterns
src = fullfile(staging_dir, 'patterns', sprintf('pat%04d.pat', i));
system(sprintf('xattr -c "%s"', src));
end
system(sprintf('xattr -c "%s"', bin_path));
system(sprintf('xattr -c "%s"', txt_path));
fprintf(' ✓ Cleared extended attributes (prevents ._* files on FAT32)\n');
end

%% Copy patterns to SD card (FIRST - for correct dirIndex order)
fprintf(' Copying %d patterns...\n', num_patterns);
try
Expand All @@ -421,30 +432,6 @@
end
fprintf(' ✓ Copied %d patterns\n', num_patterns);

%% Clean up macOS resource fork files (._* files)
% macOS creates AppleDouble "._" files when copying to FAT32 volumes.
% These are invisible in Finder but occupy FAT32 directory entries,
% which shifts dirIndex ordering and causes the G4.1 controller to
% load wrong patterns. We must remove them before writing manifests.
if ismac
dot_files = dir(fullfile(target_dir, '._*'));
if ~isempty(dot_files)
fprintf(' Cleaning %d macOS resource fork files (._*)...\n', length(dot_files));
for i = 1:length(dot_files)
delete(fullfile(target_dir, dot_files(i).name));
end
fprintf(' ✓ Removed %d dot-files (prevents dirIndex corruption)\n', length(dot_files));
end

% Also clean root-level dot-files (in case manifests create them)
dot_files_root = dir(fullfile(sd_root, '._*'));
if ~isempty(dot_files_root)
for i = 1:length(dot_files_root)
delete(fullfile(sd_root, dot_files_root(i).name));
end
end
end

%% Copy manifest files to SD card (AFTER patterns for correct dirIndex)
try
copyfile(bin_path, fullfile(sd_root, 'MANIFEST.bin'));
Expand All @@ -454,29 +441,10 @@
return;
end
fprintf(' ✓ Copied manifest files\n');

%% Final macOS dot-file cleanup (manifests may have created new ones)
if ismac
% Clean patterns dir
dot_final = dir(fullfile(target_dir, '._*'));
for i = 1:length(dot_final)
delete(fullfile(target_dir, dot_final(i).name));
end
% Clean root dir
dot_final_root = dir(fullfile(sd_root, '._*'));
for i = 1:length(dot_final_root)
delete(fullfile(sd_root, dot_final_root(i).name));
end
if ~isempty(dot_final) || ~isempty(dot_final_root)
fprintf(' ✓ Final dot-file cleanup: removed %d files\n', ...
length(dot_final) + length(dot_final_root));
end
end

%% Verify (exclude macOS ._* resource fork files from count)
%% Verify
all_pat = dir(fullfile(target_dir, '*.pat'));
real_pat = all_pat(~startsWith({all_pat.name}, '._'));
verify_count = length(real_pat);
verify_count = length(all_pat);
if verify_count ~= num_patterns
mapping.error = sprintf('Verification failed: expected %d patterns, found %d on SD card', ...
num_patterns, verify_count);
Expand Down
28 changes: 14 additions & 14 deletions tests/test_sd_card_deployment.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
% 2. MANIFEST.bin binary format (uint16 count + uint32 timestamp)
% 3. MANIFEST.txt human-readable mapping
% 4. Verification count accuracy
% 5. macOS dot-file cleanup (AppleDouble resource fork removal)
% 5. macOS dot-file prevention (xattr -c before copy to FAT32)
% 6. ValidateDriveName on Mac (volume name from mount path)
% 7. detect_sd_card utility (when UseRealSD=true)
% 8. Format + deploy pipeline (when UseRealSD=true, DESTRUCTIVE)
Expand Down Expand Up @@ -220,34 +220,34 @@
record_fail('1.7 Mapping struct fields', 'Mapping not available');
end

% Test 1.8: macOS dot-file cleanup simulation
% Test 1.8: macOS dot-file prevention (xattr -c)
if ismac
fprintf('\n [Mac-specific] Testing dot-file cleanup...\n');
fprintf('\n [Mac-specific] Testing dot-file prevention...\n');

% Clean and redo with injected dot-files
% Clean and redo
if isfolder(fake_sd), rmdir(fake_sd, 's'); end
mkdir(fake_sd);

mapping2 = prepare_sd_card_crossplatform(pattern_paths, fake_sd, ...
'ValidateDriveName', false);

% Check no dot-files remain
% Check no dot-files were created
dot_files_pat = dir(fullfile(fake_sd, 'patterns', '._*'));
dot_files_root = dir(fullfile(fake_sd, '._*'));
total_dots = length(dot_files_pat) + length(dot_files_root);

if total_dots == 0 && mapping2.success
record_pass('1.8 macOS dot-file cleanup', ...
'No ._* files remain on fake SD');
record_pass('1.8 macOS dot-file prevention', ...
'No ._* files on fake SD (xattr -c worked)');
elseif ~mapping2.success
record_fail('1.8 macOS dot-file cleanup', ...
record_fail('1.8 macOS dot-file prevention', ...
sprintf('Deployment failed: %s', mapping2.error));
else
record_fail('1.8 macOS dot-file cleanup', ...
record_fail('1.8 macOS dot-file prevention', ...
sprintf('%d dot-files still present', total_dots));
end
else
record_pass('1.8 macOS dot-file cleanup', 'Skipped (not macOS)');
record_pass('1.8 macOS dot-file prevention', 'Skipped (not macOS)');
end

% Test 1.9: ValidateDriveName with wrong name
Expand Down Expand Up @@ -356,15 +356,15 @@
real_target = mapping_real.target_dir;
dot_check = dir(fullfile(real_target, '._*'));
if isempty(dot_check)
record_pass('3.3 Real SD dot-file clean', ...
record_pass('3.3 Real SD no dot-files', ...
'No ._* files on SD card');
else
record_fail('3.3 Real SD dot-file clean', ...
record_fail('3.3 Real SD no dot-files', ...
sprintf('%d dot-files found — dirIndex may be corrupted', ...
length(dot_check)));
end
else
record_pass('3.3 Real SD dot-file clean', 'Skipped (not macOS)');
record_pass('3.3 Real SD no dot-files', 'Skipped (not macOS)');
end

% Test 3.4: Verify pattern count on real SD
Expand All @@ -379,7 +379,7 @@
end
else
record_fail('3.2 Real SD deployment', mapping_real.error);
record_fail('3.3 Real SD dot-file clean', 'Deployment failed');
record_fail('3.3 Real SD no dot-files', 'Deployment failed');
record_fail('3.4 Real SD pattern count', 'Deployment failed');
end
end
Expand Down