mirror of
https://github.com/Pixelplanet/Pixel-VR-Player.git
synced 2026-10-20 20:01:38 +02:00
- Add Hidden Vault system: - Secure directory storage under ~/.pixelvr_vault with 0700 permissions. - Cheat-code combo unlocking triggered on full grip squeeze (A-B-B-A-X-Y default, customizable). - Headset removal / face detection gating and inactivity auto-lock. - Automatic download history clearing upon vault lock. - Add File Actions Submenu (Button Y): - In-browser file/folder submenu with Move to Vault, Delete, Move, and Rename. - Recursive folder move to vault with collision handling. - Folder deletion with confirmation dialog. - Enhance VR controllers and interactions: - Dynamic left and right Steam Frame controller render models. - Single laser pointer priority with hand swapping via full grip squeeze. - Fix video format detection: - Correct auto-detection and default projection for high-resolution VR180 SBS media (e.g. 7200x3600). - Visual sharpness & rendering stability: - Render swapchain at native 2160x2160 per eye. - SDF font rendering with RMS pixel footprint and stem darkening. - Razor-sharp procedural UI anti-aliasing and crisp border highlights without distortion. - Add unit test suite (tests/test_vault.cpp) and documentation specs.
199 lines
8.7 KiB
C++
199 lines
8.7 KiB
C++
#include "app/vault_manager.hpp"
|
|
#include <cassert>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
|
|
using namespace pixelvr;
|
|
namespace fs = std::filesystem;
|
|
|
|
int main() {
|
|
std::cout << "[TEST] Starting VaultManager verification..." << std::endl;
|
|
auto& vm = VaultManager::instance();
|
|
|
|
// Reset to default combo sequence for reproducible testing
|
|
vm.startRecordingCombo();
|
|
vm.feedRecordButton(XrInputState::BtnA);
|
|
vm.feedRecordButton(XrInputState::BtnB);
|
|
vm.feedRecordButton(XrInputState::BtnB);
|
|
vm.feedRecordButton(XrInputState::BtnA);
|
|
vm.feedRecordButton(XrInputState::BtnX);
|
|
vm.feedRecordButton(XrInputState::BtnY);
|
|
|
|
// 1. Initial locked state
|
|
vm.lock();
|
|
assert(!vm.isUnlocked());
|
|
std::cout << "[PASS] Initial state is locked." << std::endl;
|
|
|
|
// 2. Discrete grip requirement (soft squeeze / grip=false should not advance)
|
|
auto now = std::chrono::steady_clock::now();
|
|
assert(vm.feedInput(XrInputState::BtnA, false, now) == VaultManager::ComboResult::None);
|
|
assert(vm.feedInput(XrInputState::BtnB, false, now) == VaultManager::ComboResult::None);
|
|
assert(!vm.isUnlocked());
|
|
std::cout << "[PASS] Soft squeeze / no grip full press does not trigger combo." << std::endl;
|
|
|
|
// 3. Step through default combo: A -> B -> B -> A -> X -> Y
|
|
assert(vm.feedInput(XrInputState::BtnA, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnB, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnB, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnA, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnX, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnY, true, now) == VaultManager::ComboResult::Unlocked);
|
|
assert(vm.isUnlocked());
|
|
std::cout << "[PASS] Cheat code A-B-B-A-X-Y with grip full press unlocks vault!" << std::endl;
|
|
|
|
// Verify vault directories exist and have 0700 permissions
|
|
assert(fs::exists(vm.vaultDir()));
|
|
assert(fs::exists(vm.thumbsDir()));
|
|
auto perms = fs::status(vm.vaultDir()).permissions();
|
|
assert((perms & fs::perms::others_all) == fs::perms::none);
|
|
std::cout << "[PASS] Vault directory created with secure 0700 permissions: " << vm.vaultDir() << std::endl;
|
|
|
|
// 4. Test re-lock via combo
|
|
assert(vm.feedInput(XrInputState::BtnA, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnB, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnB, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnA, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnX, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnY, true, now) == VaultManager::ComboResult::Locked);
|
|
assert(!vm.isUnlocked());
|
|
std::cout << "[PASS] Entering combo again locks the vault." << std::endl;
|
|
|
|
// 5. Test Headset removal auto-lock with 3-second debounce
|
|
vm.unlock();
|
|
assert(vm.isUnlocked());
|
|
assert(!vm.tick(now, false)); // Momentary flicker (<3s) does NOT lock
|
|
assert(vm.isUnlocked());
|
|
assert(vm.tick(now + std::chrono::milliseconds(3100), false)); // >=3s continuous loss locks
|
|
assert(!vm.isUnlocked());
|
|
std::cout << "[PASS] Auto-lock on headset removal successfully locks vault after 3s debounce." << std::endl;
|
|
|
|
// 6. Test Inactivity timeout (enabled vs disabled)
|
|
vm.unlock();
|
|
vm.setAutoLockTimeoutSec(2); // 2 seconds for test
|
|
vm.recordActivity(now);
|
|
assert(!vm.tick(now, true)); // immediate tick does not lock
|
|
assert(vm.isUnlocked());
|
|
assert(vm.tick(now + std::chrono::seconds(3), true)); // 3s later -> timeout
|
|
assert(!vm.isUnlocked());
|
|
std::cout << "[PASS] Inactivity timeout successfully locks vault when enabled." << std::endl;
|
|
|
|
// Test Inactivity timeout disabled (default)
|
|
vm.unlock();
|
|
vm.setAutoLockTimeoutSec(0); // 0 = disabled
|
|
vm.recordActivity(now);
|
|
assert(!vm.tick(now + std::chrono::hours(1), true)); // 1 hour later -> still unlocked because disabled
|
|
assert(vm.isUnlocked());
|
|
std::cout << "[PASS] Inactivity timeout = 0 disables timeout auto-lock." << std::endl;
|
|
|
|
// 7. Test Move-to-Vault for single file and collision resolution
|
|
vm.unlock();
|
|
std::string testSrc = "/tmp/pixelvr_test_clip.mp4";
|
|
{
|
|
std::ofstream f(testSrc);
|
|
f << "dummy video content 12345";
|
|
}
|
|
std::string outPath1;
|
|
std::string err1 = vm.moveFileToVault(testSrc, outPath1);
|
|
assert(err1.empty());
|
|
assert(!fs::exists(testSrc));
|
|
assert(fs::exists(outPath1));
|
|
std::cout << "[PASS] Move file to vault succeeded: " << outPath1 << std::endl;
|
|
|
|
// Collision test: create another file with same name
|
|
{
|
|
std::ofstream f(testSrc);
|
|
f << "dummy video content second version";
|
|
}
|
|
std::string outPath2;
|
|
std::string err2 = vm.moveFileToVault(testSrc, outPath2);
|
|
assert(err2.empty());
|
|
assert(!fs::exists(testSrc));
|
|
assert(fs::exists(outPath2));
|
|
assert(outPath1 != outPath2);
|
|
std::cout << "[PASS] Collision resolved properly: " << outPath2 << std::endl;
|
|
|
|
// Clean up test files
|
|
fs::remove(outPath1);
|
|
fs::remove(outPath2);
|
|
|
|
// 8. Test Move-to-Vault for entire directory and recursive permissions
|
|
std::string testDir = "/tmp/pixelvr_test_folder";
|
|
fs::create_directories(testDir);
|
|
{
|
|
std::ofstream f1(testDir + "/subvideo1.mp4");
|
|
f1 << "sub video 1 content";
|
|
std::ofstream f2(testDir + "/subvideo2.mp4");
|
|
f2 << "sub video 2 content";
|
|
}
|
|
std::string outDir1;
|
|
std::string errDir1 = vm.moveFileToVault(testDir, outDir1);
|
|
assert(errDir1.empty());
|
|
assert(!fs::exists(testDir));
|
|
assert(fs::exists(outDir1));
|
|
assert(fs::is_directory(outDir1));
|
|
assert(fs::exists(outDir1 + "/subvideo1.mp4"));
|
|
assert(fs::exists(outDir1 + "/subvideo2.mp4"));
|
|
|
|
// Check directory permissions
|
|
auto dirPerms = fs::status(outDir1).permissions();
|
|
assert((dirPerms & fs::perms::others_all) == fs::perms::none);
|
|
std::cout << "[PASS] Move entire folder to vault succeeded with 0700 permissions: " << outDir1 << std::endl;
|
|
|
|
// Folder collision test: recreate folder and move again
|
|
fs::create_directories(testDir);
|
|
{
|
|
std::ofstream f(testDir + "/subvideo3.mp4");
|
|
f << "sub video 3 content";
|
|
}
|
|
std::string outDir2;
|
|
std::string errDir2 = vm.moveFileToVault(testDir, outDir2);
|
|
assert(errDir2.empty());
|
|
assert(!fs::exists(testDir));
|
|
assert(fs::exists(outDir2));
|
|
assert(outDir1 != outDir2);
|
|
assert(fs::exists(outDir2 + "/subvideo3.mp4"));
|
|
std::cout << "[PASS] Folder collision resolved properly: " << outDir2 << std::endl;
|
|
|
|
// Clean up test directories
|
|
fs::remove_all(outDir1);
|
|
fs::remove_all(outDir2);
|
|
|
|
// 9. Custom combo recording
|
|
vm.startRecordingCombo();
|
|
assert(vm.isRecordingCombo());
|
|
assert(!vm.feedRecordButton(XrInputState::BtnX));
|
|
assert(!vm.feedRecordButton(XrInputState::BtnX));
|
|
assert(!vm.feedRecordButton(XrInputState::BtnY));
|
|
assert(!vm.feedRecordButton(XrInputState::BtnY));
|
|
assert(!vm.feedRecordButton(XrInputState::BtnA));
|
|
assert(vm.feedRecordButton(XrInputState::BtnB)); // 6th button finishes
|
|
assert(!vm.isRecordingCombo());
|
|
assert(vm.comboString() == "X X Y Y A B");
|
|
std::cout << "[PASS] Custom combo recording verified: " << vm.comboString() << std::endl;
|
|
|
|
// Test new combo works
|
|
vm.lock();
|
|
assert(vm.feedInput(XrInputState::BtnX, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnX, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnY, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnY, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnA, true, now) == VaultManager::ComboResult::StepMatched);
|
|
assert(vm.feedInput(XrInputState::BtnB, true, now) == VaultManager::ComboResult::Unlocked);
|
|
assert(vm.isUnlocked());
|
|
std::cout << "[PASS] New custom combo successfully unlocked vault!" << std::endl;
|
|
|
|
// Restore default combo
|
|
vm.startRecordingCombo();
|
|
vm.feedRecordButton(XrInputState::BtnA);
|
|
vm.feedRecordButton(XrInputState::BtnB);
|
|
vm.feedRecordButton(XrInputState::BtnB);
|
|
vm.feedRecordButton(XrInputState::BtnA);
|
|
vm.feedRecordButton(XrInputState::BtnX);
|
|
vm.feedRecordButton(XrInputState::BtnY);
|
|
vm.lock();
|
|
|
|
std::cout << "\n*** ALL VAULT MANAGER UNIT TESTS PASSED PERFECTLY! ***" << std::endl;
|
|
return 0;
|
|
}
|