- 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.
228 lines
7.0 KiB
C++
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
|