#include "phonecast/platform/network/LanDiscovery.h" #include "phonecast/platform/support/SupportDiagnostics.h" #include "phonecast/vr/overlay/SettingsMenuController.h" #include #include #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #else #include #include #include #endif void CheckAt(bool value, int line) { if (!value) throw std::runtime_error("support test failed at line " + std::to_string(line)); } #define Check(value) CheckAt((value), __LINE__) void Env(const char* key, const std::string& value) { #ifdef _WIN32 _putenv_s(key, value.c_str()); #else setenv(key, value.c_str(), 1); #endif } int main() { try { using namespace phonecast::platform; const std::string nonce = "0123456789abcdef0123456789abcdef"; const auto query = "PCVR_DISCOVER 1 " + nonce; Check(network::DiscoveryReply(query,49321,"Frame","0.9.0") == "PCVR_RECEIVER 1 " + nonce + " 49321 0.9.0 Frame"); Check(network::DiscoveryReply(query + "x",49321,"Frame","0.9.0").empty()); Check(network::DiscoveryReply("PCVR_DISCOVER 1 " + std::string(32,'z'),49321,"Frame","0.9.0").empty()); Check(network::DiscoveryReply(query,0,"Frame","0.9.0").empty()); Check(network::DiscoveryReply(query,49321,"Frame\nsecret","0.9.0").find('\n') == std::string::npos); Check(support::SafeDiagnosticLine("[network] Rejected a client with an invalid pairing code").size() > 0); Check(support::SafeDiagnosticLine("[network] code=123456").empty()); Check(support::SafeDiagnosticLine("[notification] private text").empty()); Check(support::SafeDiagnosticLine("[diagnostics] connected=yes rx-fps=30 dropped=0").size() > 0); Check(support::SafeDiagnosticLine("[diagnostics] connected=yes pair-code=123456").empty()); Check(support::SafeDiagnosticLine("[diagnostics] connected=yes working-set-mb=105.2 audio-opens=1 vr-dropped=0").size() > 0); Check(support::SafeDiagnosticLine("[diagnostics] audio-secret=123456").empty()); Check(support::SafeDiagnosticLine("[diagnostics] rx-fps=private").empty()); phonecast::vr::SettingsMenuController menu; using phonecast::vr::SettingsMenuCommand; menu.Open({}); for (int i=0;i<10;++i) menu.Handle(SettingsMenuCommand::NextItem); menu.Handle(SettingsMenuCommand::Activate); Check(menu.View().title == "ABOUT PHONECAST"); Check(menu.View().values[0] == PHONECAST_VERSION); menu.Handle(SettingsMenuCommand::NextItem); menu.Handle(SettingsMenuCommand::NextItem); Check(menu.Handle(SettingsMenuCommand::Activate) == phonecast::vr::SettingsMenuResult::None); Check(menu.View().values[2] == "CONFIRM EXPORT"); Check(menu.Handle(SettingsMenuCommand::Activate) == phonecast::vr::SettingsMenuResult::ExportRequested); Check(menu.Handle(SettingsMenuCommand::Activate) == phonecast::vr::SettingsMenuResult::None); menu.SetExportStatus("SAVED TO DOWNLOADS"); menu.Handle(SettingsMenuCommand::Activate); menu.Handle(SettingsMenuCommand::NextItem); menu.Handle(SettingsMenuCommand::PreviousItem); Check(menu.Handle(SettingsMenuCommand::Activate) == phonecast::vr::SettingsMenuResult::None); const auto root = std::filesystem::temp_directory_path() / ("phonecast-support-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); std::filesystem::create_directories(root / "state/phonecast-vr"); std::filesystem::create_directories(root / "config"); #ifdef _WIN32 Env("USERPROFILE", root.string()); Env("LOCALAPPDATA", (root / "state").string()); std::filesystem::create_directories(root / "state/PhoneCastVR"); auto logPath = root / "state/PhoneCastVR/receiver.log"; #else Env("HOME", root.string()); Env("XDG_STATE_HOME", (root / "state").string()); Env("XDG_CONFIG_HOME", (root / "config").string()); auto logPath = root / "state/phonecast-vr/receiver.log"; #endif { std::ofstream log(logPath); log << "private 123456\n[diagnostics] connected=yes rx-fps=30 dropped=0\n[network] Rejected a client with an invalid pairing code\n"; } std::string filename,error; Check(support::ExportDiagnostics(filename,error)); Check(filename.rfind("phonecast-headset-", 0) == 0); std::ifstream exported(root / "Downloads" / filename); std::string report((std::istreambuf_iterator(exported)), {}); Check(report.find("123456") == std::string::npos); Check(report.find("rx-fps=30") != std::string::npos); Check(report.find("invalid pairing code") != std::string::npos); exported.close(); { std::ofstream log(logPath); for (int i=0;i<12000;++i) log << "[diagnostics] connected=yes rx-fps=30 working-set-mb=105 dropped=0\n"; } Check(support::ExportDiagnostics(filename,error)); Check(std::filesystem::file_size(root / "Downloads" / filename) < 70000); #ifndef _WIN32 { std::ofstream dirs(root / "config/user-dirs.dirs"); dirs << "XDG_DOWNLOAD_DIR=\"$HOME/LocalizedDownloads\"\n"; } Check(support::ExportDiagnostics(filename,error)); Check(std::filesystem::exists(root / "LocalizedDownloads" / filename)); std::filesystem::remove(root / "config/user-dirs.dirs"); #endif std::filesystem::remove_all(root / "Downloads"); { std::ofstream blocked(root / "Downloads"); blocked << "not a directory"; } Check(!support::ExportDiagnostics(filename,error)); Check(!error.empty()); std::filesystem::remove_all(root); network::LanDiscovery responder; Check(responder.Start(49321)); auto socket = ::socket(AF_INET,SOCK_DGRAM,0); #ifdef _WIN32 DWORD timeout = 2000; setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,reinterpret_cast(&timeout),sizeof(timeout)); #else timeval timeout{2,0}; setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout)); #endif sockaddr_in address{}; address.sin_family=AF_INET; address.sin_port=htons(49322); address.sin_addr.s_addr=htonl(INADDR_LOOPBACK); sendto(socket,query.data(),static_cast(query.size()),0,reinterpret_cast(&address),sizeof(address)); char reply[256]{}; const auto received=recv(socket,reply,sizeof(reply),0); Check(received>0); Check(std::string(reply,static_cast(received)).rfind("PCVR_RECEIVER 1 " + nonce,0)==0); #ifdef _WIN32 closesocket(socket); #else close(socket); #endif responder.Stop(); Check(responder.Start(49321)); responder.Stop(); std::cout << "Support/discovery tests passed\n"; return EXIT_SUCCESS; } catch (const std::exception& error) { std::cerr << error.what() << '\n'; return EXIT_FAILURE; } }