Just a quick-n-dirty test to demonstrate the reworked parser. Obviously not suitable to be committed. Only modified conf.c to allow parsing the config without also initializing alpm. Can be run with `make check` in test/unit/pacman/. --- src/pacman/conf.c | 81 +++++++++++---------- test/unit/pacman/config.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++ test/unit/pacman/tap.h | 123 ++++++++++++++++++++++++++++++++ 3 files changed, 342 insertions(+), 36 deletions(-) create mode 100644 test/unit/pacman/config.c create mode 100644 test/unit/pacman/tap.h diff --git a/src/pacman/conf.c b/src/pacman/conf.c index 5449b92..09daeb4 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -679,6 +679,7 @@ static int register_repo(config_repo_t *repo) */ static int setup_libalpm(void) { + setup_libalpm(); int ret = 0; alpm_errno_t err; alpm_handle_t *handle; @@ -686,25 +687,6 @@ static int setup_libalpm(void) pm_printf(ALPM_LOG_DEBUG, "setup_libalpm called\n"); - /* Configure root path first. If it is set and dbpath/logfile were not - * set, then set those as well to reside under the root. */ - if(config->rootdir) { - char path[PATH_MAX]; - if(!config->dbpath) { - snprintf(path, PATH_MAX, "%s/%s", config->rootdir, DBPATH + 1); - config->dbpath = strdup(path); - } - if(!config->logfile) { - snprintf(path, PATH_MAX, "%s/%s", config->rootdir, LOGFILE + 1); - config->logfile = strdup(path); - } - } else { - config->rootdir = strdup(ROOTDIR); - if(!config->dbpath) { - config->dbpath = strdup(DBPATH); - } - } - /* initialize library */ handle = alpm_initialize(config->rootdir, config->dbpath, &err); if(!handle) { @@ -722,7 +704,6 @@ static int setup_libalpm(void) alpm_option_set_questioncb(handle, cb_question); alpm_option_set_progresscb(handle, cb_progress); - config->logfile = config->logfile ? config->logfile : strdup(LOGFILE); ret = alpm_option_set_logfile(handle, config->logfile); if(ret != 0) { pm_printf(ALPM_LOG_ERROR, _("problem setting logfile '%s' (%s)\n"), @@ -730,9 +711,6 @@ static int setup_libalpm(void) return ret; } - /* Set GnuPG's home directory. This is not relative to rootdir, even if - * rootdir is defined. Reasoning: gpgdir contains configuration data. */ - config->gpgdir = config->gpgdir ? config->gpgdir : strdup(GPGDIR); ret = alpm_option_set_gpgdir(handle, config->gpgdir); if(ret != 0) { pm_printf(ALPM_LOG_ERROR, _("problem setting gpgdir '%s' (%s)\n"), @@ -749,18 +727,12 @@ static int setup_libalpm(void) alpm_option_set_default_siglevel(handle, config->siglevel); -#define SLMERGE(l, m) if(m) { l = (l & (m)) | (config->siglevel & ~(m)); } - SLMERGE(config->localfilesiglevel, config->localfilesiglevel_mask); - SLMERGE(config->remotefilesiglevel, config->remotefilesiglevel_mask); alpm_option_set_local_file_siglevel(handle, config->localfilesiglevel); alpm_option_set_remote_file_siglevel(handle, config->remotefilesiglevel); for(i = config->repos; i; i = alpm_list_next(i)) { - config_repo_t *repo = i->data; - SLMERGE(repo->siglevel, repo->siglevel_mask); - register_repo(repo); + register_repo(i->data); } -#undef SLMERGE if(config->xfercommand) { alpm_option_set_fetchcb(handle, download_with_xfercommand); @@ -980,6 +952,42 @@ static int _parse_directive(const char *file, int linenum, const char *name, } } +static void finalize_config(void) +{ + /* Configure root path first. If it is set and dbpath/logfile were not + * set, then set those as well to reside under the root. */ + if(config->rootdir) { + char path[PATH_MAX]; + if(!config->dbpath) { + snprintf(path, PATH_MAX, "%s/%s", config->rootdir, DBPATH + 1); + config->dbpath = strdup(path); + } + if(!config->logfile) { + snprintf(path, PATH_MAX, "%s/%s", config->rootdir, LOGFILE + 1); + config->logfile = strdup(path); + } + } else { + config->rootdir = strdup(ROOTDIR); + if(!config->dbpath) { + config->dbpath = strdup(DBPATH); + } + } + config->logfile = config->logfile ? config->logfile : strdup(LOGFILE); + /* Set GnuPG's home directory. This is not relative to rootdir, even if + * rootdir is defined. Reasoning: gpgdir contains configuration data. */ + config->gpgdir = config->gpgdir ? config->gpgdir : strdup(GPGDIR); + +#define SLMERGE(l, m) if(m) { l = (l & (m)) | (config->siglevel & ~(m)); } + SLMERGE(config->localfilesiglevel, config->localfilesiglevel_mask); + SLMERGE(config->remotefilesiglevel, config->remotefilesiglevel_mask); + alpm_list_t *i; + for(i = config->repos; i; i = alpm_list_next(i)) { + config_repo_t *repo = i->data; + SLMERGE(repo->siglevel, repo->siglevel_mask); + } +#undef SLMERGE +} + /** Parse a configuration file. * @param file path to the config file * @return 0 on success, non-zero on error @@ -994,12 +1002,13 @@ int parseconfig(const char *file) return ret; } pm_printf(ALPM_LOG_DEBUG, "config: finished parsing %s\n", file); - if((ret = setup_libalpm())) { - return ret; - } - alpm_list_free_inner(config->repos, (alpm_list_fn_free) config_repo_free); - alpm_list_free(config->repos); - config->repos = NULL; + finalize_config(); + /*if((ret = setup_libalpm())) {*/ + /*return ret;*/ + /*}*/ + /*alpm_list_free_inner(config->repos, (alpm_list_fn_free) config_repo_free);*/ + /*alpm_list_free(config->repos);*/ + /*config->repos = NULL;*/ return ret; } diff --git a/test/unit/pacman/config.c b/test/unit/pacman/config.c new file mode 100644 index 0000000..9f5aa54 --- /dev/null +++ b/test/unit/pacman/config.c @@ -0,0 +1,174 @@ +#include <string.h> +#include <stdlib.h> + +#define CONFFILE "/etc/pacman.conf" +#define DBPATH "/var/lib/pacman/" +#define LOGFILE "/var/log/pacman.log" +#define ROOTDIR "/" +#define GPGDIR "/etc/pacman.d/gnupg/" +#define CACHEDIR "/var/cache/pacman/pkg" + +#include "pacman/conf.c" +#include "pacman/ini.c" +#include "pacman/util.c" +#include "pacman/callback.c" + +#include "tap.h" + +int main(int argc, char **argv) { + alpm_list_t *i; + char temp_path1[] = "pu_config_test_XXXXXX"; + char temp_path2[] = "pu_config_test_XXXXXX"; + int fd1 = mkstemp(temp_path1); + int fd2 = mkstemp(temp_path2); + FILE *stream1 = fdopen(fd1, "r+"); + FILE *stream2 = fdopen(fd2, "r+"); + fprintf(stream2, + "\n" + "SigLevel = DatabaseNever\n" + ""); + fprintf(stream1, + "\n" + "[options]\n" + "#RootDir = /root1\n" + "RootDir = /root2\n" + "DBPath = /dbpath1/ \n" + "DBPath = /dbpath2/ \n" + "CacheDir=/cachedir\n" + "GPGDir =gpgdir\n" + "LogFile= /logfile #ignore me\n" + " HoldPkg = holdpkga holdpkgb \n" + "IgnorePkg = ignorepkga\n" + "IgnorePkg = ignorepkgb\n" + " IgnoreGroup = ignoregroupa ignoregroupb \n" + "Architecture = i686\n" + "NoUpgrade = /tmp/noupgrade*\n" + "NoExtract = /tmp/noextract*\n" + "CleanMethod = KeepInstalled KeepCurrent\n" + "UseSyslog\n" + "Color\n" + "UseDelta\n" + "TotalDownload\n" + "CheckSpace\n" + "VerbosePkgLists\n" + "XferCommand = xfercommand\n" + + "LocalFileSigLevel = PackageNever\n" + "SigLevel = PackageOptional DatabaseRequired PackageTrustedOnly\n" + "RemoteFileSigLevel = PackageTrustAll\n" + + "[core]\n" + "SigLevel = PackageRequired\n" + "Server = $repo:$arch\n" + + "[extra]\n" + "Include = %s\n" + "", + temp_path2); + + fclose(stream1); + fclose(stream2); + + tap_plan(37); + + if((config = config_new()) == NULL) { + tap_bail("config_new failed"); + } + parseconfig(temp_path1); + + tap_is_str(config->rootdir, "/root2", "RootDir"); + tap_is_str(config->dbpath, "/dbpath1/", "DBPath"); + tap_is_str(config->gpgdir, "gpgdir", "DPGDir"); + tap_is_str(config->logfile, "/logfile", "LogFile"); + tap_is_str(config->arch, "i686", "Arch"); + tap_is_str(config->xfercommand, "xfercommand", "Include XferCommand"); + + tap_ok(config->usesyslog, "UseSyslog"); + tap_ok(config->color, "Color"); + tap_ok(config->totaldownload, "TotalDownload"); + tap_ok(config->checkspace, "CheckSpace"); + tap_ok(config->verbosepkglists, "VerbosePkgLists"); + +#define is_list_exhausted(l, name) do { \ + tap_ok(l == NULL, name " exhausted"); \ + if(l) { \ + tap_diag("remaining elements:"); \ + while(l) { \ + tap_diag(l->data); \ + l = alpm_list_next(l); \ + } \ + } \ + } while(0) + + +#define is_str_list(l, str, desc) do { \ + if(l) { \ + tap_is_str(l->data, str, desc); \ + l = alpm_list_next(l); \ + } else { \ + tap_ok(l != NULL, desc); \ + } \ + } while(0) + + i = config->ignorepkg; + is_str_list(i, "ignorepkga", "IgnorePkg a"); + is_str_list(i, "ignorepkgb", "IgnorePkg b"); + is_list_exhausted(i, "ignorepkg"); + + i = config->ignoregrp; + is_str_list(i, "ignoregroupa", "IgnoreGroup a"); + is_str_list(i, "ignoregroupb", "IgnoreGroup b"); + is_list_exhausted(i, "ignoregroup"); + + i = config->noupgrade; + is_str_list(i, "/tmp/noupgrade*", "NoUpgrade"); + is_list_exhausted(i, "noupgrade"); + + i = config->noextract; + is_str_list(i, "/tmp/noextract*", "NoExtract"); + is_list_exhausted(i, "noextract"); + + i = config->cachedirs; + is_str_list(i, "/cachedir", "CacheDir"); + is_list_exhausted(i, "cachedir"); + + i = config->holdpkg; + is_str_list(i, "holdpkga", "HoldPkg a"); + is_str_list(i, "holdpkgb", "HoldPkg b"); + is_list_exhausted(i, "holdpkgs"); +#undef tap_is_str_list + + tap_is_float(config->deltaratio, 0.7, 0.0001, "UseDelta (default)"); + + alpm_siglevel_t base = ALPM_SIG_PACKAGE + | ALPM_SIG_PACKAGE_OPTIONAL + | ALPM_SIG_DATABASE; + alpm_siglevel_t local = base & ~(ALPM_SIG_PACKAGE); + alpm_siglevel_t remote = base + | ALPM_SIG_PACKAGE_MARGINAL_OK + | ALPM_SIG_PACKAGE_UNKNOWN_OK; + alpm_siglevel_t core = base & ~(ALPM_SIG_PACKAGE_OPTIONAL); + alpm_siglevel_t extra = base & ~(ALPM_SIG_DATABASE); + + tap_is_int(config->siglevel, base, "SigLevel"); + tap_is_int(config->localfilesiglevel, local, "LocalFileSigLevel"); + tap_is_int(config->remotefilesiglevel, remote, "RemoteFileSigLevel"); + + i = config->repos; + config_repo_t *repo = i->data; + tap_is_str(repo->name, "core", "core name"); + tap_is_int(repo->siglevel, core, "core siglevel"); + tap_is_str(repo->servers->data, "$repo:$arch", "core server"); + tap_ok(repo->servers->next == NULL, "core server count"); + + i = alpm_list_next(i); + repo = i->data; + tap_is_str(repo->name, "extra", "extra name"); + tap_is_int(repo->siglevel, extra, "extra siglevel"); + tap_ok(repo->servers == NULL, "extra server count"); + + unlink(temp_path1); + unlink(temp_path2); + + return tap_tests_failed(); +} diff --git a/test/unit/pacman/tap.h b/test/unit/pacman/tap.h new file mode 100644 index 0000000..e670909 --- /dev/null +++ b/test/unit/pacman/tap.h @@ -0,0 +1,123 @@ +#include <math.h> +#include <stdarg.h> + +int _tap_tests_run = 0; +int _tap_tests_failed = 0; + +const char *_tap_todo = NULL; + +void tap_todo(const char *reason) +{ + _tap_todo = reason; +} + +void tap_ok(int success, const char *description, ...) +{ + const char *result; + if(success) { + result = "ok"; + if(_tap_todo) ++_tap_tests_failed; + } else { + result = "not ok"; + if(!_tap_todo) ++_tap_tests_failed; + } + + printf("%s %d", result, ++_tap_tests_run); + + if(description) { + va_list args; + fputs(" - ", stdout); + va_start(args, description); + vprintf(description, args); + va_end(args); + } + + if(_tap_todo) { + fputs(" # TODO", stdout); + if(*_tap_todo) { + fputc(' ', stdout); + fputs(_tap_todo, stdout); + } + } + + fputc('\n', stdout); +} + +void tap_bail(const char *format, ...) +{ + va_list args; + fputs("Bail out! ", stdout); + va_start(args, format); + vprintf(format, args); + va_end(args); + fputc('\n', stdout); +} + +void tap_diag(const char *format, ...) +{ + va_list args; + fputs(" # ", stdout); + va_start(args, format); + vprintf(format, args); + va_end(args); + fputc('\n', stdout); +} + +void tap_is_float(float got, float expected, float delta, + const char *description) +{ + float diff = fabs(expected - got); + int match = diff < delta; + tap_ok(match, description); + if(!match) { + tap_diag("expected '%f'", expected); + tap_diag("got '%f'", got); + tap_diag("delta '%f'", diff); + tap_diag("allowed '%f'", delta); + } +} + +void tap_is_int(int got, int expected, const char *description) +{ + int match = got == expected; + tap_ok(match, description); + if(!match) { + tap_diag("expected '%d'", expected); + tap_diag("got '%d'", got); + } +} + +void tap_is_str(const char *got, const char *expected, const char *description) +{ + int match; + if(got && expected) { + match = (strcmp(got, expected) == 0); + } else { + match = (got == expected); + } + tap_ok(match, description); + if(!match) { + tap_diag("expected '%s'", expected); + tap_diag("got '%s'", got); + } +} + +void tap_plan(int test_count) +{ + printf("1..%d\n", test_count); +} + +void tap_done_testing(void) +{ + tap_plan(_tap_tests_run); +} + +int tap_tests_run(void) +{ + return _tap_tests_run; +} + +int tap_tests_failed(void) +{ + return _tap_tests_failed; +} -- 1.9.2