diff options
| author | Joseph Hunkeler <jhunkeler@users.noreply.github.com> | 2026-04-07 15:39:47 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-07 15:39:47 -0400 |
| commit | 5b5abcce09da3ce1cc8fab57e1571d0ff0966f7b (patch) | |
| tree | 66e6e8eb470bcae56885d2ca15b67bfdd0b369f3 /tests | |
| parent | d01b465eee667e8efa4aa7c3088dc7af18ea2ab2 (diff) | |
| parent | 666e1f06d6be94114f4db5b2a4cb75d5e1ecb445 (diff) | |
| download | stasis-5b5abcce09da3ce1cc8fab57e1571d0ff0966f7b.tar.gz | |
Merge pull request #123 from jhunkeler/wheel-parser
Implement Python wheel interface
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_wheel.c | 275 | ||||
| -rw-r--r-- | tests/test_wheelinfo.c | 91 |
2 files changed, 291 insertions, 75 deletions
diff --git a/tests/test_wheel.c b/tests/test_wheel.c index 6818b22..1eabb1b 100644 --- a/tests/test_wheel.c +++ b/tests/test_wheel.c @@ -1,91 +1,216 @@ +#include "delivery.h" #include "testing.h" +#include "str.h" #include "wheel.h" -void test_get_wheel_file() { - struct testcase { - const char *filename; - struct Wheel expected; - }; - struct testcase tc[] = { - { - // Test for "build tags" - .filename = "btpackage-1.2.3-mytag-py2.py3-none-any.whl", - .expected = { - .file_name = "btpackage-1.2.3-mytag-py2.py3-none-any.whl", - .version = "1.2.3", - .distribution = "btpackage", - .build_tag = "mytag", - .platform_tag = "any", - .python_tag = "py2.py3", - .abi_tag = "none", - .path_name = ".", - } - }, - { - // Test for universal package format - .filename = "anypackage-1.2.3-py2.py3-none-any.whl", - .expected = { - .file_name = "anypackage-1.2.3-py2.py3-none-any.whl", - .version = "1.2.3", - .distribution = "anypackage", - .build_tag = NULL, - .platform_tag = "any", - .python_tag = "py2.py3", - .abi_tag = "none", - .path_name = ".", - } - }, - { - // Test for binary package format - .filename = "binpackage-1.2.3-cp311-cp311-linux_x86_64.whl", - .expected = { - .file_name = "binpackage-1.2.3-cp311-cp311-linux_x86_64.whl", - .version = "1.2.3", - .distribution = "binpackage", - .build_tag = NULL, - .platform_tag = "linux_x86_64", - .python_tag = "cp311", - .abi_tag = "cp311", - .path_name = ".", - } - }, - }; +char cwd_start[PATH_MAX]; +char cwd_workspace[PATH_MAX]; +int conda_is_installed = 0; +static char conda_prefix[PATH_MAX] = {0}; +struct Delivery ctx; +static const char *testpkg_filename = "testpkg/dist/testpkg-1.0.0-py3-none-any.whl"; + + +static void test_wheel_package() { + const char *filename = testpkg_filename; + struct Wheel *wheel = NULL; + int state = wheel_package(&wheel, filename); + STASIS_ASSERT(state != WHEEL_PACKAGE_E_ALLOC, "Cannot fail to allocate memory for package structure"); + STASIS_ASSERT(state != WHEEL_PACKAGE_E_GET, "Cannot fail to parse wheel"); + STASIS_ASSERT(state != WHEEL_PACKAGE_E_GET_METADATA, "Cannot fail to read wheel metadata"); + STASIS_ASSERT(state != WHEEL_PACKAGE_E_GET_RECORDS, "Cannot fail reading wheel path records"); + STASIS_ASSERT(state != WHEEL_PACKAGE_E_GET_ENTRY_POINT, "Cannot fail reading wheel entry points"); + STASIS_ASSERT(state == WHEEL_PACKAGE_E_SUCCESS, "Wheel file should be usable"); + STASIS_ASSERT(wheel != NULL, "wheel cannot be NULL"); + STASIS_ASSERT(wheel != NULL, "wheel_package failed to initialize wheel struct"); + STASIS_ASSERT(wheel->record != NULL, "Record cannot be NULL"); + STASIS_ASSERT(wheel->num_record > 0, "Record count cannot be zero"); + STASIS_ASSERT(wheel->tag != NULL, "Package tag list cannot be NULL"); + STASIS_ASSERT(wheel->generator != NULL, "Generator field cannot be NULL"); + STASIS_ASSERT(wheel->top_level != NULL, "Top level directory name cannot be NULL"); + STASIS_ASSERT(wheel->wheel_version != NULL, "Wheel version cannot be NULL"); + STASIS_ASSERT(wheel->metadata != NULL, "Metadata cannot be NULL"); + STASIS_ASSERT(wheel->metadata->name != NULL, "Metadata::name cannot be NULL"); + STASIS_ASSERT(wheel->metadata->version != NULL, "Metadata::version cannot be NULL"); + STASIS_ASSERT(wheel->metadata->metadata_version != NULL, "Metadata::version (of metadata) cannot be NULL"); + + // Implied test against key/id getters. If wheel_show_info segfaults, that functionality is broken. + STASIS_ASSERT(wheel_show_info(wheel) == 0, "wheel_show_info should never fail. Enum(s) might be out of sync with META_*_KEYS array(s)"); + + // Get data from DIST + const struct WheelValue dist_version = wheel_get_value_by_name(wheel, WHEEL_FROM_DIST, "Wheel-Version"); + STASIS_ASSERT(dist_version.type == WHEELVAL_STR, "Wheel dist version value must be a string"); + STASIS_ASSERT_FATAL(dist_version.data != NULL, "Wheel dist version value must not be NULL"); + STASIS_ASSERT(dist_version.count, "Wheel value must be populated"); + + // Get data from METADATA + const struct WheelValue meta_name = wheel_get_value_by_name(wheel, WHEEL_FROM_METADATA, "Metadata-Version"); + STASIS_ASSERT(meta_name.type == WHEELVAL_STR, "Wheel metadata version value must be a string"); + STASIS_ASSERT_FATAL(meta_name.data != NULL, "Wheel metadata version value must not be NULL"); + STASIS_ASSERT(meta_name.count, "Wheel metadata version value must be populated"); + + wheel_package_free(&wheel); + STASIS_ASSERT(wheel == NULL, "wheel struct should be NULL after free"); +} + +static void mock_python_package() { + const char *pyproject_toml_data = "[build-system]\n" + "requires = [\"setuptools >= 77.0.3\"]\n" + "build-backend = \"setuptools.build_meta\"\n" + "\n" + "[project]\n" + "name = \"testpkg\"\n" + "version = \"1.0.0\"\n" + "authors = [{name = \"STASIS Team\", email = \"stasis@not-a-real-domain.tld\"}]\n" + "description = \"A STASIS test package\"\n" + "readme = \"README.md\"\n" + "license = \"BSD-3-Clause\"\n" + "classifiers = [\"Programming Language :: Python :: 3\"]\n" + "\n" + "[project.urls]\n" + "Homepage = \"https://not-a-real-address.tld\"\n" + "Documentation = \"https://not-a-real-address.tld/docs\"\n" + "Repository = \"https://not-a-real-address.tld/repo.git\"\n" + "Issues = \"https://not-a-real-address.tld/tracker\"\n" + "Changelog = \"https://not-a-real-address.tld/changes\"\n"; + const char *readme = "# testpkg\n\nThis is a test package, for testing.\n"; - struct Wheel *doesnotexist = get_wheel_info("doesnotexist", "doesnotexist-0.0.1-py2.py3-none-any.whl", (char *[]) {"not", NULL}, WHEEL_MATCH_ANY); - STASIS_ASSERT(doesnotexist == NULL, "returned non-NULL on error"); - - for (size_t i = 0; i < sizeof(tc) / sizeof(*tc); i++) { - struct testcase *test = &tc[i]; - struct Wheel *wheel = get_wheel_info(".", test->expected.distribution, (char *[]) {(char *) test->expected.version, NULL}, WHEEL_MATCH_ANY); - STASIS_ASSERT(wheel != NULL, "result should not be NULL!"); - STASIS_ASSERT(wheel->file_name && strcmp(wheel->file_name, test->expected.file_name) == 0, "mismatched file name"); - STASIS_ASSERT(wheel->version && strcmp(wheel->version, test->expected.version) == 0, "mismatched version"); - STASIS_ASSERT(wheel->distribution && strcmp(wheel->distribution, test->expected.distribution) == 0, "mismatched distribution (package name)"); - STASIS_ASSERT(wheel->platform_tag && strcmp(wheel->platform_tag, test->expected.platform_tag) == 0, "mismatched platform tag ([platform]_[architecture])"); - STASIS_ASSERT(wheel->python_tag && strcmp(wheel->python_tag, test->expected.python_tag) == 0, "mismatched python tag (python version)"); - STASIS_ASSERT(wheel->abi_tag && strcmp(wheel->abi_tag, test->expected.abi_tag) == 0, "mismatched abi tag (python compatibility version)"); - if (wheel->build_tag) { - STASIS_ASSERT(strcmp(wheel->build_tag, test->expected.build_tag) == 0, - "mismatched build tag (optional arbitrary string)"); - } - wheel_free(&wheel); + mkdir("testpkg", 0755); + mkdir("testpkg/src", 0755); + mkdir("testpkg/src/testpkg", 0755); + if (touch("testpkg/src/testpkg/__init__.py")) { + fprintf(stderr, "unable to write __init__.py"); + exit(1); + } + if (touch("testpkg/README.md")) { + fprintf(stderr, "unable to write README.md"); + exit(1); + } + if (stasis_testing_write_ascii("testpkg/pyproject.toml", pyproject_toml_data)) { + perror("unable to write pyproject.toml"); + exit(1); + } + if (stasis_testing_write_ascii("testpkg/README.md", readme)) { + perror("unable to write readme"); + exit(1); + } + if (pip_exec("install build")) { + fprintf(stderr, "unable to install build tool using pip\n"); + exit(1); + } + if (python_exec("-m build -w ./testpkg")) { + fprintf(stderr, "unable build test package"); + exit(1); } } int main(int argc, char *argv[]) { STASIS_TEST_BEGIN_MAIN(); STASIS_TEST_FUNC *tests[] = { - test_get_wheel_file, + test_wheel_package, }; - // Create mock package directories, and files - mkdir("binpackage", 0755); - touch("binpackage/binpackage-1.2.3-cp311-cp311-linux_x86_64.whl"); - mkdir("anypackage", 0755); - touch("anypackage/anypackage-1.2.3-py2.py3-none-any.whl"); - mkdir("btpackage", 0755); - touch("btpackage/btpackage-1.2.3-mytag-py2.py3-none-any.whl"); + char ws[] = "workspace_XXXXXX"; + if (!mkdtemp(ws)) { + perror("mkdtemp"); + exit(1); + } + getcwd(cwd_start, sizeof(cwd_start) - 1); + mkdir(ws, 0755); + chdir(ws); + getcwd(cwd_workspace, sizeof(cwd_workspace) - 1); + + snprintf(conda_prefix, strlen(cwd_workspace) + strlen("conda") + 2, "%s/conda", cwd_workspace); + + const char *mockinidata = "[meta]\n" + "name = mock\n" + "version = 1.0.0\n" + "rc = 1\n" + "mission = generic\n" + "python = 3.11\n" + "[conda]\n" + "installer_name = Miniforge3\n" + "installer_version = 24.3.0-0\n" + "installer_platform = {{env:STASIS_CONDA_PLATFORM}}\n" + "installer_arch = {{env:STASIS_CONDA_ARCH}}\n" + "installer_baseurl = https://github.com/conda-forge/miniforge/releases/download/24.3.0-0\n"; + stasis_testing_write_ascii("mock.ini", mockinidata); + struct INIFILE *ini = ini_open("mock.ini"); + ctx._stasis_ini_fp.delivery = ini; + ctx._stasis_ini_fp.delivery_path = realpath("mock.ini", NULL); + + const char *sysconfdir = getenv("STASIS_SYSCONFDIR"); + globals.sysconfdir = strdup(sysconfdir ? sysconfdir : STASIS_SYSCONFDIR); + ctx.storage.root = strdup(cwd_workspace); + char *cfgfile = join((char *[]) {globals.sysconfdir, "stasis.ini", NULL}, "/"); + if (!cfgfile) { + perror("unable to create path to global config"); + exit(1); + } + + ctx._stasis_ini_fp.cfg = ini_open(cfgfile); + if (!ctx._stasis_ini_fp.cfg) { + fprintf(stderr, "unable to open config file, %s\n", cfgfile); + exit(1); + } + ctx._stasis_ini_fp.cfg_path = realpath(cfgfile, NULL); + if (!ctx._stasis_ini_fp.cfg_path) { + fprintf(stderr, "unable to determine absolute path of config, %s\n", cfgfile); + exit(1); + } + guard_free(cfgfile); + + setenv("LANG", "C", 1); + if (bootstrap_build_info(&ctx)) { + fprintf(stderr, "bootstrap_build_info failed\n"); + exit(1); + } + if (delivery_init(&ctx, INI_READ_RENDER)) { + fprintf(stderr, "delivery_init failed\n"); + exit(1); + } + + char *install_url = calloc(255, sizeof(install_url)); + delivery_get_conda_installer_url(&ctx, install_url); + delivery_get_conda_installer(&ctx, install_url); + delivery_install_conda(ctx.conda.installer_path, ctx.storage.conda_install_prefix); + guard_free(install_url); + + if (conda_activate(ctx.storage.conda_install_prefix, "base")) { + fprintf(stderr, "conda_activate failed\n"); + exit(1); + } + if (conda_exec("install -y boa conda-build")) { + fprintf(stderr, "conda_exec failed\n"); + exit(1); + } + if (conda_setup_headless()) { + fprintf(stderr, "conda_setup_headless failed\n"); + exit(1); + } + if (conda_env_create("testpkg", ctx.meta.python, NULL)) { + fprintf(stderr, "conda_env_create failed\n"); + exit(1); + } + if (conda_activate(ctx.storage.conda_install_prefix, "testpkg")) { + fprintf(stderr, "conda_activate failed\n"); + exit(1); + } + + mock_python_package(); STASIS_TEST_RUN(tests); + + if (chdir(cwd_start) < 0) { + fprintf(stderr, "chdir failed\n"); + exit(1); + } + if (rmtree(cwd_workspace)) { + perror(cwd_workspace); + } + delivery_free(&ctx); + globals_free(); + STASIS_TEST_END_MAIN(); + }
\ No newline at end of file diff --git a/tests/test_wheelinfo.c b/tests/test_wheelinfo.c new file mode 100644 index 0000000..1abbeac --- /dev/null +++ b/tests/test_wheelinfo.c @@ -0,0 +1,91 @@ +#include "testing.h" +#include "wheelinfo.h" + +void test_wheelinfo_get() { + struct testcase { + const char *filename; + struct WheelInfo expected; + }; + struct testcase tc[] = { + { + // Test for "build tags" + .filename = "btpackage-1.2.3-mytag-py2.py3-none-any.whl", + .expected = { + .file_name = "btpackage-1.2.3-mytag-py2.py3-none-any.whl", + .version = "1.2.3", + .distribution = "btpackage", + .build_tag = "mytag", + .platform_tag = "any", + .python_tag = "py2.py3", + .abi_tag = "none", + .path_name = ".", + } + }, + { + // Test for universal package format + .filename = "anypackage-1.2.3-py2.py3-none-any.whl", + .expected = { + .file_name = "anypackage-1.2.3-py2.py3-none-any.whl", + .version = "1.2.3", + .distribution = "anypackage", + .build_tag = NULL, + .platform_tag = "any", + .python_tag = "py2.py3", + .abi_tag = "none", + .path_name = ".", + } + }, + { + // Test for binary package format + .filename = "binpackage-1.2.3-cp311-cp311-linux_x86_64.whl", + .expected = { + .file_name = "binpackage-1.2.3-cp311-cp311-linux_x86_64.whl", + .version = "1.2.3", + .distribution = "binpackage", + .build_tag = NULL, + .platform_tag = "linux_x86_64", + .python_tag = "cp311", + .abi_tag = "cp311", + .path_name = ".", + } + }, + }; + + struct WheelInfo *doesnotexist = wheelinfo_get("doesnotexist", "doesnotexist-0.0.1-py2.py3-none-any.whl", (char *[]) {"not", NULL}, WHEEL_MATCH_ANY); + STASIS_ASSERT(doesnotexist == NULL, "returned non-NULL on error"); + + for (size_t i = 0; i < sizeof(tc) / sizeof(*tc); i++) { + struct testcase *test = &tc[i]; + struct WheelInfo *wheel = wheelinfo_get(".", test->expected.distribution, (char *[]) {(char *) test->expected.version, NULL}, WHEEL_MATCH_ANY); + STASIS_ASSERT(wheel != NULL, "result should not be NULL!"); + STASIS_ASSERT(wheel->file_name && strcmp(wheel->file_name, test->expected.file_name) == 0, "mismatched file name"); + STASIS_ASSERT(wheel->version && strcmp(wheel->version, test->expected.version) == 0, "mismatched version"); + STASIS_ASSERT(wheel->distribution && strcmp(wheel->distribution, test->expected.distribution) == 0, "mismatched distribution (package name)"); + STASIS_ASSERT(wheel->platform_tag && strcmp(wheel->platform_tag, test->expected.platform_tag) == 0, "mismatched platform tag ([platform]_[architecture])"); + STASIS_ASSERT(wheel->python_tag && strcmp(wheel->python_tag, test->expected.python_tag) == 0, "mismatched python tag (python version)"); + STASIS_ASSERT(wheel->abi_tag && strcmp(wheel->abi_tag, test->expected.abi_tag) == 0, "mismatched abi tag (python compatibility version)"); + if (wheel->build_tag) { + STASIS_ASSERT(strcmp(wheel->build_tag, test->expected.build_tag) == 0, + "mismatched build tag (optional arbitrary string)"); + } + wheelinfo_free(&wheel); + } +} + +int main(int argc, char *argv[]) { + STASIS_TEST_BEGIN_MAIN(); + STASIS_TEST_FUNC *tests[] = { + test_wheelinfo_get, + }; + + // Create mock package directories, and files + mkdir("binpackage", 0755); + touch("binpackage/binpackage-1.2.3-cp311-cp311-linux_x86_64.whl"); + mkdir("anypackage", 0755); + touch("anypackage/anypackage-1.2.3-py2.py3-none-any.whl"); + mkdir("btpackage", 0755); + touch("btpackage/btpackage-1.2.3-mytag-py2.py3-none-any.whl"); + + STASIS_TEST_RUN(tests); + STASIS_TEST_END_MAIN(); +}
\ No newline at end of file |
