Route all module interaction through dispatch
A module is addressed by name and command string, and nothing else. Typed API access handed a caller a pointer into the callee's function table, which meant compiling against that module's struct layout — a build-time dependency between modules that the design does not permit. Removing it also removes the manifest, the table magic constant, and the table size field, which existed only to describe and validate those tables. Load validation is now three steps: reserved contract symbols resolve, the minimum-version handshake passes, and the version and description probes return well-formed values. The contract is four reserved symbols, and a module's interface is the command vocabulary it documents. Documentation is brought in line, and artifacts are named exactly: libdpm-core.so for the library, <dpm/core.h> for the header, the dpm binary for the command-line tool.
This commit is contained in:
95
tests/fixtures/src/bad_magic.cpp
vendored
95
tests/fixtures/src/bad_magic.cpp
vendored
@@ -1,95 +0,0 @@
|
||||
/**
|
||||
* @file bad_magic.cpp
|
||||
* @brief Broken fixture: API table with a wrong magic constant
|
||||
*
|
||||
* Contract-complete, but its declared table opens with garbage instead
|
||||
* of the spec magic. libdpm-core must refuse it at validation step 5.
|
||||
*
|
||||
* Part of the Dark Horse Linux Package Manager (DPM)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
|
||||
struct table_header {
|
||||
uint32_t magic;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct manifest_entry {
|
||||
const char* api_name;
|
||||
int table_version;
|
||||
const char* symbol;
|
||||
};
|
||||
|
||||
struct manifest {
|
||||
uint32_t count;
|
||||
const manifest_entry* entries;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
struct badmagic_api_v1_s {
|
||||
table_header hdr;
|
||||
int (*ping)(void);
|
||||
};
|
||||
|
||||
static int ping(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern const struct badmagic_api_v1_s badmagic_api_v1;
|
||||
const struct badmagic_api_v1_s badmagic_api_v1 = {
|
||||
{ 0xDEADBEEFu, sizeof(struct badmagic_api_v1_s) },
|
||||
ping,
|
||||
};
|
||||
}
|
||||
|
||||
extern "C" const char* dpm_module_version(void)
|
||||
{
|
||||
return "1.0.0";
|
||||
}
|
||||
|
||||
extern "C" const char* dpm_module_description(void)
|
||||
{
|
||||
return "Fixture with a bad table magic.";
|
||||
}
|
||||
|
||||
extern "C" const char* dpm_module_core_min(void)
|
||||
{
|
||||
return "0.1.0";
|
||||
}
|
||||
|
||||
extern "C" const void* dpm_module_manifest(void)
|
||||
{
|
||||
static const manifest_entry entries[] = {
|
||||
{ "badmagic", 1, "badmagic_api_v1" },
|
||||
};
|
||||
static const manifest m = { 1, entries };
|
||||
return &m;
|
||||
}
|
||||
|
||||
extern "C" int dpm_module_execute(void* ctx, const char* command,
|
||||
int argc, char** argv)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)command;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return 0;
|
||||
}
|
||||
23
tests/fixtures/src/bad_version.cpp
vendored
23
tests/fixtures/src/bad_version.cpp
vendored
@@ -20,23 +20,6 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
|
||||
struct manifest_entry {
|
||||
const char* api_name;
|
||||
int table_version;
|
||||
const char* symbol;
|
||||
};
|
||||
|
||||
struct manifest {
|
||||
uint32_t count;
|
||||
const manifest_entry* entries;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" const char* dpm_module_version(void)
|
||||
{
|
||||
return "banana";
|
||||
@@ -52,12 +35,6 @@ extern "C" const char* dpm_module_core_min(void)
|
||||
return "0.1.0";
|
||||
}
|
||||
|
||||
extern "C" const void* dpm_module_manifest(void)
|
||||
{
|
||||
static const manifest m = { 0, nullptr };
|
||||
return &m;
|
||||
}
|
||||
|
||||
extern "C" int dpm_module_execute(void* ctx, const char* command,
|
||||
int argc, char** argv)
|
||||
{
|
||||
|
||||
23
tests/fixtures/src/core_too_new.cpp
vendored
23
tests/fixtures/src/core_too_new.cpp
vendored
@@ -21,23 +21,6 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
|
||||
struct manifest_entry {
|
||||
const char* api_name;
|
||||
int table_version;
|
||||
const char* symbol;
|
||||
};
|
||||
|
||||
struct manifest {
|
||||
uint32_t count;
|
||||
const manifest_entry* entries;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" const char* dpm_module_version(void)
|
||||
{
|
||||
return "1.0.0";
|
||||
@@ -53,12 +36,6 @@ extern "C" const char* dpm_module_core_min(void)
|
||||
return "99.0.0";
|
||||
}
|
||||
|
||||
extern "C" const void* dpm_module_manifest(void)
|
||||
{
|
||||
static const manifest m = { 0, nullptr };
|
||||
return &m;
|
||||
}
|
||||
|
||||
extern "C" int dpm_module_execute(void* ctx, const char* command,
|
||||
int argc, char** argv)
|
||||
{
|
||||
|
||||
61
tests/fixtures/src/good.cpp
vendored
61
tests/fixtures/src/good.cpp
vendored
@@ -3,8 +3,9 @@
|
||||
* @brief Known-good stub module fixture
|
||||
*
|
||||
* A complete, valid DPM module written against the documented module
|
||||
* contract with its own declarations — no libdpm-core headers — exactly
|
||||
* as a standalone module author would. Used to validate the happy path.
|
||||
* contract with its own declarations — no libdpm-core.so headers —
|
||||
* exactly as a standalone module author would. Used to validate the
|
||||
* happy path.
|
||||
*
|
||||
* Part of the Dark Horse Linux Package Manager (DPM)
|
||||
*
|
||||
@@ -21,45 +22,7 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
|
||||
struct table_header {
|
||||
uint32_t magic;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct manifest_entry {
|
||||
const char* api_name;
|
||||
int table_version;
|
||||
const char* symbol;
|
||||
};
|
||||
|
||||
struct manifest {
|
||||
uint32_t count;
|
||||
const manifest_entry* entries;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
struct good_api_v1_s {
|
||||
table_header hdr;
|
||||
int (*ping)(void);
|
||||
};
|
||||
|
||||
static int ping(void)
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern const struct good_api_v1_s good_api_v1;
|
||||
const struct good_api_v1_s good_api_v1 = {
|
||||
{ 0x314D5044u /* "DPM1" */, sizeof(struct good_api_v1_s) },
|
||||
ping,
|
||||
};
|
||||
}
|
||||
#include <cstring>
|
||||
|
||||
extern "C" const char* dpm_module_version(void)
|
||||
{
|
||||
@@ -76,21 +39,17 @@ extern "C" const char* dpm_module_core_min(void)
|
||||
return "0.1.0";
|
||||
}
|
||||
|
||||
extern "C" const void* dpm_module_manifest(void)
|
||||
{
|
||||
static const manifest_entry entries[] = {
|
||||
{ "good", 1, "good_api_v1" },
|
||||
};
|
||||
static const manifest m = { 1, entries };
|
||||
return &m;
|
||||
}
|
||||
|
||||
/* Answers "ping" with 42 so a dispatch round trip is observable, and
|
||||
0 for anything else. */
|
||||
extern "C" int dpm_module_execute(void* ctx, const char* command,
|
||||
int argc, char** argv)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)command;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
if (command && std::strcmp(command, "ping") == 0) {
|
||||
return 42;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
72
tests/fixtures/src/lying_manifest.cpp
vendored
72
tests/fixtures/src/lying_manifest.cpp
vendored
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* @file lying_manifest.cpp
|
||||
* @brief Broken fixture: manifest declares an API it doesn't export
|
||||
*
|
||||
* Contract-complete, but its manifest names a table symbol that does
|
||||
* not exist in the .so. libdpm-core must refuse it at validation step 4.
|
||||
*
|
||||
* Part of the Dark Horse Linux Package Manager (DPM)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
|
||||
struct manifest_entry {
|
||||
const char* api_name;
|
||||
int table_version;
|
||||
const char* symbol;
|
||||
};
|
||||
|
||||
struct manifest {
|
||||
uint32_t count;
|
||||
const manifest_entry* entries;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" const char* dpm_module_version(void)
|
||||
{
|
||||
return "1.0.0";
|
||||
}
|
||||
|
||||
extern "C" const char* dpm_module_description(void)
|
||||
{
|
||||
return "Fixture whose manifest lies.";
|
||||
}
|
||||
|
||||
extern "C" const char* dpm_module_core_min(void)
|
||||
{
|
||||
return "0.1.0";
|
||||
}
|
||||
|
||||
extern "C" const void* dpm_module_manifest(void)
|
||||
{
|
||||
static const manifest_entry entries[] = {
|
||||
{ "ghost", 1, "ghost_api_v1" },
|
||||
};
|
||||
static const manifest m = { 1, entries };
|
||||
return &m;
|
||||
}
|
||||
|
||||
extern "C" int dpm_module_execute(void* ctx, const char* command,
|
||||
int argc, char** argv)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)command;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return 0;
|
||||
}
|
||||
@@ -3,9 +3,8 @@
|
||||
* @brief Test binary: drives libdpm-core through its public C API
|
||||
*
|
||||
* Exercises context lifecycle, configuration, the full load-time
|
||||
* validation matrix against the fixture modules, versioned require,
|
||||
* typed API access, generic dispatch, and enumeration. Exits nonzero
|
||||
* on any failure.
|
||||
* validation matrix against the fixture modules, require, dispatch,
|
||||
* and enumeration. Exits nonzero on any failure.
|
||||
*
|
||||
* @copyright Copyright (c) 2026 SILO GROUP LLC
|
||||
* @author Chris Punches <chris.punches@silogroup.org>
|
||||
@@ -57,12 +56,6 @@ static bool error_contains(dpm_ctx* ctx, const char* needle)
|
||||
return err != nullptr && std::strstr(err, needle) != nullptr;
|
||||
}
|
||||
|
||||
/* Mirror of the good fixture's table layout (consumer-side spec decl). */
|
||||
struct good_api_v1_s {
|
||||
dpm_api_table_header hdr;
|
||||
int (*ping)(void);
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* ---- dpm_open: invalid explicit override refuses ---- */
|
||||
@@ -115,12 +108,6 @@ int main(void)
|
||||
CHECK(dpm_require(ctx, "bad_version") == nullptr);
|
||||
CHECK(error_contains(ctx, "malformed version"));
|
||||
|
||||
CHECK(dpm_require(ctx, "lying_manifest") == nullptr);
|
||||
CHECK(error_contains(ctx, "does not resolve"));
|
||||
|
||||
CHECK(dpm_require(ctx, "bad_magic") == nullptr);
|
||||
CHECK(error_contains(ctx, "magic"));
|
||||
|
||||
CHECK(dpm_require(ctx, "nonexistent") == nullptr);
|
||||
CHECK(error_contains(ctx, "not found"));
|
||||
}
|
||||
@@ -144,21 +131,11 @@ int main(void)
|
||||
CHECK(dpm_module_info_of(ctx, nullptr, &seen) != 0);
|
||||
CHECK(dpm_module_info_of(ctx, good, nullptr) != 0);
|
||||
|
||||
/* typed access */
|
||||
const void* table = dpm_get_api(ctx, good, "good", 1);
|
||||
CHECK(table != nullptr);
|
||||
if (table) {
|
||||
const auto* api = static_cast<const good_api_v1_s*>(table);
|
||||
CHECK(api->hdr.magic == DPM_API_TABLE_MAGIC);
|
||||
CHECK(api->hdr.size == sizeof(good_api_v1_s));
|
||||
CHECK(api->ping() == 42);
|
||||
}
|
||||
|
||||
/* absent api/version pairs are known from the manifest */
|
||||
CHECK(dpm_get_api(ctx, good, "good", 2) == nullptr);
|
||||
CHECK(dpm_get_api(ctx, good, "ghost", 1) == nullptr);
|
||||
|
||||
/* generic dispatch */
|
||||
/* dispatch: the module's return value comes back verbatim, so a
|
||||
command round trip is observable without any compile-time
|
||||
knowledge of the module */
|
||||
CHECK(dpm_execute(ctx, good, "ping", 0, nullptr) == 42);
|
||||
CHECK(dpm_execute(ctx, good, "anything_else", 0, nullptr) == 0);
|
||||
CHECK(dpm_execute(ctx, good, nullptr, 0, nullptr) == 0);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user