156 lines
4.7 KiB
C++
156 lines
4.7 KiB
C++
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <yams/compat/dlfcn.h>
|
|
|
|
#include <yams/plugins/abi.h>
|
|
#include <yams/plugins/symbol_extractor_v1.h>
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* @brief RAII wrapper for plugin handle
|
|
*/
|
|
struct PluginHandle {
|
|
void* h{};
|
|
PluginHandle() = default;
|
|
PluginHandle(const PluginHandle&) = delete;
|
|
PluginHandle& operator=(const PluginHandle&) = delete;
|
|
PluginHandle(PluginHandle&& o) noexcept : h(o.h) { o.h = nullptr; }
|
|
PluginHandle& operator=(PluginHandle&& o) noexcept {
|
|
if (this != &o) {
|
|
if (h)
|
|
dlclose(h);
|
|
h = o.h;
|
|
o.h = nullptr;
|
|
}
|
|
return *this;
|
|
}
|
|
~PluginHandle() {
|
|
if (h)
|
|
dlclose(h);
|
|
}
|
|
};
|
|
|
|
void free_result(yams_symbol_extractor_v1* api, yams_symbol_extraction_result_v1* res) {
|
|
if (!api || !res)
|
|
return;
|
|
api->free_result(api->self, res);
|
|
}
|
|
|
|
yams_symbol_extractor_v1* load_plugin(const char* so_path, PluginHandle& ph) {
|
|
ph.h = dlopen(so_path, RTLD_LAZY | RTLD_LOCAL);
|
|
if (!ph.h)
|
|
return nullptr;
|
|
auto getabi = (int (*)())dlsym(ph.h, "yams_plugin_get_abi_version");
|
|
auto getiface =
|
|
(int (*)(const char*, uint32_t, void**))dlsym(ph.h, "yams_plugin_get_interface");
|
|
auto init = (int (*)(const char*, const void*))dlsym(ph.h, "yams_plugin_init");
|
|
if (!getabi || !getiface || !init)
|
|
return nullptr;
|
|
|
|
if (getabi() <= 0)
|
|
return nullptr;
|
|
|
|
if (init("{\n \"languages\": [\"cpp\", \"python\"]\n}", nullptr) != 0)
|
|
return nullptr;
|
|
|
|
void* ptr = nullptr;
|
|
int rc = getiface(YAMS_IFACE_SYMBOL_EXTRACTOR_V1, YAMS_IFACE_SYMBOL_EXTRACTOR_V1_VERSION, &ptr);
|
|
if (rc != 0 || !ptr)
|
|
return nullptr;
|
|
|
|
return reinterpret_cast<yams_symbol_extractor_v1*>(ptr);
|
|
}
|
|
|
|
const char* get_plugin_path() {
|
|
#if defined(_WIN32)
|
|
return "builddir/plugins/symbol_extractor_treesitter/yams_symbol_extractor.dll";
|
|
#elif defined(__APPLE__)
|
|
return "builddir/plugins/symbol_extractor_treesitter/yams_symbol_extractor.dylib";
|
|
#else
|
|
return "builddir/plugins/symbol_extractor_treesitter/yams_symbol_extractor.so";
|
|
#endif
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("Symbol extractor C++ detection", "[plugins][symbol-extractor][cpp]") {
|
|
#if defined(_WIN32)
|
|
SKIP("Symbol extractor plugin not available on Windows in test environment");
|
|
#endif
|
|
|
|
PluginHandle ph;
|
|
auto* api = load_plugin(get_plugin_path(), ph);
|
|
if (!api) {
|
|
SKIP("Symbol extractor plugin not available");
|
|
}
|
|
|
|
SECTION("detects function and class") {
|
|
const char* code = R"CPP(
|
|
class Foo { public: void bar(); };
|
|
int baz(int x) { return x+1; }
|
|
)CPP";
|
|
|
|
yams_symbol_extraction_result_v1* out = nullptr;
|
|
int rc =
|
|
api->extract_symbols(api->self, code, std::strlen(code), "/tmp/test.cpp", "cpp", &out);
|
|
|
|
if (rc != 0 || !out) {
|
|
SKIP("Symbol grammar not available");
|
|
}
|
|
|
|
size_t funcs = 0, classes = 0;
|
|
bool has_scope = false;
|
|
for (size_t i = 0; i < out->symbol_count; ++i) {
|
|
if (out->symbols[i].kind && std::strcmp(out->symbols[i].kind, "function") == 0)
|
|
++funcs;
|
|
if (out->symbols[i].kind && (std::strcmp(out->symbols[i].kind, "class") == 0 ||
|
|
std::strcmp(out->symbols[i].kind, "struct") == 0))
|
|
++classes;
|
|
if (out->symbols[i].scope && std::strlen(out->symbols[i].scope) > 0) {
|
|
has_scope = true;
|
|
}
|
|
}
|
|
CHECK(funcs >= 1u);
|
|
CHECK(classes >= 1u);
|
|
CHECK(has_scope);
|
|
free_result(api, out);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Symbol extractor Python detection", "[plugins][symbol-extractor][python]") {
|
|
#if defined(_WIN32)
|
|
SKIP("Symbol extractor plugin not available on Windows in test environment");
|
|
#endif
|
|
|
|
PluginHandle ph;
|
|
auto* api = load_plugin(get_plugin_path(), ph);
|
|
if (!api) {
|
|
SKIP("Symbol extractor plugin not available");
|
|
}
|
|
|
|
SECTION("detects function") {
|
|
const char* code = R"PY(
|
|
def foo(x):
|
|
return x+1
|
|
)PY";
|
|
|
|
yams_symbol_extraction_result_v1* out = nullptr;
|
|
int rc = api->extract_symbols(api->self, code, std::strlen(code), "/tmp/test.py", "python",
|
|
&out);
|
|
|
|
if (rc != 0 || !out) {
|
|
SKIP("Symbol grammar not available");
|
|
}
|
|
|
|
size_t funcs = 0;
|
|
for (size_t i = 0; i < out->symbol_count; ++i) {
|
|
if (out->symbols[i].kind && std::strcmp(out->symbols[i].kind, "function") == 0)
|
|
++funcs;
|
|
}
|
|
CHECK(funcs >= 1u);
|
|
free_result(api, out);
|
|
}
|
|
}
|