Files
SEQUA-Engine/platform/layout/tests/test_layout.cpp
T
Ange-Marie MAURIN - A2M Systèmes da0d31385b build: arborescence bin/<plateforme>/<type> et build unifié (ADR 0010)
- platform/layout : racine sequa-root.json, chemins des composants, arguments et chemins en UTF-8.
- Règles install() : bin/windows-x64/{debug,release,asan}/ (core, render, studio + ui, engines).
- Le Core trouve sequa-render et les adaptateurs depuis la racine (--sequa-root, passé par
  Studio host à sa copie fantôme) : plus de render-unavailable depuis le panneau Rendu.
- L'adaptateur Godot cherche Godot et son projet à côté de lui (surcharge par --adapter-options) ;
  template_release pour le type release. Godot hébergé dans les presets debug et release.
- scripts/build-all.ps1 : Godot, C++, interface, arrêt des processus du dossier visé, install ;
  check.ps1 l'appelle (modification de l'ADR 0004).
- docs/installation/ : compiler, lancer, installer et désinstaller par plateforme.
2026-11-07 15:50:37 +01:00

228 lines
7.0 KiB
C++

// SPDX-License-Identifier: MPL-2.0
#include "sequa/platform/layout.hpp"
#include <doctest/doctest.h>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <string>
#include <system_error>
namespace sequa::platform::testing {
// Dossier temporaire propre à un test, supprimé à la fin.
class TempDir {
public:
TempDir()
: path_(std::filesystem::temp_directory_path()
/ ("sequa-layout-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + "-"
+ std::to_string(++counter_)))
{
std::filesystem::create_directories(path_);
}
~TempDir()
{
std::error_code ignored;
std::filesystem::remove_all(path_, ignored);
}
TempDir(const TempDir&) = delete;
TempDir& operator=(const TempDir&) = delete;
[[nodiscard]] const std::filesystem::path& path() const noexcept { return path_; }
private:
inline static int counter_ = 0;
std::filesystem::path path_;
};
// Écrit un fichier repère vide.
inline void touch(const std::filesystem::path& file)
{
std::ofstream out(file);
REQUIRE(out.good());
}
// Fonction dont l'adresse sert à module_path.
inline void function_in_test_executable() {}
} // namespace sequa::platform::testing
TEST_SUITE("sequa::platform::layout") {
TEST_CASE("find_root finds marker in start directory itself") {
using namespace sequa::platform;
using namespace sequa::platform::testing;
TempDir tmp;
const auto root = tmp.path();
std::error_code ec;
std::filesystem::create_directories(root / "core", ec);
REQUIRE(!ec);
std::filesystem::create_directories(root / "render", ec);
REQUIRE(!ec);
// Créer le fichier repère
const auto marker = root / root_marker;
std::filesystem::create_directories(marker.parent_path(), ec);
REQUIRE(!ec);
touch(marker);
const auto found = find_root(root);
REQUIRE(found.has_value());
CHECK(std::filesystem::equivalent(found.value(), root));
}
TEST_CASE("find_root finds marker two levels up from nested directory") {
using namespace sequa::platform;
using namespace sequa::platform::testing;
TempDir tmp;
const auto root = tmp.path();
const auto nested = root / "a" / "b" / "c";
std::error_code ec;
std::filesystem::create_directories(nested, ec);
REQUIRE(!ec);
// Créer le fichier repère à la racine
const auto marker = root / root_marker;
touch(marker);
const auto found = find_root(nested);
REQUIRE(found.has_value());
CHECK(std::filesystem::equivalent(found.value(), root));
}
TEST_CASE("find_root returns nullopt when marker absent") {
using namespace sequa::platform;
using namespace sequa::platform::testing;
TempDir tmp;
const auto root = tmp.path();
const auto nested = root / "a" / "b";
std::error_code ec;
std::filesystem::create_directories(nested, ec);
REQUIRE(!ec);
const auto found = find_root(nested);
CHECK_FALSE(found.has_value());
}
TEST_CASE("Layout paths match expected structure") {
using namespace sequa::platform;
using namespace sequa::platform::testing;
TempDir tmp;
const auto root = tmp.path();
Layout layout(root);
CHECK(layout.root() == root);
CHECK(layout.core_executable() == root / "core" / executable_name("sequa-core"));
CHECK(layout.render_executable() == root / "render" / executable_name("sequa-render"));
CHECK(layout.studio_executable() == root / "studio" / executable_name("sequa-studio"));
CHECK(layout.ui_dir() == root / "studio" / "ui");
CHECK(layout.engine_dir("godot") == root / "engines" / "godot");
}
TEST_CASE("adapter_library returns correct filename and rejects invalid names") {
using namespace sequa::platform;
using namespace sequa::platform::testing;
TempDir tmp;
const auto root = tmp.path();
Layout layout(root);
// Nom valide
const auto godot_adapter = layout.adapter_library("godot");
REQUIRE(godot_adapter.has_value());
CHECK(godot_adapter.value() == root / "engines" / "godot" / module_name("sequa_godot_adapter"));
// Nom invalide : contient des caractères non autorisés
CHECK_FALSE(layout.adapter_library("../x").has_value());
CHECK_FALSE(layout.adapter_library("").has_value());
CHECK_FALSE(layout.adapter_library("Godot").has_value()); // majuscule
CHECK_FALSE(layout.adapter_library("godot-1").has_value()); // tiret
CHECK_FALSE(layout.adapter_library("godot ").has_value()); // espace
}
TEST_CASE("executable_name and module_name per platform") {
using namespace sequa::platform;
#if defined(_WIN32)
CHECK(executable_name("sequa-core") == "sequa-core.exe");
CHECK(module_name("sequa_godot_adapter") == "sequa_godot_adapter.dll");
#else
CHECK(executable_name("sequa-core") == "sequa-core");
CHECK(module_name("sequa_godot_adapter") == "sequa_godot_adapter.so");
#endif
}
TEST_CASE("current_executable returns non-empty existing path") {
using namespace sequa::platform;
const auto exe = current_executable();
CHECK_FALSE(exe.empty());
std::error_code ec;
CHECK(std::filesystem::exists(exe, ec));
CHECK(!ec);
}
TEST_CASE("module_path of a function in this test equals current_executable") {
using namespace sequa::platform;
const auto exe = current_executable();
REQUIRE_FALSE(exe.empty());
// Adresse d'une fonction locale pour tester module_path
const auto mod = module_path(reinterpret_cast<const void*>(&testing::function_in_test_executable));
REQUIRE_FALSE(mod.empty());
std::error_code ec;
CHECK(std::filesystem::equivalent(exe, mod, ec));
CHECK(!ec);
}
TEST_CASE("is_valid_engine_name accepts valid names and rejects invalid ones") {
using namespace sequa::platform;
CHECK(is_valid_engine_name("godot"));
CHECK(is_valid_engine_name("debug"));
CHECK(is_valid_engine_name("engine_123"));
CHECK(is_valid_engine_name("a"));
CHECK_FALSE(is_valid_engine_name(""));
CHECK_FALSE(is_valid_engine_name("Godot"));
CHECK_FALSE(is_valid_engine_name("godot-1"));
CHECK_FALSE(is_valid_engine_name("godot "));
CHECK_FALSE(is_valid_engine_name("godot.engine"));
CHECK_FALSE(is_valid_engine_name("../godot"));
CHECK_FALSE(is_valid_engine_name("godot/evil"));
}
TEST_CASE("path_from_utf8 and path_to_utf8 keep non-ASCII characters") {
using namespace sequa::platform;
const std::string utf8 = "C:/Users/Am\xC3\xA9lie/SEQUA"; // « é » en UTF-8
const auto path = path_from_utf8(utf8);
CHECK(path.filename() == path_from_utf8("SEQUA"));
CHECK(path_to_utf8(path) == utf8);
CHECK(path.u8string() == u8"C:/Users/Amélie/SEQUA");
}
TEST_CASE("utf8_arguments drops the program name") {
using namespace sequa::platform;
char program[] = "sequa-layout-tests";
char* argv[] = {program, nullptr};
const auto args = utf8_arguments(1, argv);
#if !defined(_WIN32)
CHECK(args.empty());
#else
// Sous Windows, la vraie ligne de commande du test (arguments de doctest éventuels).
CHECK(args.size() < 64);
#endif
}
} // TEST_SUITE