1 parent
41599fa82f
commit
0dd6bef715
12 files changed
+161
-64
No files matched your search
+47
-29
@@ -10,16 +10,33 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <qt_windows.h>
|
||||
#include <stdlib.h> // _wputenv_s
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
const char kMarker[] = ".bilauncher-test-sandbox";
|
||||
|
||||
QTemporaryDir *g_sandbox = nullptr;
|
||||
bool g_realProfile = false; // Windows: работаем в настоящем профиле под меткой
|
||||
|
||||
[[noreturn]] void abortSetup(const QString &message)
|
||||
{
|
||||
std::fprintf(stderr, "TestEnv: %s\n", qPrintable(message));
|
||||
const QString line = QStringLiteral("TestEnv: ") + message + u'\n';
|
||||
#if defined(Q_OS_WIN)
|
||||
// В живую консоль пишем UTF-16 напрямую: кодовая страница консоли тогда не важна.
|
||||
const HANDLE handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
DWORD mode = 0;
|
||||
if (handle != INVALID_HANDLE_VALUE && GetConsoleMode(handle, &mode)) {
|
||||
DWORD written = 0;
|
||||
WriteConsoleW(handle, reinterpret_cast<const wchar_t *>(line.utf16()),
|
||||
DWORD(line.size()), &written, nullptr);
|
||||
std::exit(2);
|
||||
}
|
||||
#endif
|
||||
// Вывод в трубу (ctest, CI) — UTF-8, как его и читает ctest. Локальная 8-битная
|
||||
// кодировка Windows (cp1252/cp1251) превращала кириллицу в «???».
|
||||
std::fputs(line.toUtf8().constData(), stderr);
|
||||
std::fflush(stderr);
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
@@ -31,15 +48,16 @@ void TestEnv::prepareSandbox()
|
||||
if (!g_sandbox->isValid())
|
||||
abortSetup(QStringLiteral("не удалось создать временную папку"));
|
||||
|
||||
const QByteArray home = QDir::toNativeSeparators(g_sandbox->path()).toLocal8Bit();
|
||||
qputenv("HOME", home);
|
||||
qputenv("USERPROFILE", home);
|
||||
const QString home = QDir::toNativeSeparators(g_sandbox->path());
|
||||
setEnvPath("HOME", home);
|
||||
setEnvPath("USERPROFILE", home);
|
||||
// LauncherPaths: на Windows QDir::homePath() HOME/USERPROFILE не читает, а эту
|
||||
// переменную лаунчер читает везде. Дочерние процессы получают её по наследству.
|
||||
setEnvPath("BILAUNCHER_HOME", home);
|
||||
// Чужая Java из окружения машины не должна влиять на выбор Java в тестах.
|
||||
qunsetenv("JAVA_HOME");
|
||||
|
||||
std::atexit([] {
|
||||
if (g_realProfile)
|
||||
QDir(LauncherPaths::rootDir()).removeRecursively();
|
||||
delete g_sandbox;
|
||||
g_sandbox = nullptr;
|
||||
});
|
||||
@@ -52,19 +70,13 @@ void TestEnv::finishSetup()
|
||||
|
||||
const QString root = QDir::cleanPath(LauncherPaths::rootDir());
|
||||
const QString sandbox = QDir::cleanPath(g_sandbox->path());
|
||||
if (!root.startsWith(sandbox + u'/')) {
|
||||
#if defined(Q_OS_WIN)
|
||||
if (QDir(root).exists() && !QFile::exists(root + u'/' + QLatin1String(kMarker))) {
|
||||
abortSetup(QStringLiteral(
|
||||
"%1 уже существует и создана не тестами — отказываюсь её трогать. "
|
||||
"Запускайте тесты под отдельной учётной записью.")
|
||||
.arg(QDir::toNativeSeparators(root)));
|
||||
}
|
||||
g_realProfile = true;
|
||||
resetLauncherRoot();
|
||||
#else
|
||||
abortSetup(QStringLiteral("папка лаунчера %1 вне песочницы %2").arg(root, sandbox));
|
||||
#endif
|
||||
const QString game = QDir::cleanPath(LauncherPaths::defaultMinecraftDir());
|
||||
// Ни одна папка лаунчера не должна указывать на настоящий профиль: resetLauncherRoot()
|
||||
// стирает rootDir() целиком.
|
||||
for (const QString &path : {root, game}) {
|
||||
if (!path.startsWith(sandbox + u'/'))
|
||||
abortSetup(QStringLiteral("путь лаунчера %1 вне песочницы %2 — отказываюсь запускаться")
|
||||
.arg(QDir::toNativeSeparators(path), QDir::toNativeSeparators(sandbox)));
|
||||
}
|
||||
|
||||
if (!Localization::instance().load())
|
||||
@@ -72,6 +84,18 @@ void TestEnv::finishSetup()
|
||||
Localization::instance().setLanguage(QStringLiteral("en"));
|
||||
}
|
||||
|
||||
void TestEnv::setEnvPath(const char *name, const QString &path)
|
||||
{
|
||||
// На Windows qputenv() пишет в ANSI-кодировке и испортил бы, например,
|
||||
// кириллическое имя пользователя во временном пути.
|
||||
#if defined(Q_OS_WIN)
|
||||
_wputenv_s(reinterpret_cast<const wchar_t *>(QString::fromLatin1(name).utf16()),
|
||||
reinterpret_cast<const wchar_t *>(path.utf16()));
|
||||
#else
|
||||
qputenv(name, QFile::encodeName(path));
|
||||
#endif
|
||||
}
|
||||
|
||||
QString TestEnv::homeDir()
|
||||
{
|
||||
return g_sandbox->path();
|
||||
@@ -87,11 +111,5 @@ QString TestEnv::freshDir(const QString &name)
|
||||
|
||||
void TestEnv::resetLauncherRoot()
|
||||
{
|
||||
const QString root = LauncherPaths::rootDir();
|
||||
QDir(root).removeRecursively();
|
||||
if (g_realProfile) {
|
||||
QDir().mkpath(root);
|
||||
QFile marker(root + u'/' + QLatin1String(kMarker));
|
||||
marker.open(QIODevice::WriteOnly);
|
||||
}
|
||||
QDir(LauncherPaths::rootDir()).removeRecursively();
|
||||
}
|
||||
+13
-12
@@ -2,16 +2,14 @@
|
||||
|
||||
// Общая обвязка тестов: песочница для домашней папки и main для QtTest.
|
||||
//
|
||||
// LauncherPaths строит все пути от QDir::homePath(). На Linux и macOS он берёт
|
||||
// HOME, поэтому каждый тестовый процесс до создания QCoreApplication
|
||||
// подменяет HOME на свою временную папку — реальные ~/BiLauncher и .minecraft
|
||||
// машины, на которой гоняются тесты, не трогаются.
|
||||
//
|
||||
// На Windows QDir::homePath() спрашивает профиль у системы и переменные
|
||||
// окружения игнорирует. Там тесты работают в настоящей
|
||||
// %APPDATA%\BiLauncher, но только если её ещё нет либо она создана тестами
|
||||
// (в ней лежит метка .bilauncher-test-sandbox). Иначе процесс отказывается
|
||||
// запускаться, чтобы не испортить данные живого лаунчера.
|
||||
// LauncherPaths строит все пути от домашней папки. Каждый тестовый процесс до
|
||||
// создания QCoreApplication заводит свою временную папку и направляет туда
|
||||
// BILAUNCHER_HOME (её LauncherPaths читает на всех ОС), а заодно HOME и
|
||||
// USERPROFILE (их читает QDir::homePath() на Linux и macOS). На Windows
|
||||
// QDir::homePath() переменные окружения игнорирует, поэтому без BILAUNCHER_HOME
|
||||
// там не обойтись. Реальные папка лаунчера и .minecraft машины, на которой гоняются
|
||||
// тесты, не трогаются: если путь лаунчера всё же оказался вне песочницы,
|
||||
// процесс сразу завершается с кодом 2.
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QString>
|
||||
@@ -19,14 +17,17 @@
|
||||
|
||||
namespace TestEnv {
|
||||
|
||||
// До QCoreApplication: временная папка и подмена HOME/USERPROFILE.
|
||||
// До QCoreApplication: временная папка и подмена BILAUNCHER_HOME/HOME/USERPROFILE.
|
||||
void prepareSandbox();
|
||||
|
||||
// После QCoreApplication: проверка, что пути лаунчера ушли в песочницу,
|
||||
// загрузка переводов, английский язык (тексты ошибок не зависят от локали машины).
|
||||
void finishSetup();
|
||||
|
||||
// Корень песочницы — то, что сейчас возвращает QDir::homePath() в тестах.
|
||||
// Переменная окружения с путём, без потерь символов вне ANSI-кодировки на Windows.
|
||||
void setEnvPath(const char *name, const QString &path);
|
||||
|
||||
// Корень песочницы — домашняя папка, от которой LauncherPaths строит пути в тестах.
|
||||
QString homeDir();
|
||||
|
||||
// Свежая пустая папка внутри песочницы (для .minecraft, архивов и т. п.).
|
||||
|
||||
Reference in new issue
Block a user