* test(search,daemon,storage): assert behaviour instead of frozen literals Several suites encoded exact internal values that legitimate tuning and ranking changes are allowed to move: SCIENTIFIC-era profile weights and rrfK values, corpus-statistic figures, and hardcoded timeout/cancellation numbers. They failed on every platform after those changes landed, so the real behaviour regressions they should catch were hidden behind noise. Replace the frozen literals with contract assertions: weights are finite, non-negative and normalize, state selection still holds, capability-gated sources stay zeroed, and hybrid results keep matching canonical hash/path sets. Deadlines are not widened and the daemon paths they cover are unchanged. Also add independent corpus and adapter oracles, deterministic collector cancellation and late-result gates, and synchronize ServiceManager shutdown observation, so the tests fail for the behaviour they name. The portability allowlist keeps its two reviewed raw test-env reads; only their line numbers moved. Signed-off-by: trvon <git@trevon.dev> * ci(tests): summarise Meson failures without flooding the log A failing test run printed the whole Meson suite output, so the actual failing test names and their assertion messages were buried and the uploaded logs were dominated by passing-test chatter. Add a bounded reporter that extracts the failing test names plus their surrounding diagnostics, keeps an overall cap on emitted lines, and is unit-tested against representative Meson output. Wire it into the Tests workflow so failures stay readable. Signed-off-by: trvon <git@trevon.dev> * build(deb): validate candidate identity, mapping and upgrade ordering The Debian packaging scripts took filenames and versions on trust, so a misnamed candidate or a wrong package ordering could pass validation while the real install path was never exercised. Fail closed on candidate identity, version and architecture, verify the package-to-source mapping, seed the legacy-only layout, wait on the actual services, and assert install/upgrade/remove ordering for both Debian and RPM in an offline container. Cases that cannot be proven on a host are reported as skipped rather than silently passing. Signed-off-by: trvon <git@trevon.dev> --------- Signed-off-by: trvon <git@trevon.dev>
103 lines
3.7 KiB
C++
103 lines
3.7 KiB
C++
// Plugin export surface: a plugin's dynamic symbol table is its ABI. Only the
|
|
// yams_plugin_* C entry points may be visible; statically linked third-party
|
|
// code (OpenSSL, libcurl, zlib, spdlog, ...) and plugin internals must not be,
|
|
// otherwise a host that loads the plugin with RTLD_GLOBAL, or another library
|
|
// in the process, can bind to the plugin's private copies.
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <yams/compat/dlfcn.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
struct PluginHandle {
|
|
explicit PluginHandle(const char* path) : handle(dlopen(path, RTLD_LAZY | RTLD_LOCAL)) {}
|
|
~PluginHandle() {
|
|
if (handle) {
|
|
dlclose(handle);
|
|
}
|
|
}
|
|
PluginHandle(const PluginHandle&) = delete;
|
|
PluginHandle& operator=(const PluginHandle&) = delete;
|
|
void* handle;
|
|
};
|
|
|
|
const std::vector<std::string> kRequiredEntryPoints = {
|
|
"yams_plugin_get_abi_version",
|
|
"yams_plugin_get_name",
|
|
"yams_plugin_get_version",
|
|
"yams_plugin_get_manifest_json",
|
|
"yams_plugin_init",
|
|
"yams_plugin_shutdown",
|
|
"yams_plugin_get_interface",
|
|
"yams_plugin_get_health_json",
|
|
};
|
|
|
|
void requireEntryPoints(void* handle) {
|
|
for (const auto& name : kRequiredEntryPoints) {
|
|
INFO("entry point " << name);
|
|
CHECK(dlsym(handle, name.c_str()) != nullptr);
|
|
}
|
|
}
|
|
|
|
void requireHidden(void* handle, const std::vector<std::string>& names) {
|
|
#ifndef _WIN32
|
|
// POSIX dlsym also searches dependencies. A system zlib symbol is not an
|
|
// export from the plugin's private copy; compare the defining images.
|
|
auto* entry = dlsym(handle, "yams_plugin_get_abi_version");
|
|
REQUIRE(entry != nullptr);
|
|
Dl_info pluginImage{};
|
|
REQUIRE(dladdr(entry, &pluginImage) != 0);
|
|
#endif
|
|
for (const auto& name : names) {
|
|
INFO("symbol must not be exported by the plugin: " << name);
|
|
auto* symbol = dlsym(handle, name.c_str());
|
|
#ifdef _WIN32
|
|
// GetProcAddress checks the DLL's export table, not its dependencies.
|
|
CHECK(symbol == nullptr);
|
|
#else
|
|
bool definedByPlugin = false;
|
|
std::string definingImage;
|
|
if (symbol != nullptr) {
|
|
Dl_info symbolImage{};
|
|
REQUIRE(dladdr(symbol, &symbolImage) != 0);
|
|
definedByPlugin = symbolImage.dli_fbase == pluginImage.dli_fbase;
|
|
if (symbolImage.dli_fname != nullptr) {
|
|
definingImage = symbolImage.dli_fname;
|
|
}
|
|
}
|
|
CAPTURE(definingImage);
|
|
CHECK_FALSE(definedByPlugin);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#ifdef YAMS_S3_PLUGIN_PATH
|
|
TEST_CASE("S3 plugin exports only the plugin C ABI", "[plugins][s3][abi][catch2]") {
|
|
PluginHandle plugin(YAMS_S3_PLUGIN_PATH);
|
|
REQUIRE(plugin.handle != nullptr);
|
|
requireEntryPoints(plugin.handle);
|
|
// Legacy object-storage factory used by ObjectStoragePluginLoader.
|
|
CHECK(dlsym(plugin.handle, "yams_plugin_create_object_storage") != nullptr);
|
|
CHECK(dlsym(plugin.handle, "yams_plugin_destroy_object_storage") != nullptr);
|
|
// Statically linked libcurl, OpenSSL and zlib stay private.
|
|
requireHidden(plugin.handle, {"curl_easy_init", "curl_global_init", "SSL_new", "SSL_CTX_new",
|
|
"EVP_sha256", "OPENSSL_init_ssl", "deflate", "inflate"});
|
|
}
|
|
#endif
|
|
|
|
#ifdef YAMS_ZYP_PLUGIN_PATH
|
|
TEST_CASE("zyp plugin exports only the plugin C ABI", "[plugins][zyp][abi][catch2]") {
|
|
PluginHandle plugin(YAMS_ZYP_PLUGIN_PATH);
|
|
REQUIRE(plugin.handle != nullptr);
|
|
requireEntryPoints(plugin.handle);
|
|
// The zpdf wrapper and PDF metadata parser are implementation details.
|
|
requireHidden(plugin.handle,
|
|
{"_ZN4yams3zyp10TextBufferD1Ev", "_ZN4yams3zyp8DocumentC1EPv",
|
|
"_ZN4yams3zyp15extractMetadataESt4spanIKhLm18446744073709551615EE"});
|
|
}
|
|
#endif
|