// Copyright (C) 2026 Viorel Munteanu // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include "fixtures.h" #include #include #include #include #include #include #include #include #include #include AppConfig g_test_app_config; std::optional find_busybox_fixture() { std::error_code ec; auto path = std::filesystem::current_path(ec) / "images" / "busybox.tar"; if (ec || !std::filesystem::exists(path, ec) || ec) { return std::nullopt; } return path; } namespace { std::optional getenv_opt(const char* name) { const char* value = std::getenv(name); if (!value) { return std::nullopt; } return std::string(value); } } // namespace ScratchXdgDirs::ScratchXdgDirs() { // Real bug found by testing, not assumed: an earlier version built // this vector from `std::string("...").begin()` paired with a // *separate* `std::string("...").end()` -- two distinct temporary // objects, even though they held identical content. Mixing iterators // from two different containers when constructing a third is // undefined behavior; here it manifested as an intermittent (SSO/heap // address dependent, hence flaky) `std::length_error: cannot create // std::vector larger than max_size()` from a garbage begin/end // distance, confirmed via ~10 repeated runs of the [integration] tests // together before it reproduced. Fixed by using one string instance // for both ends of the range. std::string tmpl_str = "/tmp/slocker-lite-test-XXXXXX"; std::vector tmpl(tmpl_str.begin(), tmpl_str.end()); tmpl.push_back('\0'); char* created = mkdtemp(tmpl.data()); path_ = created ? std::filesystem::path(created) : std::filesystem::temp_directory_path() / "slocker-lite-test"; std::filesystem::create_directories(path_); previous_config_home_ = getenv_opt("XDG_CONFIG_HOME"); previous_state_home_ = getenv_opt("XDG_STATE_HOME"); setenv("XDG_CONFIG_HOME", path_.c_str(), 1); setenv("XDG_STATE_HOME", path_.c_str(), 1); } ScratchXdgDirs::~ScratchXdgDirs() { if (previous_config_home_) { setenv("XDG_CONFIG_HOME", previous_config_home_->c_str(), 1); } else { unsetenv("XDG_CONFIG_HOME"); } if (previous_state_home_) { setenv("XDG_STATE_HOME", previous_state_home_->c_str(), 1); } else { unsetenv("XDG_STATE_HOME"); } std::error_code ec; std::filesystem::remove_all(path_, ec); } CapturedStdout::CapturedStdout() { std::string tmpl_str = "/tmp/slocker-lite-test-stdout-XXXXXX"; std::vector tmpl(tmpl_str.begin(), tmpl_str.end()); tmpl.push_back('\0'); int fd = mkstemp(tmpl.data()); temp_path_ = tmpl.data(); fflush(stdout); saved_fd_ = dup(STDOUT_FILENO); dup2(fd, STDOUT_FILENO); close(fd); } CapturedStdout::~CapturedStdout() { fflush(stdout); if (saved_fd_ >= 0) { dup2(saved_fd_, STDOUT_FILENO); close(saved_fd_); } std::error_code ec; std::filesystem::remove(temp_path_, ec); } std::string CapturedStdout::contents() { fflush(stdout); std::ifstream in(temp_path_); std::ostringstream out; out << in.rdbuf(); return out.str(); } std::vector split_lines_trimmed(const std::string& text) { std::vector lines; std::istringstream iss(text); std::string line; while (std::getline(iss, line)) { while (!line.empty() && std::isspace(static_cast(line.back()))) { line.pop_back(); } if (!line.empty()) { lines.push_back(line); } } return lines; } std::vector extract_marked_lines(const std::string& text) { auto lines = split_lines_trimmed(text); auto begin = std::find(lines.begin(), lines.end(), "BEGIN-TEST-OUTPUT"); auto end = std::find(lines.begin(), lines.end(), "END-TEST-OUTPUT"); if (begin == lines.end() || end == lines.end() || end <= begin) { return {}; } return std::vector(begin + 1, end); }