diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 18:37:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 18:37:14 +0000 |
commit | ea648e70a989cca190cd7403fe892fd2dcc290b4 (patch) | |
tree | e2b6b1c647da68b0d4d66082835e256eb30970e8 /unit/atf-src/atf-c/detail | |
parent | Initial commit. (diff) | |
download | bind9-upstream.tar.xz bind9-upstream.zip |
Adding upstream version 1:9.11.5.P4+dfsg.upstream/1%9.11.5.P4+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
35 files changed, 9626 insertions, 0 deletions
diff --git a/unit/atf-src/atf-c/detail/Atffile b/unit/atf-src/atf-c/detail/Atffile new file mode 100644 index 0000000..5fd8593 --- /dev/null +++ b/unit/atf-src/atf-c/detail/Atffile @@ -0,0 +1,13 @@ +Content-Type: application/X-atf-atffile; version="1" + +prop: test-suite = atf + +tp: dynstr_test +tp: env_test +tp: fs_test +tp: list_test +tp: map_test +tp: process_test +tp: sanity_test +tp: text_test +tp: user_test diff --git a/unit/atf-src/atf-c/detail/Kyuafile b/unit/atf-src/atf-c/detail/Kyuafile new file mode 100644 index 0000000..bb741da --- /dev/null +++ b/unit/atf-src/atf-c/detail/Kyuafile @@ -0,0 +1,13 @@ +syntax("kyuafile", 1) + +test_suite("atf") + +atf_test_program{name="dynstr_test"} +atf_test_program{name="env_test"} +atf_test_program{name="fs_test"} +atf_test_program{name="list_test"} +atf_test_program{name="map_test"} +atf_test_program{name="process_test"} +atf_test_program{name="sanity_test"} +atf_test_program{name="text_test"} +atf_test_program{name="user_test"} diff --git a/unit/atf-src/atf-c/detail/Makefile.am.inc b/unit/atf-src/atf-c/detail/Makefile.am.inc new file mode 100644 index 0000000..ad45602 --- /dev/null +++ b/unit/atf-src/atf-c/detail/Makefile.am.inc @@ -0,0 +1,99 @@ +# Copyright (c) 2007 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND +# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +libatf_c_la_SOURCES += atf-c/detail/dynstr.c \ + atf-c/detail/dynstr.h \ + atf-c/detail/env.c \ + atf-c/detail/env.h \ + atf-c/detail/fs.c \ + atf-c/detail/fs.h \ + atf-c/detail/list.c \ + atf-c/detail/list.h \ + atf-c/detail/map.c \ + atf-c/detail/map.h \ + atf-c/detail/process.c \ + atf-c/detail/process.h \ + atf-c/detail/sanity.c \ + atf-c/detail/sanity.h \ + atf-c/detail/text.c \ + atf-c/detail/text.h \ + atf-c/detail/tp_main.c \ + atf-c/detail/user.c \ + atf-c/detail/user.h + +tests_atf_c_detail_DATA = atf-c/detail/Atffile \ + atf-c/detail/Kyuafile +tests_atf_c_detaildir = $(pkgtestsdir)/atf-c/detail +EXTRA_DIST += $(tests_atf_c_detail_DATA) + +noinst_LTLIBRARIES += atf-c/detail/libtest_helpers.la +atf_c_detail_libtest_helpers_la_SOURCES = atf-c/detail/test_helpers.c \ + atf-c/detail/test_helpers.h +atf_c_detail_libtest_helpers_la_CPPFLAGS = -I$(srcdir)/atf-c \ + -DATF_INCLUDEDIR=\"$(includedir)\" + +tests_atf_c_detail_PROGRAMS = atf-c/detail/dynstr_test +atf_c_detail_dynstr_test_SOURCES = atf-c/detail/dynstr_test.c +atf_c_detail_dynstr_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/env_test +atf_c_detail_env_test_SOURCES = atf-c/detail/env_test.c +atf_c_detail_env_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/fs_test +atf_c_detail_fs_test_SOURCES = atf-c/detail/fs_test.c +atf_c_detail_fs_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/list_test +atf_c_detail_list_test_SOURCES = atf-c/detail/list_test.c +atf_c_detail_list_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/map_test +atf_c_detail_map_test_SOURCES = atf-c/detail/map_test.c +atf_c_detail_map_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/process_helpers +atf_c_detail_process_helpers_SOURCES = atf-c/detail/process_helpers.c + +tests_atf_c_detail_PROGRAMS += atf-c/detail/process_test +atf_c_detail_process_test_SOURCES = atf-c/detail/process_test.c +atf_c_detail_process_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/sanity_test +atf_c_detail_sanity_test_SOURCES = atf-c/detail/sanity_test.c +atf_c_detail_sanity_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/text_test +atf_c_detail_text_test_SOURCES = atf-c/detail/text_test.c +atf_c_detail_text_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/user_test +atf_c_detail_user_test_SOURCES = atf-c/detail/user_test.c +atf_c_detail_user_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/version_helper +atf_c_detail_version_helper_SOURCES = atf-c/detail/version_helper.c + +# vim: syntax=make:noexpandtab:shiftwidth=8:softtabstop=8 diff --git a/unit/atf-src/atf-c/detail/dynstr.c b/unit/atf-src/atf-c/detail/dynstr.c new file mode 100644 index 0000000..424a1c9 --- /dev/null +++ b/unit/atf-src/atf-c/detail/dynstr.c @@ -0,0 +1,394 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/dynstr.h" + +#include <errno.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/text.h" +#include "atf-c/error.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +atf_error_t +resize(atf_dynstr_t *ad, size_t newsize) +{ + char *newdata; + atf_error_t err; + + PRE(newsize > ad->m_datasize); + + newdata = (char *)malloc(newsize); + if (newdata == NULL) { + err = atf_no_memory_error(); + } else { + strcpy(newdata, ad->m_data); + free(ad->m_data); + ad->m_data = newdata; + ad->m_datasize = newsize; + err = atf_no_error(); + } + + return err; +} + +static +atf_error_t +prepend_or_append(atf_dynstr_t *ad, const char *fmt, va_list ap, + bool prepend) +{ + char *aux; + atf_error_t err; + size_t newlen; + va_list ap2; + + va_copy(ap2, ap); + err = atf_text_format_ap(&aux, fmt, ap2); + va_end(ap2); + if (atf_is_error(err)) + goto out; + newlen = ad->m_length + strlen(aux); + + if (newlen + sizeof(char) > ad->m_datasize) { + err = resize(ad, newlen + sizeof(char)); + if (atf_is_error(err)) + goto out_free; + } + + if (prepend) { + memmove(ad->m_data + strlen(aux), ad->m_data, ad->m_length + 1); + memcpy(ad->m_data, aux, strlen(aux)); + } else + strcpy(ad->m_data + ad->m_length, aux); + ad->m_length = newlen; + err = atf_no_error(); + +out_free: + free(aux); +out: + return err; +} + +/* --------------------------------------------------------------------- + * The "atf_dynstr" type. + * --------------------------------------------------------------------- */ + +/* + * Constants. + */ + +const size_t atf_dynstr_npos = SIZE_MAX; + +/* + * Constructors and destructors. + */ + +atf_error_t +atf_dynstr_init(atf_dynstr_t *ad) +{ + atf_error_t err; + + ad->m_data = (char *)malloc(sizeof(char)); + if (ad->m_data == NULL) { + err = atf_no_memory_error(); + goto out; + } + + ad->m_data[0] = '\0'; + ad->m_datasize = 1; + ad->m_length = 0; + err = atf_no_error(); + +out: + return err; +} + +atf_error_t +atf_dynstr_init_ap(atf_dynstr_t *ad, const char *fmt, va_list ap) +{ + atf_error_t err; + + ad->m_datasize = strlen(fmt) + 1; + ad->m_length = 0; + + do { + va_list ap2; + int ret; + + ad->m_datasize *= 2; + ad->m_data = (char *)malloc(ad->m_datasize); + if (ad->m_data == NULL) { + err = atf_no_memory_error(); + goto out; + } + + va_copy(ap2, ap); + ret = vsnprintf(ad->m_data, ad->m_datasize, fmt, ap2); + va_end(ap2); + if (ret < 0) { + free(ad->m_data); + err = atf_libc_error(errno, "Cannot format string"); + goto out; + } + + INV(ret >= 0); + if ((size_t)ret >= ad->m_datasize) { + free(ad->m_data); + ad->m_data = NULL; + } + ad->m_length = ret; + } while (ad->m_length >= ad->m_datasize); + + err = atf_no_error(); +out: + POST(atf_is_error(err) || ad->m_data != NULL); + return err; +} + +atf_error_t +atf_dynstr_init_fmt(atf_dynstr_t *ad, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_dynstr_init_ap(ad, fmt, ap); + va_end(ap); + + return err; +} + +atf_error_t +atf_dynstr_init_raw(atf_dynstr_t *ad, const void *mem, size_t memlen) +{ + atf_error_t err; + + if (memlen >= SIZE_MAX - 1) { + err = atf_no_memory_error(); + goto out; + } + + ad->m_data = (char *)malloc(memlen + 1); + if (ad->m_data == NULL) { + err = atf_no_memory_error(); + goto out; + } + + ad->m_datasize = memlen + 1; + memcpy(ad->m_data, mem, memlen); + ad->m_data[memlen] = '\0'; + ad->m_length = strlen(ad->m_data); + INV(ad->m_length <= memlen); + err = atf_no_error(); + +out: + return err; +} + +atf_error_t +atf_dynstr_init_rep(atf_dynstr_t *ad, size_t len, char ch) +{ + atf_error_t err; + + if (len == SIZE_MAX) { + err = atf_no_memory_error(); + goto out; + } + + ad->m_datasize = (len + 1) * sizeof(char); + ad->m_data = (char *)malloc(ad->m_datasize); + if (ad->m_data == NULL) { + err = atf_no_memory_error(); + goto out; + } + + memset(ad->m_data, ch, len); + ad->m_data[len] = '\0'; + ad->m_length = len; + err = atf_no_error(); + +out: + return err; +} + +atf_error_t +atf_dynstr_init_substr(atf_dynstr_t *ad, const atf_dynstr_t *src, + size_t beg, size_t end) +{ + if (beg > src->m_length) + beg = src->m_length; + + if (end == atf_dynstr_npos || end > src->m_length) + end = src->m_length; + + return atf_dynstr_init_raw(ad, src->m_data + beg, end - beg); +} + +atf_error_t +atf_dynstr_copy(atf_dynstr_t *dest, const atf_dynstr_t *src) +{ + atf_error_t err; + + dest->m_data = (char *)malloc(src->m_datasize); + if (dest->m_data == NULL) + err = atf_no_memory_error(); + else { + memcpy(dest->m_data, src->m_data, src->m_datasize); + dest->m_datasize = src->m_datasize; + dest->m_length = src->m_length; + err = atf_no_error(); + } + + return err; +} + +void +atf_dynstr_fini(atf_dynstr_t *ad) +{ + INV(ad->m_data != NULL); + free(ad->m_data); +} + +char * +atf_dynstr_fini_disown(atf_dynstr_t *ad) +{ + INV(ad->m_data != NULL); + return ad->m_data; +} + +/* + * Getters. + */ + +const char * +atf_dynstr_cstring(const atf_dynstr_t *ad) +{ + return ad->m_data; +} + +size_t +atf_dynstr_length(const atf_dynstr_t *ad) +{ + return ad->m_length; +} + +size_t +atf_dynstr_rfind_ch(const atf_dynstr_t *ad, char ch) +{ + size_t pos; + + for (pos = ad->m_length; pos > 0 && ad->m_data[pos - 1] != ch; pos--) + ; + + return pos == 0 ? atf_dynstr_npos : pos - 1; +} + +/* + * Modifiers. + */ + +atf_error_t +atf_dynstr_append_ap(atf_dynstr_t *ad, const char *fmt, va_list ap) +{ + atf_error_t err; + va_list ap2; + + va_copy(ap2, ap); + err = prepend_or_append(ad, fmt, ap2, false); + va_end(ap2); + + return err; +} + +atf_error_t +atf_dynstr_append_fmt(atf_dynstr_t *ad, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = prepend_or_append(ad, fmt, ap, false); + va_end(ap); + + return err; +} + +void +atf_dynstr_clear(atf_dynstr_t *ad) +{ + ad->m_data[0] = '\0'; + ad->m_length = 0; +} + +atf_error_t +atf_dynstr_prepend_ap(atf_dynstr_t *ad, const char *fmt, va_list ap) +{ + atf_error_t err; + va_list ap2; + + va_copy(ap2, ap); + err = prepend_or_append(ad, fmt, ap2, true); + va_end(ap2); + + return err; +} + +atf_error_t +atf_dynstr_prepend_fmt(atf_dynstr_t *ad, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = prepend_or_append(ad, fmt, ap, true); + va_end(ap); + + return err; +} + +/* + * Operators. + */ + +bool +atf_equal_dynstr_cstring(const atf_dynstr_t *ad, const char *str) +{ + return strcmp(ad->m_data, str) == 0; +} + +bool +atf_equal_dynstr_dynstr(const atf_dynstr_t *s1, const atf_dynstr_t *s2) +{ + return strcmp(s1->m_data, s2->m_data) == 0; +} diff --git a/unit/atf-src/atf-c/detail/dynstr.h b/unit/atf-src/atf-c/detail/dynstr.h new file mode 100644 index 0000000..60b8e42 --- /dev/null +++ b/unit/atf-src/atf-c/detail/dynstr.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_DYNSTR_H) +#define ATF_C_DETAIL_DYNSTR_H + +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> + +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_dynstr" type. + * --------------------------------------------------------------------- */ + +struct atf_dynstr { + char *m_data; + size_t m_datasize; + size_t m_length; +}; +typedef struct atf_dynstr atf_dynstr_t; + +/* Constants */ +extern const size_t atf_dynstr_npos; + +/* Constructors and destructors */ +atf_error_t atf_dynstr_init(atf_dynstr_t *); +atf_error_t atf_dynstr_init_ap(atf_dynstr_t *, const char *, va_list); +atf_error_t atf_dynstr_init_fmt(atf_dynstr_t *, const char *, ...); +atf_error_t atf_dynstr_init_raw(atf_dynstr_t *, const void *, size_t); +atf_error_t atf_dynstr_init_rep(atf_dynstr_t *, size_t, char); +atf_error_t atf_dynstr_init_substr(atf_dynstr_t *, const atf_dynstr_t *, + size_t, size_t); +atf_error_t atf_dynstr_copy(atf_dynstr_t *, const atf_dynstr_t *); +void atf_dynstr_fini(atf_dynstr_t *); +char *atf_dynstr_fini_disown(atf_dynstr_t *); + +/* Getters */ +const char *atf_dynstr_cstring(const atf_dynstr_t *); +size_t atf_dynstr_length(const atf_dynstr_t *); +size_t atf_dynstr_rfind_ch(const atf_dynstr_t *, char); + +/* Modifiers */ +atf_error_t atf_dynstr_append_ap(atf_dynstr_t *, const char *, va_list); +atf_error_t atf_dynstr_append_fmt(atf_dynstr_t *, const char *, ...); +void atf_dynstr_clear(atf_dynstr_t *); +atf_error_t atf_dynstr_prepend_ap(atf_dynstr_t *, const char *, va_list); +atf_error_t atf_dynstr_prepend_fmt(atf_dynstr_t *, const char *, ...); + +/* Operators */ +bool atf_equal_dynstr_cstring(const atf_dynstr_t *, const char *); +bool atf_equal_dynstr_dynstr(const atf_dynstr_t *, const atf_dynstr_t *); + +#endif /* !defined(ATF_C_DETAIL_DYNSTR_H) */ diff --git a/unit/atf-src/atf-c/detail/dynstr_test.c b/unit/atf-src/atf-c/detail/dynstr_test.c new file mode 100644 index 0000000..2512752 --- /dev/null +++ b/unit/atf-src/atf-c/detail/dynstr_test.c @@ -0,0 +1,634 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/dynstr.h" + +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Tests for the "atf_dynstr" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors and destructors. + */ + +ATF_TC(init); +ATF_TC_HEAD(init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the empty constructor"); +} +ATF_TC_BODY(init, tc) +{ + atf_dynstr_t str; + + RE(atf_dynstr_init(&str)); + ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); +} + +static +void +init_fmt(atf_dynstr_t *str, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + RE(atf_dynstr_init_ap(str, fmt, ap)); + va_end(ap); +} + +ATF_TC(init_ap); +ATF_TC_HEAD(init_ap, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the formatted constructor using " + "a va_list argument"); +} +ATF_TC_BODY(init_ap, tc) +{ + atf_dynstr_t str; + + init_fmt(&str, "String 1"); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0); + atf_dynstr_fini(&str); + + init_fmt(&str, "String %d", 2); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0); + atf_dynstr_fini(&str); + + init_fmt(&str, "%s %d", "String", 3); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 3") == 0); + atf_dynstr_fini(&str); + + init_fmt(&str, "%s%s%s%s%s%s%s", "This ", "should ", "be ", "a ", + "large ", "string ", "aaaabbbbccccdddd"); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), + "This should be a large string " + "aaaabbbbccccdddd") == 0); + atf_dynstr_fini(&str); +} + +ATF_TC(init_fmt); +ATF_TC_HEAD(init_fmt, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the formatted constructor using " + "a variable list of parameters"); +} +ATF_TC_BODY(init_fmt, tc) +{ + atf_dynstr_t str; + + RE(atf_dynstr_init_fmt(&str, "String 1")); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_fmt(&str, "String %d", 2)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_fmt(&str, "%s %d", "String", 3)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 3") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_fmt(&str, "%s%s%s%s%s%s%s", "This ", "should ", + "be ", "a ", "large ", "string ", + "aaaabbbbccccdddd")); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), + "This should be a large string " + "aaaabbbbccccdddd") == 0); + atf_dynstr_fini(&str); +} + +ATF_TC(init_raw); +ATF_TC_HEAD(init_raw, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of a string " + "using a raw memory pointer"); +} +ATF_TC_BODY(init_raw, tc) +{ + const char *src = "String 1, String 2"; + atf_dynstr_t str; + + RE(atf_dynstr_init_raw(&str, src, 0)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_raw(&str, src, 8)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_raw(&str, src + 10, 8)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_raw(&str, "String\0Lost", 11)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String") == 0); + atf_dynstr_fini(&str); + + { + atf_error_t err = atf_dynstr_init_raw(&str, "NULL", SIZE_MAX - 1); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + atf_error_free(err); + } +} + +ATF_TC(init_rep); +ATF_TC_HEAD(init_rep, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of a string by " + "repeating characters"); +} +ATF_TC_BODY(init_rep, tc) +{ + const size_t maxlen = 8192; + char buf[maxlen + 1]; + size_t i; + + buf[0] = '\0'; + + for (i = 0; i < maxlen; i++) { + atf_dynstr_t str; + + RE(atf_dynstr_init_rep(&str, i, 'a')); + + if (strcmp(atf_dynstr_cstring(&str), buf) != 0) { + fprintf(stderr, "Failed at iteration %zd\n", i); + atf_tc_fail("Failed to construct dynstr by repeating %zd " + "times the '%c' character", i, 'a'); + } + + atf_dynstr_fini(&str); + + strcat(buf, "a"); + } + + { + atf_dynstr_t str; + atf_error_t err; + + err = atf_dynstr_init_rep(&str, SIZE_MAX, 'a'); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + atf_error_free(err); + + err = atf_dynstr_init_rep(&str, SIZE_MAX - 1, 'a'); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + atf_error_free(err); + } +} + +ATF_TC(init_substr); +ATF_TC_HEAD(init_substr, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of a string " + "using a substring of another one"); +} +ATF_TC_BODY(init_substr, tc) +{ + atf_dynstr_t src; + atf_dynstr_t str; + + RE(atf_dynstr_init_fmt(&src, "Str 1, Str 2")); + + RE(atf_dynstr_init_substr(&str, &src, 0, 0)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_substr(&str, &src, 0, atf_dynstr_npos)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1, Str 2") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_substr(&str, &src, 0, 100)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1, Str 2") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_substr(&str, &src, 0, 5)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_substr(&str, &src, 100, atf_dynstr_npos)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_substr(&str, &src, 7, atf_dynstr_npos)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 2") == 0); + atf_dynstr_fini(&str); + + atf_dynstr_fini(&src); +} + +ATF_TC(copy); +ATF_TC_HEAD(copy, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_dynstr_copy constructor"); +} +ATF_TC_BODY(copy, tc) +{ + atf_dynstr_t str, str2; + + RE(atf_dynstr_init_fmt(&str, "Test string")); + RE(atf_dynstr_copy(&str2, &str)); + + ATF_REQUIRE(atf_equal_dynstr_dynstr(&str, &str2)); + + RE(atf_dynstr_append_fmt(&str2, " non-shared text")); + + ATF_REQUIRE(!atf_equal_dynstr_dynstr(&str, &str2)); + + atf_dynstr_fini(&str2); + atf_dynstr_fini(&str); +} + +ATF_TC(fini_disown); +ATF_TC_HEAD(fini_disown, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks grabbing ownership of the " + "internal plain C string"); +} +ATF_TC_BODY(fini_disown, tc) +{ + const char *cstr; + char *cstr2; + atf_dynstr_t str; + + RE(atf_dynstr_init_fmt(&str, "Test string 1")); + cstr = atf_dynstr_cstring(&str); + cstr2 = atf_dynstr_fini_disown(&str); + + ATF_REQUIRE_EQ(cstr, cstr2); + free(cstr2); +} + +/* + * Getters. + */ + +ATF_TC(cstring); +ATF_TC_HEAD(cstring, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the method to obtain a plain C " + "string"); +} +ATF_TC_BODY(cstring, tc) +{ + const char *cstr; + atf_dynstr_t str; + + RE(atf_dynstr_init_fmt(&str, "Test string 1")); + cstr = atf_dynstr_cstring(&str); + ATF_REQUIRE(cstr != NULL); + ATF_REQUIRE(strcmp(cstr, "Test string 1") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_fmt(&str, "Test string 2")); + cstr = atf_dynstr_cstring(&str); + ATF_REQUIRE(cstr != NULL); + ATF_REQUIRE(strcmp(cstr, "Test string 2") == 0); + atf_dynstr_fini(&str); +} + +ATF_TC(length); +ATF_TC_HEAD(length, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the method to obtain the length"); +} +ATF_TC_BODY(length, tc) +{ + size_t i; + + for (i = 0; i < 8192; i++) { + atf_dynstr_t str; + RE(atf_dynstr_init_rep(&str, i, 'a')); + ATF_REQUIRE_EQ(atf_dynstr_length(&str), i); + atf_dynstr_fini(&str); + } +} + +ATF_TC(rfind_ch); +ATF_TC_HEAD(rfind_ch, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the method to locate the first " + "occurrence of a character starting from the end"); +} +ATF_TC_BODY(rfind_ch, tc) +{ + atf_dynstr_t str; + + RE(atf_dynstr_init_fmt(&str, "Foo1/Bar2/,.Baz")); + + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '\0'), atf_dynstr_npos); + + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '0'), atf_dynstr_npos); + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'b'), atf_dynstr_npos); + + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'F'), 0); + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '/'), 9); + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'a'), 13); + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'z'), 14); + + atf_dynstr_fini(&str); +} + +/* + * Modifiers. + */ + +static +void +check_append(atf_error_t (*append)(atf_dynstr_t *, const char *, ...)) +{ + const size_t maxlen = 8192; + char buf[maxlen + 1]; + size_t i; + atf_dynstr_t str; + + printf("Appending with plain string\n"); + buf[0] = '\0'; + RE(atf_dynstr_init(&str)); + for (i = 0; i < maxlen; i++) { + if (strcmp(atf_dynstr_cstring(&str), buf) != 0) { + fprintf(stderr, "Failed at iteration %zd\n", i); + atf_tc_fail("Failed to append character at iteration %zd", i); + } + + RE(append(&str, "a")); + strcat(buf, "a"); + } + atf_dynstr_fini(&str); + + printf("Appending with formatted string\n"); + buf[0] = '\0'; + RE(atf_dynstr_init(&str)); + for (i = 0; i < maxlen; i++) { + if (strcmp(atf_dynstr_cstring(&str), buf) != 0) { + fprintf(stderr, "Failed at iteration %zd\n", i); + atf_tc_fail("Failed to append character at iteration %zd", i); + } + + RE(append(&str, "%s", "a")); + strcat(buf, "a"); + } + atf_dynstr_fini(&str); +} + +static +atf_error_t +append_ap_aux(atf_dynstr_t *str, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_dynstr_append_ap(str, fmt, ap); + va_end(ap); + + return err; +} + +ATF_TC(append_ap); +ATF_TC_HEAD(append_ap, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that appending a string to " + "another one works"); +} +ATF_TC_BODY(append_ap, tc) +{ + check_append(append_ap_aux); +} + +ATF_TC(append_fmt); +ATF_TC_HEAD(append_fmt, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that appending a string to " + "another one works"); +} +ATF_TC_BODY(append_fmt, tc) +{ + check_append(atf_dynstr_append_fmt); +} + +ATF_TC(clear); +ATF_TC_HEAD(clear, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks clearing a string"); +} +ATF_TC_BODY(clear, tc) +{ + atf_dynstr_t str; + + printf("Clear an empty string\n"); + RE(atf_dynstr_init(&str)); + atf_dynstr_clear(&str); + ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); + + printf("Clear a non-empty string\n"); + RE(atf_dynstr_init_fmt(&str, "Not empty")); + ATF_REQUIRE_EQ(atf_dynstr_length(&str), strlen("Not empty")); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Not empty") == 0); + atf_dynstr_clear(&str); + ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); +} + +static +void +check_prepend(atf_error_t (*prepend)(atf_dynstr_t *, const char *, ...)) +{ + const size_t maxlen = 8192; + char buf[maxlen + 1]; + size_t i; + atf_dynstr_t str; + + printf("Prepending with plain string\n"); + buf[0] = '\0'; + RE(atf_dynstr_init(&str)); + for (i = 0; i < maxlen; i++) { + if (strcmp(atf_dynstr_cstring(&str), buf) != 0) { + fprintf(stderr, "Failed at iteration %zd\n", i); + atf_tc_fail("Failed to prepend character at iteration %zd", i); + } + + memmove(buf + 1, buf, i + 1); + if (i % 2 == 0) { + RE(prepend(&str, "%s", "a")); + buf[0] = 'a'; + } else { + RE(prepend(&str, "%s", "b")); + buf[0] = 'b'; + } + } + atf_dynstr_fini(&str); + + printf("Prepending with formatted string\n"); + buf[0] = '\0'; + RE(atf_dynstr_init(&str)); + for (i = 0; i < maxlen; i++) { + if (strcmp(atf_dynstr_cstring(&str), buf) != 0) { + fprintf(stderr, "Failed at iteration %zd\n", i); + atf_tc_fail("Failed to prepend character at iteration %zd", i); + } + + memmove(buf + 1, buf, i + 1); + if (i % 2 == 0) { + RE(prepend(&str, "%s", "a")); + buf[0] = 'a'; + } else { + RE(prepend(&str, "%s", "b")); + buf[0] = 'b'; + } + } + atf_dynstr_fini(&str); +} + +static +atf_error_t +prepend_ap_aux(atf_dynstr_t *str, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_dynstr_prepend_ap(str, fmt, ap); + va_end(ap); + + return err; +} + +ATF_TC(prepend_ap); +ATF_TC_HEAD(prepend_ap, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that prepending a string to " + "another one works"); +} +ATF_TC_BODY(prepend_ap, tc) +{ + check_prepend(prepend_ap_aux); +} + +ATF_TC(prepend_fmt); +ATF_TC_HEAD(prepend_fmt, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that prepending a string to " + "another one works"); +} +ATF_TC_BODY(prepend_fmt, tc) +{ + check_prepend(atf_dynstr_prepend_fmt); +} + +/* + * Operators. + */ + +ATF_TC(equal_cstring); +ATF_TC_HEAD(equal_cstring, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_equal_dynstr_cstring " + "function"); +} +ATF_TC_BODY(equal_cstring, tc) +{ + atf_dynstr_t str; + + RE(atf_dynstr_init(&str)); + ATF_REQUIRE( atf_equal_dynstr_cstring(&str, "")); + ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Test")); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_fmt(&str, "Test")); + ATF_REQUIRE( atf_equal_dynstr_cstring(&str, "Test")); + ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "")); + ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Tes")); + ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Test ")); + atf_dynstr_fini(&str); +} + +ATF_TC(equal_dynstr); +ATF_TC_HEAD(equal_dynstr, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_equal_dynstr_dynstr " + "function"); +} +ATF_TC_BODY(equal_dynstr, tc) +{ + atf_dynstr_t str, str2; + + RE(atf_dynstr_init(&str)); + RE(atf_dynstr_init_fmt(&str2, "Test")); + ATF_REQUIRE( atf_equal_dynstr_dynstr(&str, &str)); + ATF_REQUIRE(!atf_equal_dynstr_dynstr(&str, &str2)); + atf_dynstr_fini(&str2); + atf_dynstr_fini(&str); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Constructors and destructors. */ + ATF_TP_ADD_TC(tp, init); + ATF_TP_ADD_TC(tp, init_ap); + ATF_TP_ADD_TC(tp, init_fmt); + ATF_TP_ADD_TC(tp, init_raw); + ATF_TP_ADD_TC(tp, init_rep); + ATF_TP_ADD_TC(tp, init_substr); + ATF_TP_ADD_TC(tp, copy); + ATF_TP_ADD_TC(tp, fini_disown); + + /* Getters. */ + ATF_TP_ADD_TC(tp, cstring); + ATF_TP_ADD_TC(tp, length); + ATF_TP_ADD_TC(tp, rfind_ch); + + /* Modifiers. */ + ATF_TP_ADD_TC(tp, append_ap); + ATF_TP_ADD_TC(tp, append_fmt); + ATF_TP_ADD_TC(tp, clear); + ATF_TP_ADD_TC(tp, prepend_ap); + ATF_TP_ADD_TC(tp, prepend_fmt); + + /* Operators. */ + ATF_TP_ADD_TC(tp, equal_cstring); + ATF_TP_ADD_TC(tp, equal_dynstr); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/env.c b/unit/atf-src/atf-c/detail/env.c new file mode 100644 index 0000000..8ee3d69 --- /dev/null +++ b/unit/atf-src/atf-c/detail/env.c @@ -0,0 +1,114 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/env.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> + +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/text.h" +#include "atf-c/error.h" + +const char * +atf_env_get(const char *name) +{ + const char* val = getenv(name); + PRE(val != NULL); + return val; +} + +const char * +atf_env_get_with_default(const char *name, const char *default_value) +{ + const char* val = getenv(name); + if (val == NULL) + return default_value; + else + return val; +} + +bool +atf_env_has(const char *name) +{ + return getenv(name) != NULL; +} + +atf_error_t +atf_env_set(const char *name, const char *val) +{ + atf_error_t err; + +#if defined(HAVE_SETENV) + if (setenv(name, val, 1) == -1) + err = atf_libc_error(errno, "Cannot set environment variable " + "'%s' to '%s'", name, val); + else + err = atf_no_error(); +#elif defined(HAVE_PUTENV) + char *buf; + + err = atf_text_format(&buf, "%s=%s", name, val); + if (!atf_is_error(err)) { + if (putenv(buf) == -1) + err = atf_libc_error(errno, "Cannot set environment variable " + "'%s' to '%s'", name, val); + free(buf); + } +#else +# error "Don't know how to set an environment variable." +#endif + + return err; +} + +atf_error_t +atf_env_unset(const char *name) +{ + atf_error_t err; + +#if defined(HAVE_UNSETENV) + unsetenv(name); + err = atf_no_error(); +#elif defined(HAVE_PUTENV) + char *buf; + + err = atf_text_format(&buf, "%s=", name); + if (!atf_is_error(err)) { + if (putenv(buf) == -1) + err = atf_libc_error(errno, "Cannot unset environment variable" + " '%s'", name); + free(buf); + } +#else +# error "Don't know how to unset an environment variable." +#endif + + return err; +} diff --git a/unit/atf-src/atf-c/detail/env.h b/unit/atf-src/atf-c/detail/env.h new file mode 100644 index 0000000..5d29440 --- /dev/null +++ b/unit/atf-src/atf-c/detail/env.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_ENV_H) +#define ATF_C_DETAIL_ENV_H + +#include <stdbool.h> + +#include <atf-c/error_fwd.h> + +const char *atf_env_get(const char *); +const char *atf_env_get_with_default(const char *, const char *); +bool atf_env_has(const char *); +atf_error_t atf_env_set(const char *, const char *); +atf_error_t atf_env_unset(const char *); + +#endif /* !defined(ATF_C_DETAIL_ENV_H) */ diff --git a/unit/atf-src/atf-c/detail/env_test.c b/unit/atf-src/atf-c/detail/env_test.c new file mode 100644 index 0000000..3909c29 --- /dev/null +++ b/unit/atf-src/atf-c/detail/env_test.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/env.h" + +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" +#include "atf-c/detail/text.h" + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(has); +ATF_TC_HEAD(has, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_env_has function"); +} +ATF_TC_BODY(has, tc) +{ + ATF_REQUIRE(atf_env_has("PATH")); + ATF_REQUIRE(!atf_env_has("_UNDEFINED_VARIABLE_")); +} + +ATF_TC(get); +ATF_TC_HEAD(get, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_env_get function"); +} +ATF_TC_BODY(get, tc) +{ + const char *val; + + ATF_REQUIRE(atf_env_has("PATH")); + + val = atf_env_get("PATH"); + ATF_REQUIRE(strlen(val) > 0); + ATF_REQUIRE(strchr(val, ':') != NULL); +} + +ATF_TC(get_with_default); +ATF_TC_HEAD(get_with_default, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_env_get_with_default " + "function"); +} +ATF_TC_BODY(get_with_default, tc) +{ + const char *val; + + ATF_REQUIRE(atf_env_has("PATH")); + + val = atf_env_get_with_default("PATH", "unknown"); + ATF_REQUIRE(strcmp(val, "unknown") != 0); + + val = atf_env_get_with_default("_UNKNOWN_VARIABLE_", "foo bar"); + ATF_REQUIRE(strcmp(val, "foo bar") == 0); +} + +ATF_TC(set); +ATF_TC_HEAD(set, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_env_set function"); +} +ATF_TC_BODY(set, tc) +{ + char *oldval; + + ATF_REQUIRE(atf_env_has("PATH")); + RE(atf_text_format(&oldval, "%s", atf_env_get("PATH"))); + RE(atf_env_set("PATH", "foo-bar")); + ATF_REQUIRE(strcmp(atf_env_get("PATH"), oldval) != 0); + ATF_REQUIRE(strcmp(atf_env_get("PATH"), "foo-bar") == 0); + free(oldval); + + ATF_REQUIRE(!atf_env_has("_UNDEFINED_VARIABLE_")); + RE(atf_env_set("_UNDEFINED_VARIABLE_", "foo2-bar2")); + ATF_REQUIRE(strcmp(atf_env_get("_UNDEFINED_VARIABLE_"), + "foo2-bar2") == 0); +} + +ATF_TC(unset); +ATF_TC_HEAD(unset, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_env_unset function"); +} +ATF_TC_BODY(unset, tc) +{ + ATF_REQUIRE(atf_env_has("PATH")); + RE(atf_env_unset("PATH")); + ATF_REQUIRE(!atf_env_has("PATH")); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, has); + ATF_TP_ADD_TC(tp, get); + ATF_TP_ADD_TC(tp, get_with_default); + ATF_TP_ADD_TC(tp, set); + ATF_TP_ADD_TC(tp, unset); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/fs.c b/unit/atf-src/atf-c/detail/fs.c new file mode 100644 index 0000000..5ff7648 --- /dev/null +++ b/unit/atf-src/atf-c/detail/fs.c @@ -0,0 +1,884 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/fs.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <dirent.h> +#include <errno.h> +#include <libgen.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "atf-c/defs.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/text.h" +#include "atf-c/detail/user.h" +#include "atf-c/error.h" + +/* --------------------------------------------------------------------- + * Prototypes for auxiliary functions. + * --------------------------------------------------------------------- */ + +static bool check_umask(const mode_t, const mode_t); +static atf_error_t copy_contents(const atf_fs_path_t *, char **); +static mode_t current_umask(void); +static atf_error_t do_mkdtemp(char *); +static atf_error_t normalize(atf_dynstr_t *, char *); +static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list); +static void replace_contents(atf_fs_path_t *, const char *); +static const char *stat_type_to_string(const int); + +/* --------------------------------------------------------------------- + * The "invalid_umask" error type. + * --------------------------------------------------------------------- */ + +struct invalid_umask_error_data { + /* One of atf_fs_stat_*_type. */ + int m_type; + + /* The original path causing the error. */ + /* XXX: Ideally this would be an atf_fs_path_t, but if we create it + * from the error constructor, we cannot delete the path later on. + * Can't remember why atf_error_new does not take a hook for + * deletion. */ + char m_path[1024]; + + /* The umask that caused the error. */ + mode_t m_umask; +}; +typedef struct invalid_umask_error_data invalid_umask_error_data_t; + +static +void +invalid_umask_format(const atf_error_t err, char *buf, size_t buflen) +{ + const invalid_umask_error_data_t *data; + + PRE(atf_error_is(err, "invalid_umask")); + + data = atf_error_data(err); + snprintf(buf, buflen, "Could not create the temporary %s %s because " + "it will not have enough access rights due to the current " + "umask %05o", stat_type_to_string(data->m_type), + data->m_path, (unsigned int)data->m_umask); +} + +static +atf_error_t +invalid_umask_error(const atf_fs_path_t *path, const int type, + const mode_t failing_mask) +{ + atf_error_t err; + invalid_umask_error_data_t data; + + data.m_type = type; + + strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path)); + data.m_path[sizeof(data.m_path) - 1] = '\0'; + + data.m_umask = failing_mask; + + err = atf_error_new("invalid_umask", &data, sizeof(data), + invalid_umask_format); + + return err; +} + +/* --------------------------------------------------------------------- + * The "unknown_file_type" error type. + * --------------------------------------------------------------------- */ + +struct unknown_type_error_data { + const char *m_path; + int m_type; +}; +typedef struct unknown_type_error_data unknown_type_error_data_t; + +static +void +unknown_type_format(const atf_error_t err, char *buf, size_t buflen) +{ + const unknown_type_error_data_t *data; + + PRE(atf_error_is(err, "unknown_type")); + + data = atf_error_data(err); + snprintf(buf, buflen, "Unknown file type %d of %s", data->m_type, + data->m_path); +} + +static +atf_error_t +unknown_type_error(const char *path, int type) +{ + atf_error_t err; + unknown_type_error_data_t data; + + data.m_path = path; + data.m_type = type; + + err = atf_error_new("unknown_type", &data, sizeof(data), + unknown_type_format); + + return err; +} + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +bool +check_umask(const mode_t exp_mode, const mode_t min_mode) +{ + const mode_t actual_mode = (~current_umask() & exp_mode); + return (actual_mode & min_mode) == min_mode; +} + +static +atf_error_t +copy_contents(const atf_fs_path_t *p, char **buf) +{ + atf_error_t err; + char *str; + + str = (char *)malloc(atf_dynstr_length(&p->m_data) + 1); + if (str == NULL) + err = atf_no_memory_error(); + else { + strcpy(str, atf_dynstr_cstring(&p->m_data)); + *buf = str; + err = atf_no_error(); + } + + return err; +} + +static +mode_t +current_umask(void) +{ + const mode_t current = umask(0); + (void)umask(current); + return current; +} + +static +atf_error_t +do_mkdtemp(char *tmpl) +{ + atf_error_t err; + + PRE(strstr(tmpl, "XXXXXX") != NULL); + + if (mkdtemp(tmpl) == NULL) + err = atf_libc_error(errno, "Cannot create temporary directory " + "with template '%s'", tmpl); + else + err = atf_no_error(); + + return err; +} + +static +atf_error_t +do_mkstemp(char *tmpl, int *fdout) +{ + atf_error_t err; + + PRE(strstr(tmpl, "XXXXXX") != NULL); + + *fdout = mkstemp(tmpl); + if (*fdout == -1) + err = atf_libc_error(errno, "Cannot create temporary file " + "with template '%s'", tmpl); + + else + err = atf_no_error(); + + return err; +} + +static +atf_error_t +normalize(atf_dynstr_t *d, char *p) +{ + const char *ptr; + char *last; + atf_error_t err; + bool first; + + PRE(strlen(p) > 0); + PRE(atf_dynstr_length(d) == 0); + + if (p[0] == '/') + err = atf_dynstr_append_fmt(d, "/"); + else + err = atf_no_error(); + + first = true; + last = NULL; /* Silence GCC warning. */ + ptr = strtok_r(p, "/", &last); + while (!atf_is_error(err) && ptr != NULL) { + if (strlen(ptr) > 0) { + err = atf_dynstr_append_fmt(d, "%s%s", first ? "" : "/", ptr); + first = false; + } + + ptr = strtok_r(NULL, "/", &last); + } + + return err; +} + +static +atf_error_t +normalize_ap(atf_dynstr_t *d, const char *p, va_list ap) +{ + char *str; + atf_error_t err; + va_list ap2; + + err = atf_dynstr_init(d); + if (atf_is_error(err)) + goto out; + + va_copy(ap2, ap); + err = atf_text_format_ap(&str, p, ap2); + va_end(ap2); + if (atf_is_error(err)) + atf_dynstr_fini(d); + else { + err = normalize(d, str); + free(str); + } + +out: + return err; +} + +static +void +replace_contents(atf_fs_path_t *p, const char *buf) +{ + atf_error_t err; + + PRE(atf_dynstr_length(&p->m_data) == strlen(buf)); + + atf_dynstr_clear(&p->m_data); + err = atf_dynstr_append_fmt(&p->m_data, "%s", buf); + + INV(!atf_is_error(err)); +} + +static +const char * +stat_type_to_string(const int type) +{ + const char *str; + + if (type == atf_fs_stat_blk_type) + str = "block device"; + else if (type == atf_fs_stat_chr_type) + str = "character device"; + else if (type == atf_fs_stat_dir_type) + str = "directory"; + else if (type == atf_fs_stat_fifo_type) + str = "named pipe"; + else if (type == atf_fs_stat_lnk_type) + str = "symbolic link"; + else if (type == atf_fs_stat_reg_type) + str = "regular file"; + else if (type == atf_fs_stat_sock_type) + str = "socket"; + else if (type == atf_fs_stat_wht_type) + str = "whiteout"; + else { + UNREACHABLE; + str = NULL; + } + + return str; +} + +/* --------------------------------------------------------------------- + * The "atf_fs_path" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors/destructors. + */ + +atf_error_t +atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap) +{ + atf_error_t err; + va_list ap2; + + va_copy(ap2, ap); + err = normalize_ap(&p->m_data, fmt, ap2); + va_end(ap2); + + return err; +} + +atf_error_t +atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_fs_path_init_ap(p, fmt, ap); + va_end(ap); + + return err; +} + +atf_error_t +atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src) +{ + return atf_dynstr_copy(&dest->m_data, &src->m_data); +} + +void +atf_fs_path_fini(atf_fs_path_t *p) +{ + atf_dynstr_fini(&p->m_data); +} + +/* + * Getters. + */ + +atf_error_t +atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp) +{ + const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/'); + atf_error_t err; + + if (endpos == atf_dynstr_npos) + err = atf_fs_path_init_fmt(bp, "."); + else if (endpos == 0) + err = atf_fs_path_init_fmt(bp, "/"); + else + err = atf_dynstr_init_substr(&bp->m_data, &p->m_data, 0, endpos); + +#if defined(HAVE_CONST_DIRNAME) + INV(atf_equal_dynstr_cstring(&bp->m_data, + dirname(atf_dynstr_cstring(&p->m_data)))); +#endif /* defined(HAVE_CONST_DIRNAME) */ + + return err; +} + +const char * +atf_fs_path_cstring(const atf_fs_path_t *p) +{ + return atf_dynstr_cstring(&p->m_data); +} + +atf_error_t +atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln) +{ + size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/'); + atf_error_t err; + + if (begpos == atf_dynstr_npos) + begpos = 0; + else + begpos++; + + err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos); + +#if defined(HAVE_CONST_BASENAME) + INV(atf_equal_dynstr_cstring(ln, + basename(atf_dynstr_cstring(&p->m_data)))); +#endif /* defined(HAVE_CONST_BASENAME) */ + + return err; +} + +bool +atf_fs_path_is_absolute(const atf_fs_path_t *p) +{ + return atf_dynstr_cstring(&p->m_data)[0] == '/'; +} + +bool +atf_fs_path_is_root(const atf_fs_path_t *p) +{ + return atf_equal_dynstr_cstring(&p->m_data, "/"); +} + +/* + * Modifiers. + */ + +atf_error_t +atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap) +{ + atf_dynstr_t aux; + atf_error_t err; + va_list ap2; + + va_copy(ap2, ap); + err = normalize_ap(&aux, fmt, ap2); + va_end(ap2); + if (!atf_is_error(err)) { + const char *auxstr = atf_dynstr_cstring(&aux); + const bool needslash = auxstr[0] != '/'; + + err = atf_dynstr_append_fmt(&p->m_data, "%s%s", + needslash ? "/" : "", auxstr); + + atf_dynstr_fini(&aux); + } + + return err; +} + +atf_error_t +atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_fs_path_append_ap(p, fmt, ap); + va_end(ap); + + return err; +} + +atf_error_t +atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2) +{ + return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data)); +} + +atf_error_t +atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa) +{ + atf_error_t err; + + PRE(!atf_fs_path_is_absolute(p)); + + err = atf_fs_getcwd(pa); + if (atf_is_error(err)) + goto out; + + err = atf_fs_path_append_path(pa, p); + if (atf_is_error(err)) + atf_fs_path_fini(pa); + +out: + return err; +} + +/* + * Operators. + */ + +bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1, + const atf_fs_path_t *p2) +{ + return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data); +} + +/* --------------------------------------------------------------------- + * The "atf_fs_path" type. + * --------------------------------------------------------------------- */ + +/* + * Constants. + */ + +const int atf_fs_stat_blk_type = 1; +const int atf_fs_stat_chr_type = 2; +const int atf_fs_stat_dir_type = 3; +const int atf_fs_stat_fifo_type = 4; +const int atf_fs_stat_lnk_type = 5; +const int atf_fs_stat_reg_type = 6; +const int atf_fs_stat_sock_type = 7; +const int atf_fs_stat_wht_type = 8; + +/* + * Constructors/destructors. + */ + +atf_error_t +atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p) +{ + atf_error_t err; + const char *pstr = atf_fs_path_cstring(p); + + if (lstat(pstr, &st->m_sb) == -1) { + err = atf_libc_error(errno, "Cannot get information of %s; " + "lstat(2) failed", pstr); + } else { + int type = st->m_sb.st_mode & S_IFMT; + err = atf_no_error(); + switch (type) { + case S_IFBLK: st->m_type = atf_fs_stat_blk_type; break; + case S_IFCHR: st->m_type = atf_fs_stat_chr_type; break; + case S_IFDIR: st->m_type = atf_fs_stat_dir_type; break; + case S_IFIFO: st->m_type = atf_fs_stat_fifo_type; break; + case S_IFLNK: st->m_type = atf_fs_stat_lnk_type; break; + case S_IFREG: st->m_type = atf_fs_stat_reg_type; break; + case S_IFSOCK: st->m_type = atf_fs_stat_sock_type; break; +#if defined(S_IFWHT) + case S_IFWHT: st->m_type = atf_fs_stat_wht_type; break; +#endif + default: + err = unknown_type_error(pstr, type); + } + } + + return err; +} + +void +atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src) +{ + dest->m_type = src->m_type; + dest->m_sb = src->m_sb; +} + +void +atf_fs_stat_fini(atf_fs_stat_t *st ATF_DEFS_ATTRIBUTE_UNUSED) +{ +} + +/* + * Getters. + */ + +dev_t +atf_fs_stat_get_device(const atf_fs_stat_t *st) +{ + return st->m_sb.st_dev; +} + +ino_t +atf_fs_stat_get_inode(const atf_fs_stat_t *st) +{ + return st->m_sb.st_ino; +} + +mode_t +atf_fs_stat_get_mode(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & ~S_IFMT; +} + +off_t +atf_fs_stat_get_size(const atf_fs_stat_t *st) +{ + return st->m_sb.st_size; +} + +int +atf_fs_stat_get_type(const atf_fs_stat_t *st) +{ + return st->m_type; +} + +bool +atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IRUSR; +} + +bool +atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IWUSR; +} + +bool +atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IXUSR; +} + +bool +atf_fs_stat_is_group_readable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IRGRP; +} + +bool +atf_fs_stat_is_group_writable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IWGRP; +} + +bool +atf_fs_stat_is_group_executable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IXGRP; +} + +bool +atf_fs_stat_is_other_readable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IROTH; +} + +bool +atf_fs_stat_is_other_writable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IWOTH; +} + +bool +atf_fs_stat_is_other_executable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IXOTH; +} + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +const int atf_fs_access_f = 1 << 0; +const int atf_fs_access_r = 1 << 1; +const int atf_fs_access_w = 1 << 2; +const int atf_fs_access_x = 1 << 3; + +/* + * An implementation of access(2) but using the effective user value + * instead of the real one. Also avoids false positives for root when + * asking for execute permissions, which appear in SunOS. + */ +atf_error_t +atf_fs_eaccess(const atf_fs_path_t *p, int mode) +{ + atf_error_t err; + struct stat st; + bool ok; + + PRE(mode & atf_fs_access_f || mode & atf_fs_access_r || + mode & atf_fs_access_w || mode & atf_fs_access_x); + + if (lstat(atf_fs_path_cstring(p), &st) == -1) { + err = atf_libc_error(errno, "Cannot get information from file %s", + atf_fs_path_cstring(p)); + goto out; + } + + err = atf_no_error(); + + /* Early return if we are only checking for existence and the file + * exists (stat call returned). */ + if (mode & atf_fs_access_f) + goto out; + + ok = false; + if (atf_user_is_root()) { + if (!ok && !(mode & atf_fs_access_x)) { + /* Allow root to read/write any file. */ + ok = true; + } + + if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { + /* Allow root to execute the file if any of its execution bits + * are set. */ + ok = true; + } + } else { + if (!ok && (atf_user_euid() == st.st_uid)) { + ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRUSR)) || + ((mode & atf_fs_access_w) && (st.st_mode & S_IWUSR)) || + ((mode & atf_fs_access_x) && (st.st_mode & S_IXUSR)); + } + if (!ok && atf_user_is_member_of_group(st.st_gid)) { + ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRGRP)) || + ((mode & atf_fs_access_w) && (st.st_mode & S_IWGRP)) || + ((mode & atf_fs_access_x) && (st.st_mode & S_IXGRP)); + } + if (!ok && ((atf_user_euid() != st.st_uid) && + !atf_user_is_member_of_group(st.st_gid))) { + ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IROTH)) || + ((mode & atf_fs_access_w) && (st.st_mode & S_IWOTH)) || + ((mode & atf_fs_access_x) && (st.st_mode & S_IXOTH)); + } + } + + if (!ok) + err = atf_libc_error(EACCES, "Access check failed"); + +out: + return err; +} + +atf_error_t +atf_fs_exists(const atf_fs_path_t *p, bool *b) +{ + atf_error_t err; + + err = atf_fs_eaccess(p, atf_fs_access_f); + if (atf_is_error(err)) { + if (atf_error_is(err, "libc") && atf_libc_error_code(err) == ENOENT) { + atf_error_free(err); + err = atf_no_error(); + *b = false; + } + } else + *b = true; + + return err; +} + +atf_error_t +atf_fs_getcwd(atf_fs_path_t *p) +{ + atf_error_t err; + char *cwd; + +#if defined(HAVE_GETCWD_DYN) + cwd = getcwd(NULL, 0); +#else + cwd = getcwd(NULL, MAXPATHLEN); +#endif + if (cwd == NULL) { + err = atf_libc_error(errno, "Cannot determine current directory"); + goto out; + } + + err = atf_fs_path_init_fmt(p, "%s", cwd); + free(cwd); + +out: + return err; +} + +atf_error_t +atf_fs_mkdtemp(atf_fs_path_t *p) +{ + atf_error_t err; + char *buf; + + if (!check_umask(S_IRWXU, S_IRWXU)) { + err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask()); + goto out; + } + + err = copy_contents(p, &buf); + if (atf_is_error(err)) + goto out; + + err = do_mkdtemp(buf); + if (atf_is_error(err)) + goto out_buf; + + replace_contents(p, buf); + + INV(!atf_is_error(err)); +out_buf: + free(buf); +out: + return err; +} + +atf_error_t +atf_fs_mkstemp(atf_fs_path_t *p, int *fdout) +{ + atf_error_t err; + char *buf; + int fd; + + if (!check_umask(S_IRWXU, S_IRWXU)) { + err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask()); + goto out; + } + + err = copy_contents(p, &buf); + if (atf_is_error(err)) + goto out; + + err = do_mkstemp(buf, &fd); + if (atf_is_error(err)) + goto out_buf; + + replace_contents(p, buf); + *fdout = fd; + + INV(!atf_is_error(err)); +out_buf: + free(buf); +out: + return err; +} + +atf_error_t +atf_fs_rmdir(const atf_fs_path_t *p) +{ + atf_error_t err; + + if (rmdir(atf_fs_path_cstring(p))) { + if (errno == EEXIST) { + /* Some operating systems (e.g. OpenSolaris 200906) return + * EEXIST instead of ENOTEMPTY for non-empty directories. + * Homogenize the return value so that callers don't need + * to bother about differences in operating systems. */ + errno = ENOTEMPTY; + } + err = atf_libc_error(errno, "Cannot remove directory"); + } else + err = atf_no_error(); + + return err; +} + +atf_error_t +atf_fs_unlink(const atf_fs_path_t *p) +{ + atf_error_t err; + const char *path; + + path = atf_fs_path_cstring(p); + + if (unlink(path) != 0) + err = atf_libc_error(errno, "Cannot unlink file: '%s'", path); + else + err = atf_no_error(); + + return err; +} diff --git a/unit/atf-src/atf-c/detail/fs.h b/unit/atf-src/atf-c/detail/fs.h new file mode 100644 index 0000000..1c6c0eb --- /dev/null +++ b/unit/atf-src/atf-c/detail/fs.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_FS_H) +#define ATF_C_DETAIL_FS_H + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdarg.h> +#include <stdbool.h> + +#include <atf-c/detail/dynstr.h> +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_fs_path" type. + * --------------------------------------------------------------------- */ + +struct atf_fs_path { + atf_dynstr_t m_data; +}; +typedef struct atf_fs_path atf_fs_path_t; + +/* Constructors/destructors. */ +atf_error_t atf_fs_path_init_ap(atf_fs_path_t *, const char *, va_list); +atf_error_t atf_fs_path_init_fmt(atf_fs_path_t *, const char *, ...); +atf_error_t atf_fs_path_copy(atf_fs_path_t *, const atf_fs_path_t *); +void atf_fs_path_fini(atf_fs_path_t *); + +/* Getters. */ +atf_error_t atf_fs_path_branch_path(const atf_fs_path_t *, atf_fs_path_t *); +const char *atf_fs_path_cstring(const atf_fs_path_t *); +atf_error_t atf_fs_path_leaf_name(const atf_fs_path_t *, atf_dynstr_t *); +bool atf_fs_path_is_absolute(const atf_fs_path_t *); +bool atf_fs_path_is_root(const atf_fs_path_t *); + +/* Modifiers. */ +atf_error_t atf_fs_path_append_ap(atf_fs_path_t *, const char *, va_list); +atf_error_t atf_fs_path_append_fmt(atf_fs_path_t *, const char *, ...); +atf_error_t atf_fs_path_append_path(atf_fs_path_t *, const atf_fs_path_t *); +atf_error_t atf_fs_path_to_absolute(const atf_fs_path_t *, atf_fs_path_t *); + +/* Operators. */ +bool atf_equal_fs_path_fs_path(const atf_fs_path_t *, + const atf_fs_path_t *); + +/* --------------------------------------------------------------------- + * The "atf_fs_stat" type. + * --------------------------------------------------------------------- */ + +struct atf_fs_stat { + int m_type; + struct stat m_sb; +}; +typedef struct atf_fs_stat atf_fs_stat_t; + +/* Constants. */ +extern const int atf_fs_stat_blk_type; +extern const int atf_fs_stat_chr_type; +extern const int atf_fs_stat_dir_type; +extern const int atf_fs_stat_fifo_type; +extern const int atf_fs_stat_lnk_type; +extern const int atf_fs_stat_reg_type; +extern const int atf_fs_stat_sock_type; +extern const int atf_fs_stat_wht_type; + +/* Constructors/destructors. */ +atf_error_t atf_fs_stat_init(atf_fs_stat_t *, const atf_fs_path_t *); +void atf_fs_stat_copy(atf_fs_stat_t *, const atf_fs_stat_t *); +void atf_fs_stat_fini(atf_fs_stat_t *); + +/* Getters. */ +dev_t atf_fs_stat_get_device(const atf_fs_stat_t *); +ino_t atf_fs_stat_get_inode(const atf_fs_stat_t *); +mode_t atf_fs_stat_get_mode(const atf_fs_stat_t *); +off_t atf_fs_stat_get_size(const atf_fs_stat_t *); +int atf_fs_stat_get_type(const atf_fs_stat_t *); +bool atf_fs_stat_is_owner_readable(const atf_fs_stat_t *); +bool atf_fs_stat_is_owner_writable(const atf_fs_stat_t *); +bool atf_fs_stat_is_owner_executable(const atf_fs_stat_t *); +bool atf_fs_stat_is_group_readable(const atf_fs_stat_t *); +bool atf_fs_stat_is_group_writable(const atf_fs_stat_t *); +bool atf_fs_stat_is_group_executable(const atf_fs_stat_t *); +bool atf_fs_stat_is_other_readable(const atf_fs_stat_t *); +bool atf_fs_stat_is_other_writable(const atf_fs_stat_t *); +bool atf_fs_stat_is_other_executable(const atf_fs_stat_t *); + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +extern const int atf_fs_access_f; +extern const int atf_fs_access_r; +extern const int atf_fs_access_w; +extern const int atf_fs_access_x; + +atf_error_t atf_fs_eaccess(const atf_fs_path_t *, int); +atf_error_t atf_fs_exists(const atf_fs_path_t *, bool *); +atf_error_t atf_fs_getcwd(atf_fs_path_t *); +atf_error_t atf_fs_mkdtemp(atf_fs_path_t *); +atf_error_t atf_fs_mkstemp(atf_fs_path_t *, int *); +atf_error_t atf_fs_rmdir(const atf_fs_path_t *); +atf_error_t atf_fs_unlink(const atf_fs_path_t *); + +#endif /* !defined(ATF_C_DETAIL_FS_H) */ diff --git a/unit/atf-src/atf-c/detail/fs_test.c b/unit/atf-src/atf-c/detail/fs_test.c new file mode 100644 index 0000000..3dbc4d3 --- /dev/null +++ b/unit/atf-src/atf-c/detail/fs_test.c @@ -0,0 +1,1079 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/fs.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" +#include "atf-c/detail/user.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +void +create_dir(const char *p, int mode) +{ + int ret; + + ret = mkdir(p, mode); + if (ret == -1) + atf_tc_fail("Could not create helper directory %s", p); +} + +static +void +create_file(const char *p, int mode) +{ + int fd; + + fd = open(p, O_CREAT | O_WRONLY | O_TRUNC, mode); + if (fd == -1) + atf_tc_fail("Could not create helper file %s", p); + close(fd); +} + +static +bool +exists(const atf_fs_path_t *p) +{ + return access(atf_fs_path_cstring(p), F_OK) == 0; +} + +static +atf_error_t +mkstemp_discard_fd(atf_fs_path_t *p) +{ + int fd; + atf_error_t err = atf_fs_mkstemp(p, &fd); + if (!atf_is_error(err)) + close(fd); + return err; +} + +/* --------------------------------------------------------------------- + * Test cases for the "atf_fs_path" type. + * --------------------------------------------------------------------- */ + +ATF_TC(path_normalize); +ATF_TC_HEAD(path_normalize, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the path's normalization"); +} +ATF_TC_BODY(path_normalize, tc) +{ + struct test { + const char *in; + const char *out; + } tests[] = { + { ".", ".", }, + { "..", "..", }, + + { "/", "/", }, + { "//", "/", }, /* NO_CHECK_STYLE */ + { "///", "/", }, /* NO_CHECK_STYLE */ + + { "foo", "foo", }, + { "foo/", "foo", }, + { "foo/bar", "foo/bar", }, + { "foo/bar/", "foo/bar", }, + + { "/foo", "/foo", }, + { "/foo/bar", "/foo/bar", }, + { "/foo/bar/", "/foo/bar", }, + + { "///foo", "/foo", }, /* NO_CHECK_STYLE */ + { "///foo///bar", "/foo/bar", }, /* NO_CHECK_STYLE */ + { "///foo///bar///", "/foo/bar", }, /* NO_CHECK_STYLE */ + + { NULL, NULL } + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p; + + printf("Input : >%s<\n", t->in); + printf("Expected output: >%s<\n", t->out); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + printf("Output : >%s<\n", atf_fs_path_cstring(&p)); + ATF_REQUIRE(strcmp(atf_fs_path_cstring(&p), t->out) == 0); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_copy); +ATF_TC_HEAD(path_copy, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_copy constructor"); +} +ATF_TC_BODY(path_copy, tc) +{ + atf_fs_path_t str, str2; + + RE(atf_fs_path_init_fmt(&str, "foo")); + RE(atf_fs_path_copy(&str2, &str)); + + ATF_REQUIRE(atf_equal_fs_path_fs_path(&str, &str2)); + + RE(atf_fs_path_append_fmt(&str2, "bar")); + + ATF_REQUIRE(!atf_equal_fs_path_fs_path(&str, &str2)); + + atf_fs_path_fini(&str2); + atf_fs_path_fini(&str); +} + +ATF_TC(path_is_absolute); +ATF_TC_HEAD(path_is_absolute, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the path::is_absolute function"); +} +ATF_TC_BODY(path_is_absolute, tc) +{ + struct test { + const char *in; + bool abs; + } tests[] = { + { "/", true }, + { "////", true }, /* NO_CHECK_STYLE */ + { "////a", true }, /* NO_CHECK_STYLE */ + { "//a//", true }, /* NO_CHECK_STYLE */ + { "a////", false }, /* NO_CHECK_STYLE */ + { "../foo", false }, + { NULL, false }, + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p; + + printf("Input : %s\n", t->in); + printf("Expected result: %s\n", t->abs ? "true" : "false"); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + printf("Result : %s\n", + atf_fs_path_is_absolute(&p) ? "true" : "false"); + if (t->abs) + ATF_REQUIRE(atf_fs_path_is_absolute(&p)); + else + ATF_REQUIRE(!atf_fs_path_is_absolute(&p)); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_is_root); +ATF_TC_HEAD(path_is_root, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the path::is_root function"); +} +ATF_TC_BODY(path_is_root, tc) +{ + struct test { + const char *in; + bool root; + } tests[] = { + { "/", true }, + { "////", true }, /* NO_CHECK_STYLE */ + { "////a", false }, /* NO_CHECK_STYLE */ + { "//a//", false }, /* NO_CHECK_STYLE */ + { "a////", false }, /* NO_CHECK_STYLE */ + { "../foo", false }, + { NULL, false }, + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p; + + printf("Input : %s\n", t->in); + printf("Expected result: %s\n", t->root ? "true" : "false"); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + printf("Result : %s\n", + atf_fs_path_is_root(&p) ? "true" : "false"); + if (t->root) + ATF_REQUIRE(atf_fs_path_is_root(&p)); + else + ATF_REQUIRE(!atf_fs_path_is_root(&p)); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_branch_path); +ATF_TC_HEAD(path_branch_path, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_branch_path " + "function"); +} +ATF_TC_BODY(path_branch_path, tc) +{ + struct test { + const char *in; + const char *branch; + } tests[] = { + { ".", "." }, + { "foo", "." }, + { "foo/bar", "foo" }, + { "/foo", "/" }, + { "/foo/bar", "/foo" }, + { NULL, NULL }, + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p, bp; + + printf("Input : %s\n", t->in); + printf("Expected output: %s\n", t->branch); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + RE(atf_fs_path_branch_path(&p, &bp)); + printf("Output : %s\n", atf_fs_path_cstring(&bp)); + ATF_REQUIRE(strcmp(atf_fs_path_cstring(&bp), t->branch) == 0); + atf_fs_path_fini(&bp); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_leaf_name); +ATF_TC_HEAD(path_leaf_name, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_leaf_name " + "function"); +} +ATF_TC_BODY(path_leaf_name, tc) +{ + struct test { + const char *in; + const char *leaf; + } tests[] = { + { ".", "." }, + { "foo", "foo" }, + { "foo/bar", "bar" }, + { "/foo", "foo" }, + { "/foo/bar", "bar" }, + { NULL, NULL }, + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p; + atf_dynstr_t ln; + + printf("Input : %s\n", t->in); + printf("Expected output: %s\n", t->leaf); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + RE(atf_fs_path_leaf_name(&p, &ln)); + printf("Output : %s\n", atf_dynstr_cstring(&ln)); + ATF_REQUIRE(atf_equal_dynstr_cstring(&ln, t->leaf)); + atf_dynstr_fini(&ln); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_append); +ATF_TC_HEAD(path_append, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the concatenation of multiple " + "paths"); +} +ATF_TC_BODY(path_append, tc) +{ + struct test { + const char *in; + const char *ap; + const char *out; + } tests[] = { + { "foo", "bar", "foo/bar" }, + { "foo/", "/bar", "foo/bar" }, + { "foo/", "/bar/baz", "foo/bar/baz" }, + { "foo/", "///bar///baz", "foo/bar/baz" }, /* NO_CHECK_STYLE */ + + { NULL, NULL, NULL } + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p; + + printf("Input : >%s<\n", t->in); + printf("Append : >%s<\n", t->ap); + printf("Expected output: >%s<\n", t->out); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + + RE(atf_fs_path_append_fmt(&p, "%s", t->ap)); + + printf("Output : >%s<\n", atf_fs_path_cstring(&p)); + ATF_REQUIRE(strcmp(atf_fs_path_cstring(&p), t->out) == 0); + + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_to_absolute); +ATF_TC_HEAD(path_to_absolute, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_to_absolute " + "function"); +} +ATF_TC_BODY(path_to_absolute, tc) +{ + const char *names[] = { ".", "dir", NULL }; + const char **n; + + ATF_REQUIRE(mkdir("dir", 0755) != -1); + + for (n = names; *n != NULL; n++) { + atf_fs_path_t p, p2; + atf_fs_stat_t st1, st2; + + RE(atf_fs_path_init_fmt(&p, "%s", *n)); + RE(atf_fs_stat_init(&st1, &p)); + printf("Relative path: %s\n", atf_fs_path_cstring(&p)); + + RE(atf_fs_path_to_absolute(&p, &p2)); + printf("Absolute path: %s\n", atf_fs_path_cstring(&p2)); + + ATF_REQUIRE(atf_fs_path_is_absolute(&p2)); + RE(atf_fs_stat_init(&st2, &p2)); + + ATF_REQUIRE_EQ(atf_fs_stat_get_device(&st1), + atf_fs_stat_get_device(&st2)); + ATF_REQUIRE_EQ(atf_fs_stat_get_inode(&st1), + atf_fs_stat_get_inode(&st2)); + + atf_fs_stat_fini(&st2); + atf_fs_stat_fini(&st1); + atf_fs_path_fini(&p2); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_equal); +ATF_TC_HEAD(path_equal, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the equality operators for paths"); +} +ATF_TC_BODY(path_equal, tc) +{ + atf_fs_path_t p1, p2; + + RE(atf_fs_path_init_fmt(&p1, "foo")); + + RE(atf_fs_path_init_fmt(&p2, "foo")); + ATF_REQUIRE(atf_equal_fs_path_fs_path(&p1, &p2)); + atf_fs_path_fini(&p2); + + RE(atf_fs_path_init_fmt(&p2, "bar")); + ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2)); + atf_fs_path_fini(&p2); + + atf_fs_path_fini(&p1); +} + +/* --------------------------------------------------------------------- + * Test cases for the "atf_fs_stat" type. + * --------------------------------------------------------------------- */ + +ATF_TC(stat_mode); +ATF_TC_HEAD(stat_mode, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_get_mode function " + "and, indirectly, the constructor"); +} +ATF_TC_BODY(stat_mode, tc) +{ + atf_fs_path_t p; + atf_fs_stat_t st; + + create_file("f1", 0400); + create_file("f2", 0644); + + RE(atf_fs_path_init_fmt(&p, "f1")); + RE(atf_fs_stat_init(&st, &p)); + ATF_CHECK_EQ(0400, atf_fs_stat_get_mode(&st)); + atf_fs_stat_fini(&st); + atf_fs_path_fini(&p); + + RE(atf_fs_path_init_fmt(&p, "f2")); + RE(atf_fs_stat_init(&st, &p)); + ATF_CHECK_EQ(0644, atf_fs_stat_get_mode(&st)); + atf_fs_stat_fini(&st); + atf_fs_path_fini(&p); +} + +ATF_TC(stat_type); +ATF_TC_HEAD(stat_type, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_get_type function " + "and, indirectly, the constructor"); +} +ATF_TC_BODY(stat_type, tc) +{ + atf_fs_path_t p; + atf_fs_stat_t st; + + create_dir("dir", 0755); + create_file("reg", 0644); + + RE(atf_fs_path_init_fmt(&p, "dir")); + RE(atf_fs_stat_init(&st, &p)); + ATF_REQUIRE_EQ(atf_fs_stat_get_type(&st), atf_fs_stat_dir_type); + atf_fs_stat_fini(&st); + atf_fs_path_fini(&p); + + RE(atf_fs_path_init_fmt(&p, "reg")); + RE(atf_fs_stat_init(&st, &p)); + ATF_REQUIRE_EQ(atf_fs_stat_get_type(&st), atf_fs_stat_reg_type); + atf_fs_stat_fini(&st); + atf_fs_path_fini(&p); +} + +ATF_TC(stat_perms); +ATF_TC_HEAD(stat_perms, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_is_* functions"); +} +ATF_TC_BODY(stat_perms, tc) +{ + atf_fs_path_t p; + atf_fs_stat_t st; + + create_file("reg", 0); + + RE(atf_fs_path_init_fmt(&p, "reg")); + +#define perms(ur, uw, ux, gr, gw, gx, othr, othw, othx) \ + { \ + RE(atf_fs_stat_init(&st, &p)); \ + ATF_REQUIRE(atf_fs_stat_is_owner_readable(&st) == ur); \ + ATF_REQUIRE(atf_fs_stat_is_owner_writable(&st) == uw); \ + ATF_REQUIRE(atf_fs_stat_is_owner_executable(&st) == ux); \ + ATF_REQUIRE(atf_fs_stat_is_group_readable(&st) == gr); \ + ATF_REQUIRE(atf_fs_stat_is_group_writable(&st) == gw); \ + ATF_REQUIRE(atf_fs_stat_is_group_executable(&st) == gx); \ + ATF_REQUIRE(atf_fs_stat_is_other_readable(&st) == othr); \ + ATF_REQUIRE(atf_fs_stat_is_other_writable(&st) == othw); \ + ATF_REQUIRE(atf_fs_stat_is_other_executable(&st) == othx); \ + atf_fs_stat_fini(&st); \ + } + + chmod("reg", 0000); + perms(false, false, false, false, false, false, false, false, false); + + chmod("reg", 0001); + perms(false, false, false, false, false, false, false, false, true); + + chmod("reg", 0010); + perms(false, false, false, false, false, true, false, false, false); + + chmod("reg", 0100); + perms(false, false, true, false, false, false, false, false, false); + + chmod("reg", 0002); + perms(false, false, false, false, false, false, false, true, false); + + chmod("reg", 0020); + perms(false, false, false, false, true, false, false, false, false); + + chmod("reg", 0200); + perms(false, true, false, false, false, false, false, false, false); + + chmod("reg", 0004); + perms(false, false, false, false, false, false, true, false, false); + + chmod("reg", 0040); + perms(false, false, false, true, false, false, false, false, false); + + chmod("reg", 0400); + perms(true, false, false, false, false, false, false, false, false); + + chmod("reg", 0644); + perms(true, true, false, true, false, false, true, false, false); + + chmod("reg", 0755); + perms(true, true, true, true, false, true, true, false, true); + + chmod("reg", 0777); + perms(true, true, true, true, true, true, true, true, true); + +#undef perms + + atf_fs_path_fini(&p); +} + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(exists); +ATF_TC_HEAD(exists, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_exists function"); +} +ATF_TC_BODY(exists, tc) +{ + atf_error_t err; + atf_fs_path_t pdir, pfile; + bool b; + + RE(atf_fs_path_init_fmt(&pdir, "dir")); + RE(atf_fs_path_init_fmt(&pfile, "dir/file")); + + create_dir(atf_fs_path_cstring(&pdir), 0755); + create_file(atf_fs_path_cstring(&pfile), 0644); + + printf("Checking existence of a directory\n"); + RE(atf_fs_exists(&pdir, &b)); + ATF_REQUIRE(b); + + printf("Checking existence of a file\n"); + RE(atf_fs_exists(&pfile, &b)); + ATF_REQUIRE(b); + + /* XXX: This should probably be a separate test case to let the user + * be aware that some tests were skipped because privileges were not + * correct. */ + if (!atf_user_is_root()) { + printf("Checking existence of a file inside a directory without " + "permissions\n"); + ATF_REQUIRE(chmod(atf_fs_path_cstring(&pdir), 0000) != -1); + err = atf_fs_exists(&pfile, &b); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE(chmod(atf_fs_path_cstring(&pdir), 0755) != -1); + atf_error_free(err); + } + + printf("Checking existence of a non-existent file\n"); + ATF_REQUIRE(unlink(atf_fs_path_cstring(&pfile)) != -1); + RE(atf_fs_exists(&pfile, &b)); + ATF_REQUIRE(!b); + + atf_fs_path_fini(&pfile); + atf_fs_path_fini(&pdir); +} + +ATF_TC(eaccess); +ATF_TC_HEAD(eaccess, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_eaccess function"); +} +ATF_TC_BODY(eaccess, tc) +{ + const int modes[] = { atf_fs_access_f, atf_fs_access_r, atf_fs_access_w, + atf_fs_access_x, 0 }; + const int *m; + struct tests { + mode_t fmode; + int amode; + int uerror; + int rerror; + } tests[] = { + { 0000, atf_fs_access_r, EACCES, 0 }, + { 0000, atf_fs_access_w, EACCES, 0 }, + { 0000, atf_fs_access_x, EACCES, EACCES }, + + { 0001, atf_fs_access_r, EACCES, 0 }, + { 0001, atf_fs_access_w, EACCES, 0 }, + { 0001, atf_fs_access_x, EACCES, 0 }, + { 0002, atf_fs_access_r, EACCES, 0 }, + { 0002, atf_fs_access_w, EACCES, 0 }, + { 0002, atf_fs_access_x, EACCES, EACCES }, + { 0004, atf_fs_access_r, EACCES, 0 }, + { 0004, atf_fs_access_w, EACCES, 0 }, + { 0004, atf_fs_access_x, EACCES, EACCES }, + + { 0010, atf_fs_access_r, EACCES, 0 }, + { 0010, atf_fs_access_w, EACCES, 0 }, + { 0010, atf_fs_access_x, 0, 0 }, + { 0020, atf_fs_access_r, EACCES, 0 }, + { 0020, atf_fs_access_w, 0, 0 }, + { 0020, atf_fs_access_x, EACCES, EACCES }, + { 0040, atf_fs_access_r, 0, 0 }, + { 0040, atf_fs_access_w, EACCES, 0 }, + { 0040, atf_fs_access_x, EACCES, EACCES }, + + { 0100, atf_fs_access_r, EACCES, 0 }, + { 0100, atf_fs_access_w, EACCES, 0 }, + { 0100, atf_fs_access_x, 0, 0 }, + { 0200, atf_fs_access_r, EACCES, 0 }, + { 0200, atf_fs_access_w, 0, 0 }, + { 0200, atf_fs_access_x, EACCES, EACCES }, + { 0400, atf_fs_access_r, 0, 0 }, + { 0400, atf_fs_access_w, EACCES, 0 }, + { 0400, atf_fs_access_x, EACCES, EACCES }, + + { 0, 0, 0, 0 } + }; + struct tests *t; + atf_fs_path_t p; + atf_error_t err; + + RE(atf_fs_path_init_fmt(&p, "the-file")); + + printf("Non-existent file checks\n"); + for (m = &modes[0]; *m != 0; m++) { + err = atf_fs_eaccess(&p, *m); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE_EQ(atf_libc_error_code(err), ENOENT); + atf_error_free(err); + } + + create_file(atf_fs_path_cstring(&p), 0000); + ATF_REQUIRE(chown(atf_fs_path_cstring(&p), geteuid(), getegid()) != -1); + + for (t = &tests[0]; t->amode != 0; t++) { + const int experr = atf_user_is_root() ? t->rerror : t->uerror; + + printf("\n"); + printf("File mode : %04o\n", (unsigned int)t->fmode); + printf("Access mode : 0x%02x\n", t->amode); + + ATF_REQUIRE(chmod(atf_fs_path_cstring(&p), t->fmode) != -1); + + /* First, existence check. */ + err = atf_fs_eaccess(&p, atf_fs_access_f); + ATF_REQUIRE(!atf_is_error(err)); + + /* Now do the specific test case. */ + printf("Expected error: %d\n", experr); + err = atf_fs_eaccess(&p, t->amode); + if (atf_is_error(err)) { + if (atf_error_is(err, "libc")) + printf("Error : %d\n", atf_libc_error_code(err)); + else + printf("Error : Non-libc error\n"); + } else + printf("Error : None\n"); + if (experr == 0) { + ATF_REQUIRE(!atf_is_error(err)); + } else { + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE_EQ(atf_libc_error_code(err), experr); + atf_error_free(err); + } + } + + atf_fs_path_fini(&p); +} + +ATF_TC(getcwd); +ATF_TC_HEAD(getcwd, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_getcwd function"); +} +ATF_TC_BODY(getcwd, tc) +{ + atf_fs_path_t cwd1, cwd2; + + create_dir ("root", 0755); + + RE(atf_fs_getcwd(&cwd1)); + ATF_REQUIRE(chdir("root") != -1); + RE(atf_fs_getcwd(&cwd2)); + + RE(atf_fs_path_append_fmt(&cwd1, "root")); + + ATF_REQUIRE(atf_equal_fs_path_fs_path(&cwd1, &cwd2)); + + atf_fs_path_fini(&cwd2); + atf_fs_path_fini(&cwd1); +} + +ATF_TC(rmdir_empty); +ATF_TC_HEAD(rmdir_empty, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function"); +} +ATF_TC_BODY(rmdir_empty, tc) +{ + atf_fs_path_t p; + + RE(atf_fs_path_init_fmt(&p, "test-dir")); + + ATF_REQUIRE(mkdir("test-dir", 0755) != -1); + ATF_REQUIRE(exists(&p)); + RE(atf_fs_rmdir(&p)); + ATF_REQUIRE(!exists(&p)); + + atf_fs_path_fini(&p); +} + +ATF_TC(rmdir_enotempty); +ATF_TC_HEAD(rmdir_enotempty, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function"); +} +ATF_TC_BODY(rmdir_enotempty, tc) +{ + atf_fs_path_t p; + atf_error_t err; + + RE(atf_fs_path_init_fmt(&p, "test-dir")); + + ATF_REQUIRE(mkdir("test-dir", 0755) != -1); + ATF_REQUIRE(exists(&p)); + create_file("test-dir/foo", 0644); + + err = atf_fs_rmdir(&p); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE_EQ(atf_libc_error_code(err), ENOTEMPTY); + atf_error_free(err); + + atf_fs_path_fini(&p); +} + +ATF_TC(rmdir_eperm); +ATF_TC_HEAD(rmdir_eperm, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function"); +} +ATF_TC_BODY(rmdir_eperm, tc) +{ + atf_fs_path_t p; + atf_error_t err; + + RE(atf_fs_path_init_fmt(&p, "test-dir/foo")); + + ATF_REQUIRE(mkdir("test-dir", 0755) != -1); + ATF_REQUIRE(mkdir("test-dir/foo", 0755) != -1); + ATF_REQUIRE(chmod("test-dir", 0555) != -1); + ATF_REQUIRE(exists(&p)); + + err = atf_fs_rmdir(&p); + if (atf_user_is_root()) { + ATF_REQUIRE(!atf_is_error(err)); + } else { + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE_EQ(atf_libc_error_code(err), EACCES); + atf_error_free(err); + } + + atf_fs_path_fini(&p); +} + +ATF_TC(mkdtemp_ok); +ATF_TC_HEAD(mkdtemp_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function, " + "successful execution"); +} +ATF_TC_BODY(mkdtemp_ok, tc) +{ + atf_fs_path_t p1, p2; + atf_fs_stat_t s1, s2; + + RE(atf_fs_path_init_fmt(&p1, "testdir.XXXXXX")); + RE(atf_fs_path_init_fmt(&p2, "testdir.XXXXXX")); + RE(atf_fs_mkdtemp(&p1)); + RE(atf_fs_mkdtemp(&p2)); + ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2)); + ATF_REQUIRE(exists(&p1)); + ATF_REQUIRE(exists(&p2)); + + RE(atf_fs_stat_init(&s1, &p1)); + ATF_REQUIRE_EQ(atf_fs_stat_get_type(&s1), atf_fs_stat_dir_type); + ATF_REQUIRE( atf_fs_stat_is_owner_readable(&s1)); + ATF_REQUIRE( atf_fs_stat_is_owner_writable(&s1)); + ATF_REQUIRE( atf_fs_stat_is_owner_executable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_group_readable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_group_writable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_group_executable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_other_readable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_other_writable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_other_executable(&s1)); + + RE(atf_fs_stat_init(&s2, &p2)); + ATF_REQUIRE_EQ(atf_fs_stat_get_type(&s2), atf_fs_stat_dir_type); + ATF_REQUIRE( atf_fs_stat_is_owner_readable(&s2)); + ATF_REQUIRE( atf_fs_stat_is_owner_writable(&s2)); + ATF_REQUIRE( atf_fs_stat_is_owner_executable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_group_readable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_group_writable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_group_executable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_other_readable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_other_writable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_other_executable(&s2)); + + atf_fs_stat_fini(&s2); + atf_fs_stat_fini(&s1); + atf_fs_path_fini(&p2); + atf_fs_path_fini(&p1); +} + +ATF_TC(mkdtemp_err); +ATF_TC_HEAD(mkdtemp_err, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function, " + "error conditions"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(mkdtemp_err, tc) +{ + atf_error_t err; + atf_fs_path_t p; + + ATF_REQUIRE(mkdir("dir", 0555) != -1); + + RE(atf_fs_path_init_fmt(&p, "dir/testdir.XXXXXX")); + + err = atf_fs_mkdtemp(&p); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_CHECK_EQ(atf_libc_error_code(err), EACCES); + atf_error_free(err); + + ATF_CHECK(!exists(&p)); + ATF_CHECK(strcmp(atf_fs_path_cstring(&p), "dir/testdir.XXXXXX") == 0); + + atf_fs_path_fini(&p); +} + +static +void +do_umask_check(atf_error_t (*const mk_func)(atf_fs_path_t *), + atf_fs_path_t *path, const mode_t test_mask, + const char *str_mask, const char *exp_name) +{ + char buf[1024]; + int old_umask; + atf_error_t err; + + printf("Creating temporary %s with umask %s\n", exp_name, str_mask); + + old_umask = umask(test_mask); + err = mk_func(path); + (void)umask(old_umask); + + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "invalid_umask")); + atf_error_format(err, buf, sizeof(buf)); + ATF_CHECK(strstr(buf, exp_name) != NULL); + ATF_CHECK(strstr(buf, str_mask) != NULL); + atf_error_free(err); +} + +ATF_TC(mkdtemp_umask); +ATF_TC_HEAD(mkdtemp_umask, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function " + "causing an error due to a too strict umask"); +} +ATF_TC_BODY(mkdtemp_umask, tc) +{ + atf_fs_path_t p; + + RE(atf_fs_path_init_fmt(&p, "testdir.XXXXXX")); + + do_umask_check(atf_fs_mkdtemp, &p, 00100, "00100", "directory"); + do_umask_check(atf_fs_mkdtemp, &p, 00200, "00200", "directory"); + do_umask_check(atf_fs_mkdtemp, &p, 00400, "00400", "directory"); + do_umask_check(atf_fs_mkdtemp, &p, 00500, "00500", "directory"); + do_umask_check(atf_fs_mkdtemp, &p, 00600, "00600", "directory"); + + atf_fs_path_fini(&p); +} + +ATF_TC(mkstemp_ok); +ATF_TC_HEAD(mkstemp_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function, " + "successful execution"); +} +ATF_TC_BODY(mkstemp_ok, tc) +{ + int fd1, fd2; + atf_fs_path_t p1, p2; + atf_fs_stat_t s1, s2; + + RE(atf_fs_path_init_fmt(&p1, "testfile.XXXXXX")); + RE(atf_fs_path_init_fmt(&p2, "testfile.XXXXXX")); + fd1 = fd2 = -1; + RE(atf_fs_mkstemp(&p1, &fd1)); + RE(atf_fs_mkstemp(&p2, &fd2)); + ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2)); + ATF_REQUIRE(exists(&p1)); + ATF_REQUIRE(exists(&p2)); + + ATF_CHECK(fd1 != -1); + ATF_CHECK(fd2 != -1); + ATF_CHECK(write(fd1, "foo", 3) == 3); + ATF_CHECK(write(fd2, "bar", 3) == 3); + close(fd1); + close(fd2); + + RE(atf_fs_stat_init(&s1, &p1)); + ATF_CHECK_EQ(atf_fs_stat_get_type(&s1), atf_fs_stat_reg_type); + ATF_CHECK( atf_fs_stat_is_owner_readable(&s1)); + ATF_CHECK( atf_fs_stat_is_owner_writable(&s1)); + ATF_CHECK(!atf_fs_stat_is_owner_executable(&s1)); + ATF_CHECK(!atf_fs_stat_is_group_readable(&s1)); + ATF_CHECK(!atf_fs_stat_is_group_writable(&s1)); + ATF_CHECK(!atf_fs_stat_is_group_executable(&s1)); + ATF_CHECK(!atf_fs_stat_is_other_readable(&s1)); + ATF_CHECK(!atf_fs_stat_is_other_writable(&s1)); + ATF_CHECK(!atf_fs_stat_is_other_executable(&s1)); + + RE(atf_fs_stat_init(&s2, &p2)); + ATF_CHECK_EQ(atf_fs_stat_get_type(&s2), atf_fs_stat_reg_type); + ATF_CHECK( atf_fs_stat_is_owner_readable(&s2)); + ATF_CHECK( atf_fs_stat_is_owner_writable(&s2)); + ATF_CHECK(!atf_fs_stat_is_owner_executable(&s2)); + ATF_CHECK(!atf_fs_stat_is_group_readable(&s2)); + ATF_CHECK(!atf_fs_stat_is_group_writable(&s2)); + ATF_CHECK(!atf_fs_stat_is_group_executable(&s2)); + ATF_CHECK(!atf_fs_stat_is_other_readable(&s2)); + ATF_CHECK(!atf_fs_stat_is_other_writable(&s2)); + ATF_CHECK(!atf_fs_stat_is_other_executable(&s2)); + + atf_fs_stat_fini(&s2); + atf_fs_stat_fini(&s1); + atf_fs_path_fini(&p2); + atf_fs_path_fini(&p1); +} + +ATF_TC(mkstemp_err); +ATF_TC_HEAD(mkstemp_err, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function, " + "error conditions"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(mkstemp_err, tc) +{ + int fd; + atf_error_t err; + atf_fs_path_t p; + + ATF_REQUIRE(mkdir("dir", 0555) != -1); + + RE(atf_fs_path_init_fmt(&p, "dir/testfile.XXXXXX")); + fd = 1234; + + err = atf_fs_mkstemp(&p, &fd); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_CHECK_EQ(atf_libc_error_code(err), EACCES); + atf_error_free(err); + + ATF_CHECK(!exists(&p)); + ATF_CHECK(strcmp(atf_fs_path_cstring(&p), "dir/testfile.XXXXXX") == 0); + ATF_CHECK_EQ(fd, 1234); + + atf_fs_path_fini(&p); +} + +ATF_TC(mkstemp_umask); +ATF_TC_HEAD(mkstemp_umask, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function " + "causing an error due to a too strict umask"); +} +ATF_TC_BODY(mkstemp_umask, tc) +{ + atf_fs_path_t p; + + RE(atf_fs_path_init_fmt(&p, "testfile.XXXXXX")); + + do_umask_check(mkstemp_discard_fd, &p, 00100, "00100", "regular file"); + do_umask_check(mkstemp_discard_fd, &p, 00200, "00200", "regular file"); + do_umask_check(mkstemp_discard_fd, &p, 00400, "00400", "regular file"); + + atf_fs_path_fini(&p); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Add the tests for the "atf_fs_path" type. */ + ATF_TP_ADD_TC(tp, path_normalize); + ATF_TP_ADD_TC(tp, path_copy); + ATF_TP_ADD_TC(tp, path_is_absolute); + ATF_TP_ADD_TC(tp, path_is_root); + ATF_TP_ADD_TC(tp, path_branch_path); + ATF_TP_ADD_TC(tp, path_leaf_name); + ATF_TP_ADD_TC(tp, path_append); + ATF_TP_ADD_TC(tp, path_to_absolute); + ATF_TP_ADD_TC(tp, path_equal); + + /* Add the tests for the "atf_fs_stat" type. */ + ATF_TP_ADD_TC(tp, stat_mode); + ATF_TP_ADD_TC(tp, stat_type); + ATF_TP_ADD_TC(tp, stat_perms); + + /* Add the tests for the free functions. */ + ATF_TP_ADD_TC(tp, eaccess); + ATF_TP_ADD_TC(tp, exists); + ATF_TP_ADD_TC(tp, getcwd); + ATF_TP_ADD_TC(tp, rmdir_empty); + ATF_TP_ADD_TC(tp, rmdir_enotempty); + ATF_TP_ADD_TC(tp, rmdir_eperm); + ATF_TP_ADD_TC(tp, mkdtemp_ok); + ATF_TP_ADD_TC(tp, mkdtemp_err); + ATF_TP_ADD_TC(tp, mkdtemp_umask); + ATF_TP_ADD_TC(tp, mkstemp_ok); + ATF_TP_ADD_TC(tp, mkstemp_err); + ATF_TP_ADD_TC(tp, mkstemp_umask); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/list.c b/unit/atf-src/atf-c/detail/list.c new file mode 100644 index 0000000..d14216e --- /dev/null +++ b/unit/atf-src/atf-c/detail/list.c @@ -0,0 +1,388 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/list.h" + +#include <stdlib.h> +#include <string.h> + +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" +#include "atf-c/utils.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +struct list_entry { + struct list_entry *m_prev; + struct list_entry *m_next; + void *m_object; + bool m_managed; +}; + +static +atf_list_citer_t +entry_to_citer(const atf_list_t *l, const struct list_entry *le) +{ + atf_list_citer_t iter; + iter.m_list = l; + iter.m_entry = le; + return iter; +} + +static +atf_list_iter_t +entry_to_iter(atf_list_t *l, struct list_entry *le) +{ + atf_list_iter_t iter; + iter.m_list = l; + iter.m_entry = le; + return iter; +} + +static +struct list_entry * +new_entry(void *object, bool managed) +{ + struct list_entry *le; + + le = (struct list_entry *)malloc(sizeof(*le)); + if (le != NULL) { + le->m_prev = le->m_next = NULL; + le->m_object = object; + le->m_managed = managed; + } else + free(object); + + return le; +} + +static +void +delete_entry(struct list_entry *le) +{ + if (le->m_managed) + free(le->m_object); + + free(le); +} + +static +struct list_entry * +new_entry_and_link(void *object, bool managed, struct list_entry *prev, + struct list_entry *next) +{ + struct list_entry *le; + + le = new_entry(object, managed); + if (le != NULL) { + le->m_prev = prev; + le->m_next = next; + + prev->m_next = le; + next->m_prev = le; + } + + return le; +} + +/* --------------------------------------------------------------------- + * The "atf_list_citer" type. + * --------------------------------------------------------------------- */ + +/* + * Getters. + */ + +const void * +atf_list_citer_data(const atf_list_citer_t citer) +{ + const struct list_entry *le = citer.m_entry; + PRE(le != NULL); + return le->m_object; +} + +atf_list_citer_t +atf_list_citer_next(const atf_list_citer_t citer) +{ + const struct list_entry *le = citer.m_entry; + atf_list_citer_t newciter; + + PRE(le != NULL); + + newciter = citer; + newciter.m_entry = le->m_next; + + return newciter; +} + +bool +atf_equal_list_citer_list_citer(const atf_list_citer_t i1, + const atf_list_citer_t i2) +{ + return i1.m_list == i2.m_list && i1.m_entry == i2.m_entry; +} + +/* --------------------------------------------------------------------- + * The "atf_list_iter" type. + * --------------------------------------------------------------------- */ + +/* + * Getters. + */ + +void * +atf_list_iter_data(const atf_list_iter_t iter) +{ + const struct list_entry *le = iter.m_entry; + PRE(le != NULL); + return le->m_object; +} + +atf_list_iter_t +atf_list_iter_next(const atf_list_iter_t iter) +{ + const struct list_entry *le = iter.m_entry; + atf_list_iter_t newiter; + + PRE(le != NULL); + + newiter = iter; + newiter.m_entry = le->m_next; + + return newiter; +} + +bool +atf_equal_list_iter_list_iter(const atf_list_iter_t i1, + const atf_list_iter_t i2) +{ + return i1.m_list == i2.m_list && i1.m_entry == i2.m_entry; +} + +/* --------------------------------------------------------------------- + * The "atf_list" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors and destructors. + */ + +atf_error_t +atf_list_init(atf_list_t *l) +{ + struct list_entry *lebeg, *leend; + + lebeg = new_entry(NULL, false); + if (lebeg == NULL) { + return atf_no_memory_error(); + } + + leend = new_entry(NULL, false); + if (leend == NULL) { + free(lebeg); + return atf_no_memory_error(); + } + + lebeg->m_next = leend; + lebeg->m_prev = NULL; + + leend->m_next = NULL; + leend->m_prev = lebeg; + + l->m_size = 0; + l->m_begin = lebeg; + l->m_end = leend; + + return atf_no_error(); +} + +void +atf_list_fini(atf_list_t *l) +{ + struct list_entry *le; + size_t freed; + + le = (struct list_entry *)l->m_begin; + freed = 0; + while (le != NULL) { + struct list_entry *lenext; + + lenext = le->m_next; + delete_entry(le); + le = lenext; + + freed++; + } + INV(freed == l->m_size + 2); +} + +/* + * Getters. + */ + +atf_list_iter_t +atf_list_begin(atf_list_t *l) +{ + struct list_entry *le = l->m_begin; + return entry_to_iter(l, le->m_next); +} + +atf_list_citer_t +atf_list_begin_c(const atf_list_t *l) +{ + const struct list_entry *le = l->m_begin; + return entry_to_citer(l, le->m_next); +} + +atf_list_iter_t +atf_list_end(atf_list_t *l) +{ + return entry_to_iter(l, l->m_end); +} + +atf_list_citer_t +atf_list_end_c(const atf_list_t *l) +{ + return entry_to_citer(l, l->m_end); +} + +void * +atf_list_index(atf_list_t *list, const size_t idx) +{ + atf_list_iter_t iter; + + PRE(idx < atf_list_size(list)); + + iter = atf_list_begin(list); + { + size_t pos = 0; + while (pos < idx && + !atf_equal_list_iter_list_iter((iter), atf_list_end(list))) { + iter = atf_list_iter_next(iter); + pos++; + } + } + return atf_list_iter_data(iter); +} + +const void * +atf_list_index_c(const atf_list_t *list, const size_t idx) +{ + atf_list_citer_t iter; + + PRE(idx < atf_list_size(list)); + + iter = atf_list_begin_c(list); + { + size_t pos = 0; + while (pos < idx && + !atf_equal_list_citer_list_citer((iter), + atf_list_end_c(list))) { + iter = atf_list_citer_next(iter); + pos++; + } + } + return atf_list_citer_data(iter); +} + +size_t +atf_list_size(const atf_list_t *l) +{ + return l->m_size; +} + +char ** +atf_list_to_charpp(const atf_list_t *l) +{ + char **array; + atf_list_citer_t iter; + size_t i; + + array = malloc(sizeof(char *) * (atf_list_size(l) + 1)); + if (array == NULL) + goto out; + + i = 0; + atf_list_for_each_c(iter, l) { + array[i] = strdup((const char *)atf_list_citer_data(iter)); + if (array[i] == NULL) { + atf_utils_free_charpp(array); + array = NULL; + goto out; + } + + i++; + } + array[i] = NULL; + +out: + return array; +} + +/* + * Modifiers. + */ + +atf_error_t +atf_list_append(atf_list_t *l, void *data, bool managed) +{ + struct list_entry *le, *next, *prev; + atf_error_t err; + + next = (struct list_entry *)l->m_end; + prev = next->m_prev; + le = new_entry_and_link(data, managed, prev, next); + if (le == NULL) + err = atf_no_memory_error(); + else { + l->m_size++; + err = atf_no_error(); + } + + return err; +} + +void +atf_list_append_list(atf_list_t *l, atf_list_t *src) +{ + struct list_entry *e1, *e2, *ghost1, *ghost2; + + ghost1 = (struct list_entry *)l->m_end; + ghost2 = (struct list_entry *)src->m_begin; + + e1 = ghost1->m_prev; + e2 = ghost2->m_next; + + delete_entry(ghost1); + delete_entry(ghost2); + + e1->m_next = e2; + e2->m_prev = e1; + + l->m_end = src->m_end; + l->m_size += src->m_size; +} diff --git a/unit/atf-src/atf-c/detail/list.h b/unit/atf-src/atf-c/detail/list.h new file mode 100644 index 0000000..b250066 --- /dev/null +++ b/unit/atf-src/atf-c/detail/list.h @@ -0,0 +1,111 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_LIST_H) +#define ATF_C_DETAIL_LIST_H + +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> + +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_list_citer" type. + * --------------------------------------------------------------------- */ + +struct atf_list_citer { + const struct atf_list *m_list; + const void *m_entry; +}; +typedef struct atf_list_citer atf_list_citer_t; + +/* Getters. */ +const void *atf_list_citer_data(const atf_list_citer_t); +atf_list_citer_t atf_list_citer_next(const atf_list_citer_t); + +/* Operators. */ +bool atf_equal_list_citer_list_citer(const atf_list_citer_t, + const atf_list_citer_t); + +/* --------------------------------------------------------------------- + * The "atf_list_iter" type. + * --------------------------------------------------------------------- */ + +struct atf_list_iter { + struct atf_list *m_list; + void *m_entry; +}; +typedef struct atf_list_iter atf_list_iter_t; + +/* Getters. */ +void *atf_list_iter_data(const atf_list_iter_t); +atf_list_iter_t atf_list_iter_next(const atf_list_iter_t); + +/* Operators. */ +bool atf_equal_list_iter_list_iter(const atf_list_iter_t, + const atf_list_iter_t); + +/* --------------------------------------------------------------------- + * The "atf_list" type. + * --------------------------------------------------------------------- */ + +struct atf_list { + void *m_begin; + void *m_end; + + size_t m_size; +}; +typedef struct atf_list atf_list_t; + +/* Constructors and destructors */ +atf_error_t atf_list_init(atf_list_t *); +void atf_list_fini(atf_list_t *); + +/* Getters. */ +atf_list_iter_t atf_list_begin(atf_list_t *); +atf_list_citer_t atf_list_begin_c(const atf_list_t *); +atf_list_iter_t atf_list_end(atf_list_t *); +atf_list_citer_t atf_list_end_c(const atf_list_t *); +void *atf_list_index(atf_list_t *, const size_t); +const void *atf_list_index_c(const atf_list_t *, const size_t); +size_t atf_list_size(const atf_list_t *); +char **atf_list_to_charpp(const atf_list_t *); + +/* Modifiers. */ +atf_error_t atf_list_append(atf_list_t *, void *, bool); +void atf_list_append_list(atf_list_t *, atf_list_t *); + +/* Macros. */ +#define atf_list_for_each(iter, list) \ + for (iter = atf_list_begin(list); \ + !atf_equal_list_iter_list_iter((iter), atf_list_end(list)); \ + iter = atf_list_iter_next(iter)) +#define atf_list_for_each_c(iter, list) \ + for (iter = atf_list_begin_c(list); \ + !atf_equal_list_citer_list_citer((iter), atf_list_end_c(list)); \ + iter = atf_list_citer_next(iter)) + +#endif /* !defined(ATF_C_DETAIL_LIST_H) */ diff --git a/unit/atf-src/atf-c/detail/list_test.c b/unit/atf-src/atf-c/detail/list_test.c new file mode 100644 index 0000000..82a715d --- /dev/null +++ b/unit/atf-src/atf-c/detail/list_test.c @@ -0,0 +1,365 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/list.h" + +#include <stdio.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" +#include "atf-c/utils.h" + +/* --------------------------------------------------------------------- + * Tests for the "atf_list" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors and destructors. + */ + +ATF_TC(list_init); +ATF_TC_HEAD(list_init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_init function"); +} +ATF_TC_BODY(list_init, tc) +{ + atf_list_t list; + + RE(atf_list_init(&list)); + ATF_REQUIRE_EQ(atf_list_size(&list), 0); + atf_list_fini(&list); +} + +/* + * Getters. + */ + +ATF_TC(list_index); +ATF_TC_HEAD(list_index, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_index function"); +} +ATF_TC_BODY(list_index, tc) +{ + atf_list_t list; + int i1 = 1; + int i2 = 5; + int i3 = 9; + + RE(atf_list_init(&list)); + RE(atf_list_append(&list, &i1, false)); + RE(atf_list_append(&list, &i2, false)); + RE(atf_list_append(&list, &i3, false)); + + ATF_CHECK_EQ(*(int *)atf_list_index(&list, 0), 1); + ATF_CHECK_EQ(*(int *)atf_list_index(&list, 1), 5); + ATF_CHECK_EQ(*(int *)atf_list_index(&list, 2), 9); + + atf_list_fini(&list); +} + +ATF_TC(list_index_c); +ATF_TC_HEAD(list_index_c, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_index_c function"); +} +ATF_TC_BODY(list_index_c, tc) +{ + atf_list_t list; + int i1 = 1; + int i2 = 5; + int i3 = 9; + + RE(atf_list_init(&list)); + RE(atf_list_append(&list, &i1, false)); + RE(atf_list_append(&list, &i2, false)); + RE(atf_list_append(&list, &i3, false)); + + ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 0), 1); + ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 1), 5); + ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 2), 9); + + atf_list_fini(&list); +} + +ATF_TC_WITHOUT_HEAD(list_to_charpp_empty); +ATF_TC_BODY(list_to_charpp_empty, tc) +{ + atf_list_t list; + char **array; + + RE(atf_list_init(&list)); + ATF_REQUIRE((array = atf_list_to_charpp(&list)) != NULL); + atf_list_fini(&list); + + ATF_CHECK_EQ(NULL, array[0]); + atf_utils_free_charpp(array); +} + +ATF_TC_WITHOUT_HEAD(list_to_charpp_some); +ATF_TC_BODY(list_to_charpp_some, tc) +{ + atf_list_t list; + char **array; + + char s1[] = "one"; + char s2[] = "two"; + char s3[] = "three"; + + RE(atf_list_init(&list)); + RE(atf_list_append(&list, s1, false)); + RE(atf_list_append(&list, s2, false)); + RE(atf_list_append(&list, s3, false)); + ATF_REQUIRE((array = atf_list_to_charpp(&list)) != NULL); + atf_list_fini(&list); + + ATF_CHECK_STREQ("one", array[0]); + ATF_CHECK_STREQ("two", array[1]); + ATF_CHECK_STREQ("three", array[2]); + ATF_CHECK_EQ(NULL, array[3]); + atf_utils_free_charpp(array); +} + +/* + * Modifiers. + */ + +ATF_TC(list_append); +ATF_TC_HEAD(list_append, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_append function"); +} +ATF_TC_BODY(list_append, tc) +{ + atf_list_t list; + size_t i; + char buf[] = "Test string"; + + RE(atf_list_init(&list)); + for (i = 0; i < 1024; i++) { + ATF_REQUIRE_EQ(atf_list_size(&list), i); + RE(atf_list_append(&list, buf, false)); + } + atf_list_fini(&list); +} + +ATF_TC(list_append_list); +ATF_TC_HEAD(list_append_list, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_append_list " + "function"); +} +ATF_TC_BODY(list_append_list, tc) +{ + { + atf_list_t l1, l2; + + RE(atf_list_init(&l1)); + RE(atf_list_init(&l2)); + + atf_list_append_list(&l1, &l2); + ATF_CHECK_EQ(atf_list_size(&l1), 0); + + atf_list_fini(&l1); + } + + { + atf_list_t l1, l2; + int item = 5; + + RE(atf_list_init(&l1)); + RE(atf_list_append(&l1, &item, false)); + RE(atf_list_init(&l2)); + + atf_list_append_list(&l1, &l2); + ATF_CHECK_EQ(atf_list_size(&l1), 1); + ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item); + + atf_list_fini(&l1); + } + + { + atf_list_t l1, l2; + int item = 5; + + RE(atf_list_init(&l1)); + RE(atf_list_init(&l2)); + RE(atf_list_append(&l2, &item, false)); + + atf_list_append_list(&l1, &l2); + ATF_CHECK_EQ(atf_list_size(&l1), 1); + ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item); + + atf_list_fini(&l1); + } + + { + atf_list_t l1, l2; + int item1 = 5; + int item2 = 9; + + RE(atf_list_init(&l1)); + RE(atf_list_append(&l1, &item1, false)); + RE(atf_list_init(&l2)); + RE(atf_list_append(&l2, &item2, false)); + + atf_list_append_list(&l1, &l2); + ATF_CHECK_EQ(atf_list_size(&l1), 2); + ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item1); + ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 1), item2); + + atf_list_fini(&l1); + } + + { + atf_list_t l1, l2; + atf_list_citer_t end1, end2; + + RE(atf_list_init(&l1)); + RE(atf_list_init(&l2)); + + end1 = atf_list_end_c(&l1); + end2 = atf_list_end_c(&l2); + /* XXX Shouldn't query m_entry here. */ + ATF_CHECK(end1.m_entry != end2.m_entry); + + atf_list_append_list(&l1, &l2); + ATF_CHECK(atf_list_end_c(&l1).m_entry == end2.m_entry); + + atf_list_fini(&l1); + } +} + +/* + * Macros. + */ + +ATF_TC(list_for_each); +ATF_TC_HEAD(list_for_each, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_for_each macro"); +} +ATF_TC_BODY(list_for_each, tc) +{ + atf_list_t list; + atf_list_iter_t iter; + size_t count, i, size; + int nums[10]; + + printf("Iterating over empty list\n"); + RE(atf_list_init(&list)); + count = 0; + atf_list_for_each(iter, &list) { + count++; + printf("Item count is now %zd\n", count); + } + ATF_REQUIRE_EQ(count, 0); + atf_list_fini(&list); + + for (size = 0; size <= 10; size++) { + printf("Iterating over list of %zd elements\n", size); + RE(atf_list_init(&list)); + for (i = 0; i < size; i++) { + nums[i] = i + 1; + RE(atf_list_append(&list, &nums[i], false)); + } + count = 0; + atf_list_for_each(iter, &list) { + printf("Retrieved item: %d\n", *(int *)atf_list_iter_data(iter)); + count++; + } + ATF_REQUIRE_EQ(count, size); + atf_list_fini(&list); + } +} + +ATF_TC(list_for_each_c); +ATF_TC_HEAD(list_for_each_c, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_for_each_c macro"); +} +ATF_TC_BODY(list_for_each_c, tc) +{ + atf_list_t list; + atf_list_citer_t iter; + size_t count, i, size; + int nums[10]; + + printf("Iterating over empty list\n"); + RE(atf_list_init(&list)); + count = 0; + atf_list_for_each_c(iter, &list) { + count++; + printf("Item count is now %zd\n", count); + } + ATF_REQUIRE_EQ(count, 0); + atf_list_fini(&list); + + for (size = 0; size <= 10; size++) { + printf("Iterating over list of %zd elements\n", size); + RE(atf_list_init(&list)); + for (i = 0; i < size; i++) { + nums[i] = i + 1; + RE(atf_list_append(&list, &nums[i], false)); + } + count = 0; + atf_list_for_each_c(iter, &list) { + printf("Retrieved item: %d\n", + *(const int *)atf_list_citer_data(iter)); + count++; + } + ATF_REQUIRE_EQ(count, size); + atf_list_fini(&list); + } +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Constructors and destructors. */ + ATF_TP_ADD_TC(tp, list_init); + + /* Getters. */ + ATF_TP_ADD_TC(tp, list_index); + ATF_TP_ADD_TC(tp, list_index_c); + ATF_TP_ADD_TC(tp, list_to_charpp_empty); + ATF_TP_ADD_TC(tp, list_to_charpp_some); + + /* Modifiers. */ + ATF_TP_ADD_TC(tp, list_append); + ATF_TP_ADD_TC(tp, list_append_list); + + /* Macros. */ + ATF_TP_ADD_TC(tp, list_for_each); + ATF_TP_ADD_TC(tp, list_for_each_c); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/map.c b/unit/atf-src/atf-c/detail/map.c new file mode 100644 index 0000000..45de47f --- /dev/null +++ b/unit/atf-src/atf-c/detail/map.c @@ -0,0 +1,380 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/map.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" +#include "atf-c/utils.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +struct map_entry { + char *m_key; + void *m_value; + bool m_managed; +}; + +static +struct map_entry * +new_entry(const char *key, void *value, bool managed) +{ + struct map_entry *me; + + me = (struct map_entry *)malloc(sizeof(*me)); + if (me != NULL) { + me->m_key = strdup(key); + if (me->m_key == NULL) { + free(me); + me = NULL; + } else { + me->m_value = value; + me->m_managed = managed; + } + } + + return me; +} + +/* --------------------------------------------------------------------- + * The "atf_map_citer" type. + * --------------------------------------------------------------------- */ + +/* + * Getters. + */ + +const char * +atf_map_citer_key(const atf_map_citer_t citer) +{ + const struct map_entry *me = citer.m_entry; + PRE(me != NULL); + return me->m_key; +} + +const void * +atf_map_citer_data(const atf_map_citer_t citer) +{ + const struct map_entry *me = citer.m_entry; + PRE(me != NULL); + return me->m_value; +} + +atf_map_citer_t +atf_map_citer_next(const atf_map_citer_t citer) +{ + atf_map_citer_t newciter; + + newciter = citer; + newciter.m_listiter = atf_list_citer_next(citer.m_listiter); + newciter.m_entry = ((const struct map_entry *) + atf_list_citer_data(newciter.m_listiter)); + + return newciter; +} + +bool +atf_equal_map_citer_map_citer(const atf_map_citer_t i1, + const atf_map_citer_t i2) +{ + return i1.m_map == i2.m_map && i1.m_entry == i2.m_entry; +} + +/* --------------------------------------------------------------------- + * The "atf_map_iter" type. + * --------------------------------------------------------------------- */ + +/* + * Getters. + */ + +const char * +atf_map_iter_key(const atf_map_iter_t iter) +{ + const struct map_entry *me = iter.m_entry; + PRE(me != NULL); + return me->m_key; +} + +void * +atf_map_iter_data(const atf_map_iter_t iter) +{ + const struct map_entry *me = iter.m_entry; + PRE(me != NULL); + return me->m_value; +} + +atf_map_iter_t +atf_map_iter_next(const atf_map_iter_t iter) +{ + atf_map_iter_t newiter; + + newiter = iter; + newiter.m_listiter = atf_list_iter_next(iter.m_listiter); + newiter.m_entry = ((struct map_entry *) + atf_list_iter_data(newiter.m_listiter)); + + return newiter; +} + +bool +atf_equal_map_iter_map_iter(const atf_map_iter_t i1, + const atf_map_iter_t i2) +{ + return i1.m_map == i2.m_map && i1.m_entry == i2.m_entry; +} + +/* --------------------------------------------------------------------- + * The "atf_map" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors and destructors. + */ + +atf_error_t +atf_map_init(atf_map_t *m) +{ + return atf_list_init(&m->m_list); +} + +atf_error_t +atf_map_init_charpp(atf_map_t *m, const char *const *array) +{ + atf_error_t err; + const char *const *ptr = array; + + err = atf_map_init(m); + if (array != NULL) { + while (!atf_is_error(err) && *ptr != NULL) { + const char *key, *value; + + key = *ptr; + INV(key != NULL); + ptr++; + + if ((value = *ptr) == NULL) { + err = atf_libc_error(EINVAL, "List too short; no value for " + "key '%s' provided", key); /* XXX: Not really libc_error */ + break; + } + ptr++; + + err = atf_map_insert(m, key, strdup(value), true); + } + } + + if (atf_is_error(err)) + atf_map_fini(m); + + return err; +} + +void +atf_map_fini(atf_map_t *m) +{ + atf_list_iter_t iter; + + atf_list_for_each(iter, &m->m_list) { + struct map_entry *me = atf_list_iter_data(iter); + + if (me->m_managed) + free(me->m_value); + free(me->m_key); + free(me); + } + atf_list_fini(&m->m_list); +} + +/* + * Getters. + */ + +atf_map_iter_t +atf_map_begin(atf_map_t *m) +{ + atf_map_iter_t iter; + iter.m_map = m; + iter.m_listiter = atf_list_begin(&m->m_list); + iter.m_entry = atf_list_iter_data(iter.m_listiter); + return iter; +} + +atf_map_citer_t +atf_map_begin_c(const atf_map_t *m) +{ + atf_map_citer_t citer; + citer.m_map = m; + citer.m_listiter = atf_list_begin_c(&m->m_list); + citer.m_entry = atf_list_citer_data(citer.m_listiter); + return citer; +} + +atf_map_iter_t +atf_map_end(atf_map_t *m) +{ + atf_map_iter_t iter; + iter.m_map = m; + iter.m_entry = NULL; + iter.m_listiter = atf_list_end(&m->m_list); + return iter; +} + +atf_map_citer_t +atf_map_end_c(const atf_map_t *m) +{ + atf_map_citer_t iter; + iter.m_map = m; + iter.m_entry = NULL; + iter.m_listiter = atf_list_end_c(&m->m_list); + return iter; +} + +atf_map_iter_t +atf_map_find(atf_map_t *m, const char *key) +{ + atf_list_iter_t iter; + + atf_list_for_each(iter, &m->m_list) { + struct map_entry *me = atf_list_iter_data(iter); + + if (strcmp(me->m_key, key) == 0) { + atf_map_iter_t i; + i.m_map = m; + i.m_entry = me; + i.m_listiter = iter; + return i; + } + } + + return atf_map_end(m); +} + +atf_map_citer_t +atf_map_find_c(const atf_map_t *m, const char *key) +{ + atf_list_citer_t iter; + + atf_list_for_each_c(iter, &m->m_list) { + const struct map_entry *me = atf_list_citer_data(iter); + + if (strcmp(me->m_key, key) == 0) { + atf_map_citer_t i; + i.m_map = m; + i.m_entry = me; + i.m_listiter = iter; + return i; + } + } + + return atf_map_end_c(m); +} + +size_t +atf_map_size(const atf_map_t *m) +{ + return atf_list_size(&m->m_list); +} + +char ** +atf_map_to_charpp(const atf_map_t *l) +{ + char **array; + atf_map_citer_t iter; + size_t i; + + array = malloc(sizeof(char *) * (atf_map_size(l) * 2 + 1)); + if (array == NULL) + goto out; + + i = 0; + atf_map_for_each_c(iter, l) { + array[i] = strdup(atf_map_citer_key(iter)); + if (array[i] == NULL) { + atf_utils_free_charpp(array); + array = NULL; + goto out; + } + + array[i + 1] = strdup((const char *)atf_map_citer_data(iter)); + if (array[i + 1] == NULL) { + atf_utils_free_charpp(array); + array = NULL; + goto out; + } + + i += 2; + } + array[i] = NULL; + +out: + return array; +} + +/* + * Modifiers. + */ + +atf_error_t +atf_map_insert(atf_map_t *m, const char *key, void *value, bool managed) +{ + struct map_entry *me; + atf_error_t err; + atf_map_iter_t iter; + + iter = atf_map_find(m, key); + if (atf_equal_map_iter_map_iter(iter, atf_map_end(m))) { + me = new_entry(key, value, managed); + if (me == NULL) + err = atf_no_memory_error(); + else { + err = atf_list_append(&m->m_list, me, false); + if (atf_is_error(err)) { + if (managed) + free(value); + free(me->m_key); + free(me); + } + } + } else { + me = iter.m_entry; + if (me->m_managed) + free(me->m_value); + + INV(strcmp(me->m_key, key) == 0); + me->m_value = value; + me->m_managed = managed; + + err = atf_no_error(); + } + + return err; +} diff --git a/unit/atf-src/atf-c/detail/map.h b/unit/atf-src/atf-c/detail/map.h new file mode 100644 index 0000000..7ca8e8a --- /dev/null +++ b/unit/atf-src/atf-c/detail/map.h @@ -0,0 +1,114 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_MAP_H) +#define ATF_C_DETAIL_MAP_H + +#include <stdarg.h> +#include <stdbool.h> + +#include <atf-c/detail/list.h> +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_map_citer" type. + * --------------------------------------------------------------------- */ + +struct atf_map_citer { + const struct atf_map *m_map; + const void *m_entry; + atf_list_citer_t m_listiter; +}; +typedef struct atf_map_citer atf_map_citer_t; + +/* Getters. */ +const char *atf_map_citer_key(const atf_map_citer_t); +const void *atf_map_citer_data(const atf_map_citer_t); +atf_map_citer_t atf_map_citer_next(const atf_map_citer_t); + +/* Operators. */ +bool atf_equal_map_citer_map_citer(const atf_map_citer_t, + const atf_map_citer_t); + +/* --------------------------------------------------------------------- + * The "atf_map_iter" type. + * --------------------------------------------------------------------- */ + +struct atf_map_iter { + struct atf_map *m_map; + void *m_entry; + atf_list_iter_t m_listiter; +}; +typedef struct atf_map_iter atf_map_iter_t; + +/* Getters. */ +const char *atf_map_iter_key(const atf_map_iter_t); +void *atf_map_iter_data(const atf_map_iter_t); +atf_map_iter_t atf_map_iter_next(const atf_map_iter_t); + +/* Operators. */ +bool atf_equal_map_iter_map_iter(const atf_map_iter_t, + const atf_map_iter_t); + +/* --------------------------------------------------------------------- + * The "atf_map" type. + * --------------------------------------------------------------------- */ + +/* A list-based map. Typically very inefficient, but our maps are small + * enough. */ +struct atf_map { + atf_list_t m_list; +}; +typedef struct atf_map atf_map_t; + +/* Constructors and destructors */ +atf_error_t atf_map_init(atf_map_t *); +atf_error_t atf_map_init_charpp(atf_map_t *, const char *const *); +void atf_map_fini(atf_map_t *); + +/* Getters. */ +atf_map_iter_t atf_map_begin(atf_map_t *); +atf_map_citer_t atf_map_begin_c(const atf_map_t *); +atf_map_iter_t atf_map_end(atf_map_t *); +atf_map_citer_t atf_map_end_c(const atf_map_t *); +atf_map_iter_t atf_map_find(atf_map_t *, const char *); +atf_map_citer_t atf_map_find_c(const atf_map_t *, const char *); +size_t atf_map_size(const atf_map_t *); +char **atf_map_to_charpp(const atf_map_t *); + +/* Modifiers. */ +atf_error_t atf_map_insert(atf_map_t *, const char *, void *, bool); + +/* Macros. */ +#define atf_map_for_each(iter, map) \ + for (iter = atf_map_begin(map); \ + !atf_equal_map_iter_map_iter((iter), atf_map_end(map)); \ + iter = atf_map_iter_next(iter)) +#define atf_map_for_each_c(iter, map) \ + for (iter = atf_map_begin_c(map); \ + !atf_equal_map_citer_map_citer((iter), atf_map_end_c(map)); \ + iter = atf_map_citer_next(iter)) + +#endif /* !defined(ATF_C_DETAIL_MAP_H) */ diff --git a/unit/atf-src/atf-c/detail/map_test.c b/unit/atf-src/atf-c/detail/map_test.c new file mode 100644 index 0000000..0982e20 --- /dev/null +++ b/unit/atf-src/atf-c/detail/map_test.c @@ -0,0 +1,421 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/map.h" + +#include <stdio.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" +#include "atf-c/utils.h" + +/* --------------------------------------------------------------------- + * Tests for the "atf_map" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors and destructors. + */ + +ATF_TC(map_init); +ATF_TC_HEAD(map_init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_init function"); +} +ATF_TC_BODY(map_init, tc) +{ + atf_map_t map; + + RE(atf_map_init(&map)); + ATF_REQUIRE_EQ(atf_map_size(&map), 0); + atf_map_fini(&map); +} + +ATF_TC_WITHOUT_HEAD(map_init_charpp_null); +ATF_TC_BODY(map_init_charpp_null, tc) +{ + atf_map_t map; + + RE(atf_map_init_charpp(&map, NULL)); + ATF_REQUIRE_EQ(atf_map_size(&map), 0); + atf_map_fini(&map); +} + +ATF_TC_WITHOUT_HEAD(map_init_charpp_empty); +ATF_TC_BODY(map_init_charpp_empty, tc) +{ + const char *const array[] = { NULL }; + atf_map_t map; + + RE(atf_map_init_charpp(&map, array)); + ATF_REQUIRE_EQ(atf_map_size(&map), 0); + atf_map_fini(&map); +} + +ATF_TC_WITHOUT_HEAD(map_init_charpp_some); +ATF_TC_BODY(map_init_charpp_some, tc) +{ + const char *const array[] = { "K1", "V1", "K2", "V2", NULL }; + atf_map_t map; + atf_map_citer_t iter; + + RE(atf_map_init_charpp(&map, array)); + ATF_REQUIRE_EQ(atf_map_size(&map), 2); + + iter = atf_map_find_c(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K1") == 0); + ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V1") == 0); + + iter = atf_map_find_c(&map, "K2"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K2") == 0); + ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V2") == 0); + + atf_map_fini(&map); +} + +ATF_TC_WITHOUT_HEAD(map_init_charpp_short); +ATF_TC_BODY(map_init_charpp_short, tc) +{ + const char *const array[] = { "K1", "V1", "K2", NULL }; + atf_map_t map; + + atf_error_t err = atf_map_init_charpp(&map, array); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); +} + +/* + * Getters. + */ + +ATF_TC(find); +ATF_TC_HEAD(find, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_find function"); +} +ATF_TC_BODY(find, tc) +{ + atf_map_t map; + char val1[] = "V1"; + char val2[] = "V2"; + atf_map_iter_t iter; + + RE(atf_map_init(&map)); + RE(atf_map_insert(&map, "K1", val1, false)); + RE(atf_map_insert(&map, "K2", val2, false)); + + iter = atf_map_find(&map, "K0"); + ATF_REQUIRE(atf_equal_map_iter_map_iter(iter, atf_map_end(&map))); + + iter = atf_map_find(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map))); + ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K1") == 0); + ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "V1") == 0); + strcpy(atf_map_iter_data(iter), "Z1"); + + iter = atf_map_find(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map))); + ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K1") == 0); + ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "Z1") == 0); + + iter = atf_map_find(&map, "K2"); + ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map))); + ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K2") == 0); + ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "V2") == 0); + + atf_map_fini(&map); +} + +ATF_TC(find_c); +ATF_TC_HEAD(find_c, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_find_c function"); +} +ATF_TC_BODY(find_c, tc) +{ + atf_map_t map; + char val1[] = "V1"; + char val2[] = "V2"; + atf_map_citer_t iter; + + RE(atf_map_init(&map)); + RE(atf_map_insert(&map, "K1", val1, false)); + RE(atf_map_insert(&map, "K2", val2, false)); + + iter = atf_map_find_c(&map, "K0"); + ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + + iter = atf_map_find_c(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K1") == 0); + ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V1") == 0); + + iter = atf_map_find_c(&map, "K2"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K2") == 0); + ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V2") == 0); + + atf_map_fini(&map); +} + +ATF_TC_WITHOUT_HEAD(to_charpp_empty); +ATF_TC_BODY(to_charpp_empty, tc) +{ + atf_map_t map; + char **array; + + RE(atf_map_init(&map)); + ATF_REQUIRE((array = atf_map_to_charpp(&map)) != NULL); + atf_map_fini(&map); + + ATF_CHECK_EQ(NULL, array[0]); + atf_utils_free_charpp(array); +} + +ATF_TC_WITHOUT_HEAD(to_charpp_some); +ATF_TC_BODY(to_charpp_some, tc) +{ + atf_map_t map; + char **array; + + char s1[] = "one"; + char s2[] = "two"; + char s3[] = "three"; + + RE(atf_map_init(&map)); + RE(atf_map_insert(&map, "K1", s1, false)); + RE(atf_map_insert(&map, "K2", s2, false)); + RE(atf_map_insert(&map, "K3", s3, false)); + ATF_REQUIRE((array = atf_map_to_charpp(&map)) != NULL); + atf_map_fini(&map); + + ATF_CHECK_STREQ("K1", array[0]); + ATF_CHECK_STREQ("one", array[1]); + ATF_CHECK_STREQ("K2", array[2]); + ATF_CHECK_STREQ("two", array[3]); + ATF_CHECK_STREQ("K3", array[4]); + ATF_CHECK_STREQ("three", array[5]); + ATF_CHECK_EQ(NULL, array[6]); + atf_utils_free_charpp(array); +} + +/* + * Modifiers. + */ + +ATF_TC(map_insert); +ATF_TC_HEAD(map_insert, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_insert function"); +} +ATF_TC_BODY(map_insert, tc) +{ + atf_map_t map; + char buf[] = "1st test string"; + char buf2[] = "2nd test string"; + const char *ptr; + atf_map_citer_t iter; + + RE(atf_map_init(&map)); + + printf("Inserting some values\n"); + ATF_REQUIRE_EQ(atf_map_size(&map), 0); + RE(atf_map_insert(&map, "K1", buf, false)); + ATF_REQUIRE_EQ(atf_map_size(&map), 1); + RE(atf_map_insert(&map, "K2", buf, false)); + ATF_REQUIRE_EQ(atf_map_size(&map), 2); + RE(atf_map_insert(&map, "K3", buf, false)); + ATF_REQUIRE_EQ(atf_map_size(&map), 3); + + printf("Replacing a value\n"); + iter = atf_map_find_c(&map, "K3"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ptr = atf_map_citer_data(iter); + ATF_REQUIRE_EQ(ptr, buf); + RE(atf_map_insert(&map, "K3", buf2, false)); + ATF_REQUIRE_EQ(atf_map_size(&map), 3); + iter = atf_map_find_c(&map, "K3"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ptr = atf_map_citer_data(iter); + ATF_REQUIRE_EQ(ptr, buf2); + + atf_map_fini(&map); +} + +/* + * Macros. + */ + +ATF_TC(map_for_each); +ATF_TC_HEAD(map_for_each, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_for_each macro"); +} +ATF_TC_BODY(map_for_each, tc) +{ + atf_map_t map; + atf_map_iter_t iter; + size_t count, i, size; + char keys[10][5]; + int nums[10]; + + printf("Iterating over empty map\n"); + RE(atf_map_init(&map)); + count = 0; + atf_map_for_each(iter, &map) { + count++; + printf("Item count is now %zd\n", count); + } + ATF_REQUIRE_EQ(count, 0); + atf_map_fini(&map); + + for (size = 0; size <= 10; size++) { + printf("Iterating over map of %zd elements\n", size); + RE(atf_map_init(&map)); + for (i = 0; i < size; i++) { + nums[i] = i + 1; + snprintf(keys[i], sizeof(keys[i]), "%d", nums[i]); + RE(atf_map_insert(&map, keys[i], &nums[i], false)); + } + count = 0; + atf_map_for_each(iter, &map) { + printf("Retrieved item: %d\n", *(int *)atf_map_iter_data(iter)); + count++; + } + ATF_REQUIRE_EQ(count, size); + atf_map_fini(&map); + } +} + +ATF_TC(map_for_each_c); +ATF_TC_HEAD(map_for_each_c, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_for_each_c macro"); +} +ATF_TC_BODY(map_for_each_c, tc) +{ + atf_map_t map; + atf_map_citer_t iter; + size_t count, i, size; + char keys[10][5]; + int nums[10]; + + printf("Iterating over empty map\n"); + RE(atf_map_init(&map)); + count = 0; + atf_map_for_each_c(iter, &map) { + count++; + printf("Item count is now %zd\n", count); + } + ATF_REQUIRE_EQ(count, 0); + atf_map_fini(&map); + + for (size = 0; size <= 10; size++) { + printf("Iterating over map of %zd elements\n", size); + RE(atf_map_init(&map)); + for (i = 0; i < size; i++) { + nums[i] = i + 1; + snprintf(keys[i], sizeof(keys[i]), "%d", nums[i]); + RE(atf_map_insert(&map, keys[i], &nums[i], false)); + } + count = 0; + atf_map_for_each_c(iter, &map) { + printf("Retrieved item: %d\n", + *(const int *)atf_map_citer_data(iter)); + count++; + } + ATF_REQUIRE_EQ(count, size); + atf_map_fini(&map); + } +} + +/* + * Other. + */ + +ATF_TC(stable_keys); +ATF_TC_HEAD(stable_keys, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that the keys do not change " + "even if their original values do"); +} +ATF_TC_BODY(stable_keys, tc) +{ + atf_map_t map; + atf_map_citer_t iter; + char key[] = "K1"; + + RE(atf_map_init(&map)); + + RE(atf_map_insert(&map, key, strdup("test-value"), true)); + iter = atf_map_find_c(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + iter = atf_map_find_c(&map, "K2"); + ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + + strcpy(key, "K2"); + iter = atf_map_find_c(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + iter = atf_map_find_c(&map, "K2"); + ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + + atf_map_fini(&map); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Constructors and destructors. */ + ATF_TP_ADD_TC(tp, map_init); + ATF_TP_ADD_TC(tp, map_init_charpp_null); + ATF_TP_ADD_TC(tp, map_init_charpp_empty); + ATF_TP_ADD_TC(tp, map_init_charpp_some); + ATF_TP_ADD_TC(tp, map_init_charpp_short); + + /* Getters. */ + ATF_TP_ADD_TC(tp, find); + ATF_TP_ADD_TC(tp, find_c); + ATF_TP_ADD_TC(tp, to_charpp_empty); + ATF_TP_ADD_TC(tp, to_charpp_some); + + /* Modifiers. */ + ATF_TP_ADD_TC(tp, map_insert); + + /* Macros. */ + ATF_TP_ADD_TC(tp, map_for_each); + ATF_TP_ADD_TC(tp, map_for_each_c); + + /* Other. */ + ATF_TP_ADD_TC(tp, stable_keys); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/process.c b/unit/atf-src/atf-c/detail/process.c new file mode 100644 index 0000000..8e08b6c --- /dev/null +++ b/unit/atf-src/atf-c/detail/process.c @@ -0,0 +1,670 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/process.h" + +#include <sys/types.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "atf-c/defs.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" + +/* This prototype is not in the header file because this is a private + * function; however, we need to access it during testing. */ +atf_error_t atf_process_status_init(atf_process_status_t *, int); + +/* --------------------------------------------------------------------- + * The "stream_prepare" auxiliary type. + * --------------------------------------------------------------------- */ + +struct stream_prepare { + const atf_process_stream_t *m_sb; + + bool m_pipefds_ok; + int m_pipefds[2]; +}; +typedef struct stream_prepare stream_prepare_t; + +static +atf_error_t +stream_prepare_init(stream_prepare_t *sp, const atf_process_stream_t *sb) +{ + atf_error_t err; + + const int type = atf_process_stream_type(sb); + + sp->m_sb = sb; + sp->m_pipefds_ok = false; + + if (type == atf_process_stream_type_capture) { + if (pipe(sp->m_pipefds) == -1) + err = atf_libc_error(errno, "Failed to create pipe"); + else { + err = atf_no_error(); + sp->m_pipefds_ok = true; + } + } else + err = atf_no_error(); + + return err; +} + +static +void +stream_prepare_fini(stream_prepare_t *sp) +{ + if (sp->m_pipefds_ok) { + close(sp->m_pipefds[0]); + close(sp->m_pipefds[1]); + } +} + +/* --------------------------------------------------------------------- + * The "atf_process_stream" type. + * --------------------------------------------------------------------- */ + +const int atf_process_stream_type_capture = 1; +const int atf_process_stream_type_connect = 2; +const int atf_process_stream_type_inherit = 3; +const int atf_process_stream_type_redirect_fd = 4; +const int atf_process_stream_type_redirect_path = 5; + +static +bool +stream_is_valid(const atf_process_stream_t *sb) +{ + return (sb->m_type == atf_process_stream_type_capture) || + (sb->m_type == atf_process_stream_type_connect) || + (sb->m_type == atf_process_stream_type_inherit) || + (sb->m_type == atf_process_stream_type_redirect_fd) || + (sb->m_type == atf_process_stream_type_redirect_path); +} + +atf_error_t +atf_process_stream_init_capture(atf_process_stream_t *sb) +{ + sb->m_type = atf_process_stream_type_capture; + + POST(stream_is_valid(sb)); + return atf_no_error(); +} + +atf_error_t +atf_process_stream_init_connect(atf_process_stream_t *sb, + const int src_fd, const int tgt_fd) +{ + PRE(src_fd >= 0); + PRE(tgt_fd >= 0); + PRE(src_fd != tgt_fd); + + sb->m_type = atf_process_stream_type_connect; + sb->m_src_fd = src_fd; + sb->m_tgt_fd = tgt_fd; + + POST(stream_is_valid(sb)); + return atf_no_error(); +} + +atf_error_t +atf_process_stream_init_inherit(atf_process_stream_t *sb) +{ + sb->m_type = atf_process_stream_type_inherit; + + POST(stream_is_valid(sb)); + return atf_no_error(); +} + +atf_error_t +atf_process_stream_init_redirect_fd(atf_process_stream_t *sb, + const int fd) +{ + sb->m_type = atf_process_stream_type_redirect_fd; + sb->m_fd = fd; + + POST(stream_is_valid(sb)); + return atf_no_error(); +} + +atf_error_t +atf_process_stream_init_redirect_path(atf_process_stream_t *sb, + const atf_fs_path_t *path) +{ + sb->m_type = atf_process_stream_type_redirect_path; + sb->m_path = path; + + POST(stream_is_valid(sb)); + return atf_no_error(); +} + +void +atf_process_stream_fini(atf_process_stream_t *sb) +{ + PRE(stream_is_valid(sb)); +} + +int +atf_process_stream_type(const atf_process_stream_t *sb) +{ + PRE(stream_is_valid(sb)); + + return sb->m_type; +} + +/* --------------------------------------------------------------------- + * The "atf_process_status" type. + * --------------------------------------------------------------------- */ + +atf_error_t +atf_process_status_init(atf_process_status_t *s, int status) +{ + s->m_status = status; + + return atf_no_error(); +} + +void +atf_process_status_fini(atf_process_status_t *s ATF_DEFS_ATTRIBUTE_UNUSED) +{ +} + +bool +atf_process_status_exited(const atf_process_status_t *s) +{ + int mutable_status = s->m_status; + return WIFEXITED(mutable_status); +} + +int +atf_process_status_exitstatus(const atf_process_status_t *s) +{ + PRE(atf_process_status_exited(s)); + int mutable_status = s->m_status; + return WEXITSTATUS(mutable_status); +} + +bool +atf_process_status_signaled(const atf_process_status_t *s) +{ + int mutable_status = s->m_status; + return WIFSIGNALED(mutable_status); +} + +int +atf_process_status_termsig(const atf_process_status_t *s) +{ + PRE(atf_process_status_signaled(s)); + int mutable_status = s->m_status; + return WTERMSIG(mutable_status); +} + +bool +atf_process_status_coredump(const atf_process_status_t *s) +{ + PRE(atf_process_status_signaled(s)); +#if defined(WCOREDUMP) + int mutable_status = s->m_status; + return WCOREDUMP(mutable_status); +#else + return false; +#endif +} + +/* --------------------------------------------------------------------- + * The "atf_process_child" type. + * --------------------------------------------------------------------- */ + +static +atf_error_t +atf_process_child_init(atf_process_child_t *c) +{ + c->m_pid = 0; + c->m_stdout = -1; + c->m_stderr = -1; + + return atf_no_error(); +} + +static +void +atf_process_child_fini(atf_process_child_t *c) +{ + if (c->m_stdout != -1) + close(c->m_stdout); + if (c->m_stderr != -1) + close(c->m_stderr); +} + +atf_error_t +atf_process_child_wait(atf_process_child_t *c, atf_process_status_t *s) +{ + atf_error_t err; + int status; + + if (waitpid(c->m_pid, &status, 0) == -1) + err = atf_libc_error(errno, "Failed waiting for process %d", + c->m_pid); + else { + atf_process_child_fini(c); + err = atf_process_status_init(s, status); + } + + return err; +} + +pid_t +atf_process_child_pid(const atf_process_child_t *c) +{ + return c->m_pid; +} + +int +atf_process_child_stdout(atf_process_child_t *c) +{ + PRE(c->m_stdout != -1); + return c->m_stdout; +} + +int +atf_process_child_stderr(atf_process_child_t *c) +{ + PRE(c->m_stderr != -1); + return c->m_stderr; +} + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +static +atf_error_t +safe_dup(const int oldfd, const int newfd) +{ + atf_error_t err; + + if (oldfd != newfd) { + if (dup2(oldfd, newfd) == -1) { + err = atf_libc_error(errno, "Could not allocate file descriptor"); + } else { + close(oldfd); + err = atf_no_error(); + } + } else + err = atf_no_error(); + + return err; +} + +static +atf_error_t +child_connect(const stream_prepare_t *sp, int procfd) +{ + atf_error_t err; + const int type = atf_process_stream_type(sp->m_sb); + + if (type == atf_process_stream_type_capture) { + close(sp->m_pipefds[0]); + err = safe_dup(sp->m_pipefds[1], procfd); + } else if (type == atf_process_stream_type_connect) { + if (dup2(sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd) == -1) + err = atf_libc_error(errno, "Cannot connect descriptor %d to %d", + sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd); + else + err = atf_no_error(); + } else if (type == atf_process_stream_type_inherit) { + err = atf_no_error(); + } else if (type == atf_process_stream_type_redirect_fd) { + err = safe_dup(sp->m_sb->m_fd, procfd); + } else if (type == atf_process_stream_type_redirect_path) { + int aux = open(atf_fs_path_cstring(sp->m_sb->m_path), + O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (aux == -1) + err = atf_libc_error(errno, "Could not create %s", + atf_fs_path_cstring(sp->m_sb->m_path)); + else { + err = safe_dup(aux, procfd); + if (atf_is_error(err)) + close(aux); + } + } else { + UNREACHABLE; + err = atf_no_error(); + } + + return err; +} + +static +void +parent_connect(const stream_prepare_t *sp, int *fd) +{ + const int type = atf_process_stream_type(sp->m_sb); + + if (type == atf_process_stream_type_capture) { + close(sp->m_pipefds[1]); + *fd = sp->m_pipefds[0]; + } else if (type == atf_process_stream_type_connect) { + /* Do nothing. */ + } else if (type == atf_process_stream_type_inherit) { + /* Do nothing. */ + } else if (type == atf_process_stream_type_redirect_fd) { + /* Do nothing. */ + } else if (type == atf_process_stream_type_redirect_path) { + /* Do nothing. */ + } else { + UNREACHABLE; + } +} + +static +atf_error_t +do_parent(atf_process_child_t *c, + const pid_t pid, + const stream_prepare_t *outsp, + const stream_prepare_t *errsp) +{ + atf_error_t err; + + err = atf_process_child_init(c); + if (atf_is_error(err)) + goto out; + + c->m_pid = pid; + + parent_connect(outsp, &c->m_stdout); + parent_connect(errsp, &c->m_stderr); + +out: + return err; +} + +static +void +do_child(void (*)(void *), + void *, + const stream_prepare_t *, + const stream_prepare_t *) ATF_DEFS_ATTRIBUTE_NORETURN; + +static +void +do_child(void (*start)(void *), + void *v, + const stream_prepare_t *outsp, + const stream_prepare_t *errsp) +{ + atf_error_t err; + + err = child_connect(outsp, STDOUT_FILENO); + if (atf_is_error(err)) + goto out; + + err = child_connect(errsp, STDERR_FILENO); + if (atf_is_error(err)) + goto out; + + start(v); + UNREACHABLE; + +out: + if (atf_is_error(err)) { + char buf[1024]; + + atf_error_format(err, buf, sizeof(buf)); + fprintf(stderr, "Unhandled error: %s\n", buf); + atf_error_free(err); + + exit(EXIT_FAILURE); + } else + exit(EXIT_SUCCESS); +} + +static +atf_error_t +fork_with_streams(atf_process_child_t *c, + void (*start)(void *), + const atf_process_stream_t *outsb, + const atf_process_stream_t *errsb, + void *v) +{ + atf_error_t err; + stream_prepare_t outsp; + stream_prepare_t errsp; + pid_t pid; + + err = stream_prepare_init(&outsp, outsb); + if (atf_is_error(err)) + goto out; + + err = stream_prepare_init(&errsp, errsb); + if (atf_is_error(err)) + goto err_outpipe; + + pid = fork(); + if (pid == -1) { + err = atf_libc_error(errno, "Failed to fork"); + goto err_errpipe; + } + + if (pid == 0) { + do_child(start, v, &outsp, &errsp); + UNREACHABLE; + abort(); + err = atf_no_error(); + } else { + err = do_parent(c, pid, &outsp, &errsp); + if (atf_is_error(err)) + goto err_errpipe; + } + + goto out; + +err_errpipe: + stream_prepare_fini(&errsp); +err_outpipe: + stream_prepare_fini(&outsp); + +out: + return err; +} + +static +atf_error_t +init_stream_w_default(const atf_process_stream_t *usersb, + atf_process_stream_t *inheritsb, + const atf_process_stream_t **realsb) +{ + atf_error_t err; + + if (usersb == NULL) { + err = atf_process_stream_init_inherit(inheritsb); + if (!atf_is_error(err)) + *realsb = inheritsb; + } else { + err = atf_no_error(); + *realsb = usersb; + } + + return err; +} + +atf_error_t +atf_process_fork(atf_process_child_t *c, + void (*start)(void *), + const atf_process_stream_t *outsb, + const atf_process_stream_t *errsb, + void *v) +{ + atf_error_t err; + atf_process_stream_t inherit_outsb, inherit_errsb; + const atf_process_stream_t *real_outsb, *real_errsb; + + real_outsb = NULL; /* Shut up GCC warning. */ + err = init_stream_w_default(outsb, &inherit_outsb, &real_outsb); + if (atf_is_error(err)) + goto out; + + real_errsb = NULL; /* Shut up GCC warning. */ + err = init_stream_w_default(errsb, &inherit_errsb, &real_errsb); + if (atf_is_error(err)) + goto out_out; + + err = fork_with_streams(c, start, real_outsb, real_errsb, v); + + if (errsb == NULL) + atf_process_stream_fini(&inherit_errsb); +out_out: + if (outsb == NULL) + atf_process_stream_fini(&inherit_outsb); +out: + return err; +} + +static +int +const_execvp(const char *file, const char *const *argv) +{ +#define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) + return execvp(file, UNCONST(argv)); +#undef UNCONST +} + +static +atf_error_t +list_to_array(const atf_list_t *l, const char ***ap) +{ + atf_error_t err; + const char **a; + + a = (const char **)malloc((atf_list_size(l) + 1) * sizeof(const char *)); + if (a == NULL) + err = atf_no_memory_error(); + else { + const char **aiter; + atf_list_citer_t liter; + + aiter = a; + atf_list_for_each_c(liter, l) { + *aiter = (const char *)atf_list_citer_data(liter); + aiter++; + } + *aiter = NULL; + + err = atf_no_error(); + *ap = a; + } + + return err; +} + +struct exec_args { + const atf_fs_path_t *m_prog; + const char *const *m_argv; + void (*m_prehook)(void); +}; + +static +void +do_exec(void *v) +{ + struct exec_args *ea = v; + + if (ea->m_prehook != NULL) + ea->m_prehook(); + + const int ret = const_execvp(atf_fs_path_cstring(ea->m_prog), ea->m_argv); + const int errnocopy = errno; + INV(ret == -1); + fprintf(stderr, "exec(%s) failed: %s\n", + atf_fs_path_cstring(ea->m_prog), strerror(errnocopy)); + exit(EXIT_FAILURE); +} + +atf_error_t +atf_process_exec_array(atf_process_status_t *s, + const atf_fs_path_t *prog, + const char *const *argv, + const atf_process_stream_t *outsb, + const atf_process_stream_t *errsb, + void (*prehook)(void)) +{ + atf_error_t err; + atf_process_child_t c; + struct exec_args ea = { prog, argv, prehook }; + + PRE(outsb == NULL || + atf_process_stream_type(outsb) != atf_process_stream_type_capture); + PRE(errsb == NULL || + atf_process_stream_type(errsb) != atf_process_stream_type_capture); + + err = atf_process_fork(&c, do_exec, outsb, errsb, &ea); + if (atf_is_error(err)) + goto out; + +again: + err = atf_process_child_wait(&c, s); + if (atf_is_error(err)) { + INV(atf_error_is(err, "libc") && atf_libc_error_code(err) == EINTR); + atf_error_free(err); + goto again; + } + +out: + return err; +} + +atf_error_t +atf_process_exec_list(atf_process_status_t *s, + const atf_fs_path_t *prog, + const atf_list_t *argv, + const atf_process_stream_t *outsb, + const atf_process_stream_t *errsb, + void (*prehook)(void)) +{ + atf_error_t err; + const char **argv2; + + PRE(outsb == NULL || + atf_process_stream_type(outsb) != atf_process_stream_type_capture); + PRE(errsb == NULL || + atf_process_stream_type(errsb) != atf_process_stream_type_capture); + + argv2 = NULL; /* Silence GCC warning. */ + err = list_to_array(argv, &argv2); + if (atf_is_error(err)) + goto out; + + err = atf_process_exec_array(s, prog, argv2, outsb, errsb, prehook); + + free(argv2); +out: + return err; +} diff --git a/unit/atf-src/atf-c/detail/process.h b/unit/atf-src/atf-c/detail/process.h new file mode 100644 index 0000000..81158d0 --- /dev/null +++ b/unit/atf-src/atf-c/detail/process.h @@ -0,0 +1,131 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_PROCESS_H) +#define ATF_C_DETAIL_PROCESS_H + +#include <sys/types.h> + +#include <stdbool.h> + +#include <atf-c/detail/fs.h> +#include <atf-c/detail/list.h> +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_process_stream" type. + * --------------------------------------------------------------------- */ + +struct atf_process_stream { + int m_type; + + /* Valid if m_type == connect. */ + int m_src_fd; + int m_tgt_fd; + + /* Valid if m_type == redirect_fd. */ + int m_fd; + + /* Valid if m_type == redirect_path. */ + const atf_fs_path_t *m_path; +}; +typedef struct atf_process_stream atf_process_stream_t; + +extern const int atf_process_stream_type_capture; +extern const int atf_process_stream_type_connect; +extern const int atf_process_stream_type_inherit; +extern const int atf_process_stream_type_redirect_fd; +extern const int atf_process_stream_type_redirect_path; + +atf_error_t atf_process_stream_init_capture(atf_process_stream_t *); +atf_error_t atf_process_stream_init_connect(atf_process_stream_t *, + const int, const int); +atf_error_t atf_process_stream_init_inherit(atf_process_stream_t *); +atf_error_t atf_process_stream_init_redirect_fd(atf_process_stream_t *, + const int fd); +atf_error_t atf_process_stream_init_redirect_path(atf_process_stream_t *, + const atf_fs_path_t *); +void atf_process_stream_fini(atf_process_stream_t *); + +int atf_process_stream_type(const atf_process_stream_t *); + +/* --------------------------------------------------------------------- + * The "atf_process_status" type. + * --------------------------------------------------------------------- */ + +struct atf_process_status { + int m_status; +}; +typedef struct atf_process_status atf_process_status_t; + +void atf_process_status_fini(atf_process_status_t *); + +bool atf_process_status_exited(const atf_process_status_t *); +int atf_process_status_exitstatus(const atf_process_status_t *); +bool atf_process_status_signaled(const atf_process_status_t *); +int atf_process_status_termsig(const atf_process_status_t *); +bool atf_process_status_coredump(const atf_process_status_t *); + +/* --------------------------------------------------------------------- + * The "atf_process_child" type. + * --------------------------------------------------------------------- */ + +struct atf_process_child { + pid_t m_pid; + + int m_stdout; + int m_stderr; +}; +typedef struct atf_process_child atf_process_child_t; + +atf_error_t atf_process_child_wait(atf_process_child_t *, + atf_process_status_t *); +pid_t atf_process_child_pid(const atf_process_child_t *); +int atf_process_child_stdout(atf_process_child_t *); +int atf_process_child_stderr(atf_process_child_t *); + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +atf_error_t atf_process_fork(atf_process_child_t *, + void (*)(void *), + const atf_process_stream_t *, + const atf_process_stream_t *, + void *); +atf_error_t atf_process_exec_array(atf_process_status_t *, + const atf_fs_path_t *, + const char *const *, + const atf_process_stream_t *, + const atf_process_stream_t *, + void (*)(void)); +atf_error_t atf_process_exec_list(atf_process_status_t *, + const atf_fs_path_t *, + const atf_list_t *, + const atf_process_stream_t *, + const atf_process_stream_t *, + void (*)(void)); + +#endif /* !defined(ATF_C_DETAIL_PROCESS_H) */ diff --git a/unit/atf-src/atf-c/detail/process_helpers.c b/unit/atf-src/atf-c/detail/process_helpers.c new file mode 100644 index 0000000..47b90bc --- /dev/null +++ b/unit/atf-src/atf-c/detail/process_helpers.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include <sys/types.h> + +#include <assert.h> /* NO_CHECK_STYLE */ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static +int +h_echo(const char *msg) +{ + printf("%s\n", msg); + return EXIT_SUCCESS; +} + +static +int +h_exit_failure(void) +{ + return EXIT_FAILURE; +} + +static +int +h_exit_signal(void) +{ + kill(getpid(), SIGKILL); + assert(0); /* NO_CHECK_STYLE */ + return EXIT_FAILURE; +} + +static +int +h_exit_success(void) +{ + return EXIT_SUCCESS; +} + +static +int +h_stdout_stderr(const char *id) +{ + fprintf(stdout, "Line 1 to stdout for %s\n", id); + fprintf(stdout, "Line 2 to stdout for %s\n", id); + fprintf(stderr, "Line 1 to stderr for %s\n", id); + fprintf(stderr, "Line 2 to stderr for %s\n", id); + + return EXIT_SUCCESS; +} + +static +void +check_args(const int argc, const char *const argv[], const int required) +{ + if (argc < required) { + fprintf(stderr, "Usage: %s helper-name [args]\n", argv[0]); + exit(EXIT_FAILURE); + } +} + +int +main(int argc, const char *const argv[]) +{ + int exitcode; + + check_args(argc, argv, 2); + + if (strcmp(argv[1], "echo") == 0) { + check_args(argc, argv, 3); + exitcode = h_echo(argv[2]); + } else if (strcmp(argv[1], "exit-failure") == 0) + exitcode = h_exit_failure(); + else if (strcmp(argv[1], "exit-signal") == 0) + exitcode = h_exit_signal(); + else if (strcmp(argv[1], "exit-success") == 0) + exitcode = h_exit_success(); + else if (strcmp(argv[1], "stdout-stderr") == 0) { + check_args(argc, argv, 3); + exitcode = h_stdout_stderr(argv[2]); + } else { + fprintf(stderr, "%s: Unknown helper %s\n", argv[0], argv[1]); + exitcode = EXIT_FAILURE; + } + + return exitcode; +} diff --git a/unit/atf-src/atf-c/detail/process_test.c b/unit/atf-src/atf-c/detail/process_test.c new file mode 100644 index 0000000..5ae5565 --- /dev/null +++ b/unit/atf-src/atf-c/detail/process_test.c @@ -0,0 +1,1159 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/process.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/defs.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/test_helpers.h" + +atf_error_t atf_process_status_init(atf_process_status_t *, int); + +/* --------------------------------------------------------------------- + * Auxiliary functions for testing of 'atf_process_fork'. + * --------------------------------------------------------------------- */ + +/* + * Testing of atf_process_fork is quite messy. We want to be able to test + * all the possible combinations of stdout and stderr behavior to ensure + * that the streams are manipulated correctly. + * + * To do this, the do_fork function is a wrapper for atf_process_fork that + * issues stream-specific hooks before fork, while the child is running and + * after the child terminates. We then provide test cases that just call + * do_fork with different hooks. + * + * The hooks are described by base_stream, and we then have one *_stream + * type for ever possible stream behavior. + */ + +enum out_type { stdout_type, stderr_type }; + +struct base_stream { + void (*init)(void *); + void (*process)(void *, atf_process_child_t *); + void (*fini)(void *); + + /* m_sb is initialized by subclasses that need it, but all consumers + * must use m_sb_ptr, which may or may not point to m_sb. This allows + * us to test the interface with a NULL value, which triggers a + * default behavior. */ + atf_process_stream_t m_sb; + atf_process_stream_t *m_sb_ptr; + enum out_type m_type; +}; +#define BASE_STREAM(ihook, phook, fhook, type) \ + { .init = ihook, \ + .process = phook, \ + .fini = fhook, \ + .m_type = type } + +static +void +check_file(const enum out_type type) +{ + switch (type) { + case stdout_type: + ATF_CHECK(atf_utils_grep_file("stdout: msg", "stdout")); + ATF_CHECK(!atf_utils_grep_file("stderr: msg", "stdout")); + break; + case stderr_type: + ATF_CHECK(atf_utils_grep_file("stderr: msg", "stderr")); + ATF_CHECK(!atf_utils_grep_file("stdout: msg", "stderr")); + break; + default: + UNREACHABLE; + } +} + +struct capture_stream { + struct base_stream m_base; + + char *m_msg; +}; +#define CAPTURE_STREAM(type) \ + { .m_base = BASE_STREAM(capture_stream_init, \ + capture_stream_process, \ + capture_stream_fini, \ + type) } + +static +void +capture_stream_init(void *v) +{ + struct capture_stream *s = v; + + s->m_base.m_sb_ptr = &s->m_base.m_sb; + RE(atf_process_stream_init_capture(&s->m_base.m_sb)); + s->m_msg = NULL; +} + +static +void +capture_stream_process(void *v, atf_process_child_t *c) +{ + struct capture_stream *s = v; + + switch (s->m_base.m_type) { + case stdout_type: + s->m_msg = atf_utils_readline(atf_process_child_stdout(c)); + break; + case stderr_type: + s->m_msg = atf_utils_readline(atf_process_child_stderr(c)); + break; + default: + UNREACHABLE; + } +} + +static +void +capture_stream_fini(void *v) +{ + struct capture_stream *s = v; + + switch (s->m_base.m_type) { + case stdout_type: + ATF_CHECK(atf_utils_grep_string("stdout: msg", s->m_msg)); + ATF_CHECK(!atf_utils_grep_string("stderr: msg", s->m_msg)); + break; + case stderr_type: + ATF_CHECK(!atf_utils_grep_string("stdout: msg", s->m_msg)); + ATF_CHECK(atf_utils_grep_string("stderr: msg", s->m_msg)); + break; + default: + UNREACHABLE; + } + + free(s->m_msg); + atf_process_stream_fini(&s->m_base.m_sb); +} + +struct connect_stream { + struct base_stream m_base; + + int m_fd; +}; +#define CONNECT_STREAM(type) \ + { .m_base = BASE_STREAM(connect_stream_init, \ + NULL, \ + connect_stream_fini, \ + type) } + +static +void +connect_stream_init(void *v) +{ + struct connect_stream *s = v; + int src_fd; + + switch (s->m_base.m_type) { + case stdout_type: + src_fd = STDOUT_FILENO; + s->m_fd = open("stdout", O_WRONLY | O_CREAT | O_TRUNC, 0644); + break; + case stderr_type: + src_fd = STDERR_FILENO; + s->m_fd = open("stderr", O_WRONLY | O_CREAT | O_TRUNC, 0644); + break; + default: + UNREACHABLE; + src_fd = -1; + } + ATF_REQUIRE(s->m_fd != -1); + + s->m_base.m_sb_ptr = &s->m_base.m_sb; + RE(atf_process_stream_init_connect(&s->m_base.m_sb, src_fd, s->m_fd)); +} + +static +void +connect_stream_fini(void *v) +{ + struct connect_stream *s = v; + + ATF_REQUIRE(close(s->m_fd) != -1); + + atf_process_stream_fini(&s->m_base.m_sb); + + check_file(s->m_base.m_type); +} + +struct inherit_stream { + struct base_stream m_base; + int m_fd; + + int m_old_fd; +}; +#define INHERIT_STREAM(type) \ + { .m_base = BASE_STREAM(inherit_stream_init, \ + NULL, \ + inherit_stream_fini, \ + type) } + +static +void +inherit_stream_init(void *v) +{ + struct inherit_stream *s = v; + const char *name; + + s->m_base.m_sb_ptr = &s->m_base.m_sb; + RE(atf_process_stream_init_inherit(&s->m_base.m_sb)); + + switch (s->m_base.m_type) { + case stdout_type: + s->m_fd = STDOUT_FILENO; + name = "stdout"; + break; + case stderr_type: + s->m_fd = STDERR_FILENO; + name = "stderr"; + break; + default: + UNREACHABLE; + name = NULL; + } + + s->m_old_fd = dup(s->m_fd); + ATF_REQUIRE(s->m_old_fd != -1); + ATF_REQUIRE(close(s->m_fd) != -1); + ATF_REQUIRE_EQ(open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644), + s->m_fd); +} + +static +void +inherit_stream_fini(void *v) +{ + struct inherit_stream *s = v; + + ATF_REQUIRE(dup2(s->m_old_fd, s->m_fd) != -1); + ATF_REQUIRE(close(s->m_old_fd) != -1); + + atf_process_stream_fini(&s->m_base.m_sb); + + check_file(s->m_base.m_type); +} + +#define default_stream inherit_stream +#define DEFAULT_STREAM(type) \ + { .m_base = BASE_STREAM(default_stream_init, \ + NULL, \ + default_stream_fini, \ + type) } + +static +void +default_stream_init(void *v) +{ + struct inherit_stream *s = v; + + inherit_stream_init(v); + s->m_base.m_sb_ptr = NULL; +} + +static +void +default_stream_fini(void *v) +{ + inherit_stream_fini(v); +} + +struct redirect_fd_stream { + struct base_stream m_base; + + int m_fd; +}; +#define REDIRECT_FD_STREAM(type) \ + { .m_base = BASE_STREAM(redirect_fd_stream_init, \ + NULL, \ + redirect_fd_stream_fini, \ + type) } + +static +void +redirect_fd_stream_init(void *v) +{ + struct redirect_fd_stream *s = v; + + switch (s->m_base.m_type) { + case stdout_type: + s->m_fd = open("stdout", O_WRONLY | O_CREAT | O_TRUNC, 0644); + break; + case stderr_type: + s->m_fd = open("stderr", O_WRONLY | O_CREAT | O_TRUNC, 0644); + break; + default: + UNREACHABLE; + } + ATF_REQUIRE(s->m_fd != -1); + + s->m_base.m_sb_ptr = &s->m_base.m_sb; + RE(atf_process_stream_init_redirect_fd(&s->m_base.m_sb, s->m_fd)); +} + +static +void +redirect_fd_stream_fini(void *v) +{ + struct redirect_fd_stream *s = v; + + ATF_REQUIRE(close(s->m_fd) != -1); + + atf_process_stream_fini(&s->m_base.m_sb); + + check_file(s->m_base.m_type); +} + +struct redirect_path_stream { + struct base_stream m_base; + + atf_fs_path_t m_path; +}; +#define REDIRECT_PATH_STREAM(type) \ + { .m_base = BASE_STREAM(redirect_path_stream_init, \ + NULL, \ + redirect_path_stream_fini, \ + type) } + +static +void +redirect_path_stream_init(void *v) +{ + struct redirect_path_stream *s = v; + + switch (s->m_base.m_type) { + case stdout_type: + RE(atf_fs_path_init_fmt(&s->m_path, "stdout")); + break; + case stderr_type: + RE(atf_fs_path_init_fmt(&s->m_path, "stderr")); + break; + default: + UNREACHABLE; + } + + s->m_base.m_sb_ptr = &s->m_base.m_sb; + RE(atf_process_stream_init_redirect_path(&s->m_base.m_sb, &s->m_path)); +} + +static +void +redirect_path_stream_fini(void *v) +{ + struct redirect_path_stream *s = v; + + atf_process_stream_fini(&s->m_base.m_sb); + + atf_fs_path_fini(&s->m_path); + + check_file(s->m_base.m_type); +} + +static void child_print(void *) ATF_DEFS_ATTRIBUTE_NORETURN; + +struct child_print_data { + const char *m_msg; +}; + +static +void +child_print(void *v) +{ + struct child_print_data *cpd = v; + + fprintf(stdout, "stdout: %s\n", cpd->m_msg); + fprintf(stderr, "stderr: %s\n", cpd->m_msg); + + exit(EXIT_SUCCESS); +} + +static +void +do_fork(const struct base_stream *outfs, void *out, + const struct base_stream *errfs, void *err) +{ + atf_process_child_t child; + atf_process_status_t status; + struct child_print_data cpd = { "msg" }; + + outfs->init(out); + errfs->init(err); + + RE(atf_process_fork(&child, child_print, outfs->m_sb_ptr, + errfs->m_sb_ptr, &cpd)); + if (outfs->process != NULL) + outfs->process(out, &child); + if (errfs->process != NULL) + errfs->process(err, &child); + RE(atf_process_child_wait(&child, &status)); + + outfs->fini(out); + errfs->fini(err); + + atf_process_status_fini(&status); +} + +/* --------------------------------------------------------------------- + * Test cases for the "stream" type. + * --------------------------------------------------------------------- */ + +ATF_TC(stream_init_capture); +ATF_TC_HEAD(stream_init_capture, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the " + "atf_process_stream_init_capture function"); +} +ATF_TC_BODY(stream_init_capture, tc) +{ + atf_process_stream_t sb; + + RE(atf_process_stream_init_capture(&sb)); + + ATF_CHECK_EQ(atf_process_stream_type(&sb), + atf_process_stream_type_capture); + + atf_process_stream_fini(&sb); +} + +ATF_TC(stream_init_connect); +ATF_TC_HEAD(stream_init_connect, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the " + "atf_process_stream_init_connect function"); +} +ATF_TC_BODY(stream_init_connect, tc) +{ + atf_process_stream_t sb; + + RE(atf_process_stream_init_connect(&sb, 1, 2)); + + ATF_CHECK_EQ(atf_process_stream_type(&sb), + atf_process_stream_type_connect); + + atf_process_stream_fini(&sb); +} + +ATF_TC(stream_init_inherit); +ATF_TC_HEAD(stream_init_inherit, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the " + "atf_process_stream_init_inherit function"); +} +ATF_TC_BODY(stream_init_inherit, tc) +{ + atf_process_stream_t sb; + + RE(atf_process_stream_init_inherit(&sb)); + + ATF_CHECK_EQ(atf_process_stream_type(&sb), + atf_process_stream_type_inherit); + + atf_process_stream_fini(&sb); +} + +ATF_TC(stream_init_redirect_fd); +ATF_TC_HEAD(stream_init_redirect_fd, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the " + "atf_process_stream_init_redirect_fd function"); +} +ATF_TC_BODY(stream_init_redirect_fd, tc) +{ + atf_process_stream_t sb; + + RE(atf_process_stream_init_redirect_fd(&sb, 1)); + + ATF_CHECK_EQ(atf_process_stream_type(&sb), + atf_process_stream_type_redirect_fd); + + atf_process_stream_fini(&sb); +} + +ATF_TC(stream_init_redirect_path); +ATF_TC_HEAD(stream_init_redirect_path, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the " + "atf_process_stream_init_redirect_path function"); +} +ATF_TC_BODY(stream_init_redirect_path, tc) +{ + atf_process_stream_t sb; + atf_fs_path_t path; + + RE(atf_fs_path_init_fmt(&path, "foo")); + RE(atf_process_stream_init_redirect_path(&sb, &path)); + + ATF_CHECK_EQ(atf_process_stream_type(&sb), + atf_process_stream_type_redirect_path); + + atf_process_stream_fini(&sb); + atf_fs_path_fini(&path); +} + +/* --------------------------------------------------------------------- + * Test cases for the "status" type. + * --------------------------------------------------------------------- */ + +static void child_exit_success(void) ATF_DEFS_ATTRIBUTE_NORETURN; +static void child_exit_failure(void) ATF_DEFS_ATTRIBUTE_NORETURN; +static void child_sigkill(void) ATF_DEFS_ATTRIBUTE_NORETURN; +static void child_sigquit(void) ATF_DEFS_ATTRIBUTE_NORETURN; +static void child_sigterm(void) ATF_DEFS_ATTRIBUTE_NORETURN; + +void +child_exit_success(void) +{ + exit(EXIT_SUCCESS); +} + +void +child_exit_failure(void) +{ + exit(EXIT_FAILURE); +} + +void +child_sigkill(void) +{ + kill(getpid(), SIGKILL); + abort(); +} + +void +child_sigquit(void) +{ + kill(getpid(), SIGQUIT); + abort(); +} + +void +child_sigterm(void) +{ + kill(getpid(), SIGTERM); + abort(); +} + +static +int +fork_and_wait_child(void (*child_func)(void)) +{ + pid_t pid; + int status; + + pid = fork(); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + status = 0; /* Silence compiler warnings */ + child_func(); + UNREACHABLE; + } else { + ATF_REQUIRE(waitpid(pid, &status, 0) != 0); + } + + return status; +} + +ATF_TC(status_exited); +ATF_TC_HEAD(status_exited, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the status type for processes " + "that exit cleanly"); +} +ATF_TC_BODY(status_exited, tc) +{ + { + const int rawstatus = fork_and_wait_child(child_exit_success); + atf_process_status_t s; + RE(atf_process_status_init(&s, rawstatus)); + ATF_CHECK(atf_process_status_exited(&s)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&s), EXIT_SUCCESS); + ATF_CHECK(!atf_process_status_signaled(&s)); + atf_process_status_fini(&s); + } + + { + const int rawstatus = fork_and_wait_child(child_exit_failure); + atf_process_status_t s; + RE(atf_process_status_init(&s, rawstatus)); + ATF_CHECK(atf_process_status_exited(&s)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&s), EXIT_FAILURE); + ATF_CHECK(!atf_process_status_signaled(&s)); + atf_process_status_fini(&s); + } +} + +ATF_TC(status_signaled); +ATF_TC_HEAD(status_signaled, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the status type for processes " + "that end due to a signal"); +} +ATF_TC_BODY(status_signaled, tc) +{ + { + const int rawstatus = fork_and_wait_child(child_sigkill); + atf_process_status_t s; + RE(atf_process_status_init(&s, rawstatus)); + ATF_CHECK(!atf_process_status_exited(&s)); + ATF_CHECK(atf_process_status_signaled(&s)); + ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGKILL); + ATF_CHECK(!atf_process_status_coredump(&s)); + atf_process_status_fini(&s); + } + + { + const int rawstatus = fork_and_wait_child(child_sigterm); + atf_process_status_t s; + RE(atf_process_status_init(&s, rawstatus)); + ATF_CHECK(!atf_process_status_exited(&s)); + ATF_CHECK(atf_process_status_signaled(&s)); + ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGTERM); + ATF_CHECK(!atf_process_status_coredump(&s)); + atf_process_status_fini(&s); + } +} + +ATF_TC(status_coredump); +ATF_TC_HEAD(status_coredump, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the status type for processes " + "that crash"); +} +ATF_TC_BODY(status_coredump, tc) +{ + struct rlimit rl; + rl.rlim_cur = RLIM_INFINITY; + rl.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &rl) == -1) + atf_tc_skip("Cannot unlimit the core file size; check limits " + "manually"); + + const int rawstatus = fork_and_wait_child(child_sigquit); + atf_process_status_t s; + RE(atf_process_status_init(&s, rawstatus)); + ATF_CHECK(!atf_process_status_exited(&s)); + ATF_CHECK(atf_process_status_signaled(&s)); + ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGQUIT); + ATF_CHECK(atf_process_status_coredump(&s)); + atf_process_status_fini(&s); +} + +/* --------------------------------------------------------------------- + * Test cases for the "child" type. + * --------------------------------------------------------------------- */ + +static void child_report_pid(void *) ATF_DEFS_ATTRIBUTE_NORETURN; + +static +void +child_report_pid(void *v ATF_DEFS_ATTRIBUTE_UNUSED) +{ + const pid_t pid = getpid(); + if (write(STDOUT_FILENO, &pid, sizeof(pid)) != sizeof(pid)) + abort(); + fprintf(stderr, "Reporting %d to parent\n", (int)getpid()); + exit(EXIT_SUCCESS); +} + +ATF_TC(child_pid); +ATF_TC_HEAD(child_pid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the correctness of the pid " + "stored in the child type"); +} +ATF_TC_BODY(child_pid, tc) +{ + atf_process_stream_t outsb, errsb; + atf_process_child_t child; + atf_process_status_t status; + pid_t pid; + + RE(atf_process_stream_init_capture(&outsb)); + RE(atf_process_stream_init_inherit(&errsb)); + + RE(atf_process_fork(&child, child_report_pid, &outsb, &errsb, NULL)); + ATF_CHECK_EQ(read(atf_process_child_stdout(&child), &pid, sizeof(pid)), + sizeof(pid)); + printf("Expected PID: %d\n", (int)atf_process_child_pid(&child)); + printf("Actual PID: %d\n", (int)pid); + ATF_CHECK_EQ(atf_process_child_pid(&child), pid); + + RE(atf_process_child_wait(&child, &status)); + atf_process_status_fini(&status); + + atf_process_stream_fini(&outsb); + atf_process_stream_fini(&errsb); +} + +static +void +child_loop(void *v ATF_DEFS_ATTRIBUTE_UNUSED) +{ + for (;;) + sleep(1); +} + +static +void +nop_signal(int sig ATF_DEFS_ATTRIBUTE_UNUSED) +{ +} + +static +void +child_spawn_loop_and_wait_eintr(void *v ATF_DEFS_ATTRIBUTE_UNUSED) +{ + atf_process_child_t child; + atf_process_status_t status; + struct sigaction sighup, old_sighup; + +#define RE_ABORT(expr) \ + do { \ + atf_error_t _aux_err = expr; \ + if (atf_is_error(_aux_err)) { \ + atf_error_free(_aux_err); \ + abort(); \ + } \ + } while (0) + + { + atf_process_stream_t outsb, errsb; + + RE_ABORT(atf_process_stream_init_capture(&outsb)); + RE_ABORT(atf_process_stream_init_inherit(&errsb)); + RE_ABORT(atf_process_fork(&child, child_loop, &outsb, &errsb, NULL)); + atf_process_stream_fini(&outsb); + atf_process_stream_fini(&errsb); + } + + sighup.sa_handler = nop_signal; + sigemptyset(&sighup.sa_mask); + sighup.sa_flags = 0; + if (sigaction(SIGHUP, &sighup, &old_sighup) == -1) + abort(); + + printf("waiting\n"); + fflush(stdout); + + fprintf(stderr, "Child entering wait(2)\n"); + atf_error_t err = atf_process_child_wait(&child, &status); + fprintf(stderr, "Child's wait(2) terminated\n"); + if (!atf_is_error(err)) { + fprintf(stderr, "wait completed successfully (not interrupted)\n"); + abort(); + } + if (!atf_error_is(err, "libc")) { + fprintf(stderr, "wait did not raise libc_error\n"); + abort(); + } + if (atf_libc_error_code(err) != EINTR) { + fprintf(stderr, "libc_error is not EINTR\n"); + abort(); + } + atf_error_free(err); + + sigaction(SIGHUP, &old_sighup, NULL); + + fprintf(stderr, "Child is killing subchild\n"); + kill(atf_process_child_pid(&child), SIGTERM); + + RE_ABORT(atf_process_child_wait(&child, &status)); + atf_process_status_fini(&status); + +#undef RE_ABORT + + exit(EXIT_SUCCESS); +} + +ATF_TC(child_wait_eintr); +ATF_TC_HEAD(child_wait_eintr, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the interruption of the wait " + "method by an external signal, and the return of " + "an EINTR error"); + atf_tc_set_md_var(tc, "timeout", "30"); +} +ATF_TC_BODY(child_wait_eintr, tc) +{ + atf_process_child_t child; + atf_process_status_t status; + + { + atf_process_stream_t outsb, errsb; + + RE(atf_process_stream_init_capture(&outsb)); + RE(atf_process_stream_init_inherit(&errsb)); + RE(atf_process_fork(&child, child_spawn_loop_and_wait_eintr, + &outsb, &errsb, NULL)); + atf_process_stream_fini(&outsb); + atf_process_stream_fini(&errsb); + } + + { + /* Wait until the child process performs the wait call. This is + * racy, because the message we get from it is sent *before* + * doing the real system call... but I can't figure any other way + * to do this. */ + char buf[16]; + printf("Waiting for child to issue wait(2)\n"); + ATF_REQUIRE(read(atf_process_child_stdout(&child), buf, + sizeof(buf)) > 0); + sleep(1); + } + + printf("Interrupting child's wait(2) call\n"); + kill(atf_process_child_pid(&child), SIGHUP); + + printf("Waiting for child's completion\n"); + RE(atf_process_child_wait(&child, &status)); + ATF_REQUIRE(atf_process_status_exited(&status)); + ATF_REQUIRE_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS); + atf_process_status_fini(&status); +} + +/* --------------------------------------------------------------------- + * Tests cases for the free functions. + * --------------------------------------------------------------------- */ + +static +void +do_exec(const atf_tc_t *tc, const char *helper_name, atf_process_status_t *s, + void (*prehook)(void)) +{ + atf_fs_path_t process_helpers; + const char *argv[3]; + + get_process_helpers_path(tc, true, &process_helpers); + + argv[0] = atf_fs_path_cstring(&process_helpers); + argv[1] = helper_name; + argv[2] = NULL; + printf("Executing %s %s\n", argv[0], argv[1]); + + RE(atf_process_exec_array(s, &process_helpers, argv, NULL, NULL, prehook)); + atf_fs_path_fini(&process_helpers); +} + +static +void +check_line(int fd, const char *exp) +{ + char *line = atf_utils_readline(fd); + ATF_CHECK(line != NULL); + ATF_CHECK_STREQ_MSG(exp, line, "read: '%s', expected: '%s'", line, exp); + free(line); +} + +ATF_TC(exec_failure); +ATF_TC_HEAD(exec_failure, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests execing a command"); +} +ATF_TC_BODY(exec_failure, tc) +{ + atf_process_status_t status; + + do_exec(tc, "exit-failure", &status, NULL); + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_FAILURE); + atf_process_status_fini(&status); +} + +ATF_TC(exec_list); +ATF_TC_HEAD(exec_list, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests execing a command"); +} +ATF_TC_BODY(exec_list, tc) +{ + atf_fs_path_t process_helpers; + atf_list_t argv; + atf_process_status_t status; + + RE(atf_list_init(&argv)); + + get_process_helpers_path(tc, true, &process_helpers); + atf_list_append(&argv, strdup(atf_fs_path_cstring(&process_helpers)), true); + atf_list_append(&argv, strdup("echo"), true); + atf_list_append(&argv, strdup("test-message"), true); + { + atf_fs_path_t outpath; + atf_process_stream_t outsb; + + RE(atf_fs_path_init_fmt(&outpath, "stdout")); + RE(atf_process_stream_init_redirect_path(&outsb, &outpath)); + RE(atf_process_exec_list(&status, &process_helpers, &argv, &outsb, + NULL, NULL)); + atf_process_stream_fini(&outsb); + atf_fs_path_fini(&outpath); + } + atf_list_fini(&argv); + + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS); + + { + int fd = open("stdout", O_RDONLY); + ATF_CHECK(fd != -1); + check_line(fd, "test-message"); + close(fd); + } + + atf_process_status_fini(&status); + atf_fs_path_fini(&process_helpers); +} + +static void +exit_early(void) +{ + exit(80); +} + +ATF_TC(exec_prehook); +ATF_TC_HEAD(exec_prehook, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests execing a command with a prehook"); +} +ATF_TC_BODY(exec_prehook, tc) +{ + atf_process_status_t status; + + do_exec(tc, "exit-success", &status, exit_early); + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), 80); + atf_process_status_fini(&status); +} + +ATF_TC(exec_success); +ATF_TC_HEAD(exec_success, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests execing a command"); +} +ATF_TC_BODY(exec_success, tc) +{ + atf_process_status_t status; + + do_exec(tc, "exit-success", &status, NULL); + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS); + atf_process_status_fini(&status); +} + +static const int exit_v_null = 1; +static const int exit_v_notnull = 2; + +static +void +child_cookie(void *v) +{ + if (v == NULL) + exit(exit_v_null); + else + exit(exit_v_notnull); + + UNREACHABLE; +} + +ATF_TC(fork_cookie); +ATF_TC_HEAD(fork_cookie, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests forking a child, with " + "a null and non-null data cookie"); +} +ATF_TC_BODY(fork_cookie, tc) +{ + atf_process_stream_t outsb, errsb; + + RE(atf_process_stream_init_inherit(&outsb)); + RE(atf_process_stream_init_inherit(&errsb)); + + { + atf_process_child_t child; + atf_process_status_t status; + + RE(atf_process_fork(&child, child_cookie, &outsb, &errsb, NULL)); + RE(atf_process_child_wait(&child, &status)); + + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), exit_v_null); + + atf_process_status_fini(&status); + } + + { + atf_process_child_t child; + atf_process_status_t status; + int dummy_int; + + RE(atf_process_fork(&child, child_cookie, &outsb, &errsb, &dummy_int)); + RE(atf_process_child_wait(&child, &status)); + + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), exit_v_notnull); + + atf_process_status_fini(&status); + } + + atf_process_stream_fini(&errsb); + atf_process_stream_fini(&outsb); +} + +#define TC_FORK_STREAMS(outlc, outuc, errlc, erruc) \ + ATF_TC(fork_out_ ## outlc ## _err_ ## errlc); \ + ATF_TC_HEAD(fork_out_ ## outlc ## _err_ ## errlc, tc) \ + { \ + atf_tc_set_md_var(tc, "descr", "Tests forking a child, with " \ + "stdout " #outlc " and stderr " #errlc); \ + } \ + ATF_TC_BODY(fork_out_ ## outlc ## _err_ ## errlc, tc) \ + { \ + struct outlc ## _stream out = outuc ## _STREAM(stdout_type); \ + struct errlc ## _stream err = erruc ## _STREAM(stderr_type); \ + do_fork(&out.m_base, &out, &err.m_base, &err); \ + } + +TC_FORK_STREAMS(capture, CAPTURE, capture, CAPTURE); +TC_FORK_STREAMS(capture, CAPTURE, connect, CONNECT); +TC_FORK_STREAMS(capture, CAPTURE, default, DEFAULT); +TC_FORK_STREAMS(capture, CAPTURE, inherit, INHERIT); +TC_FORK_STREAMS(capture, CAPTURE, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(capture, CAPTURE, redirect_path, REDIRECT_PATH); +TC_FORK_STREAMS(connect, CONNECT, capture, CAPTURE); +TC_FORK_STREAMS(connect, CONNECT, connect, CONNECT); +TC_FORK_STREAMS(connect, CONNECT, default, DEFAULT); +TC_FORK_STREAMS(connect, CONNECT, inherit, INHERIT); +TC_FORK_STREAMS(connect, CONNECT, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(connect, CONNECT, redirect_path, REDIRECT_PATH); +TC_FORK_STREAMS(default, DEFAULT, capture, CAPTURE); +TC_FORK_STREAMS(default, DEFAULT, connect, CONNECT); +TC_FORK_STREAMS(default, DEFAULT, default, DEFAULT); +TC_FORK_STREAMS(default, DEFAULT, inherit, INHERIT); +TC_FORK_STREAMS(default, DEFAULT, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(default, DEFAULT, redirect_path, REDIRECT_PATH); +TC_FORK_STREAMS(inherit, INHERIT, capture, CAPTURE); +TC_FORK_STREAMS(inherit, INHERIT, connect, CONNECT); +TC_FORK_STREAMS(inherit, INHERIT, default, DEFAULT); +TC_FORK_STREAMS(inherit, INHERIT, inherit, INHERIT); +TC_FORK_STREAMS(inherit, INHERIT, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(inherit, INHERIT, redirect_path, REDIRECT_PATH); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, capture, CAPTURE); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, connect, CONNECT); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, default, DEFAULT); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, inherit, INHERIT); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, redirect_path, REDIRECT_PATH); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, capture, CAPTURE); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, connect, CONNECT); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, default, DEFAULT); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, inherit, INHERIT); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, redirect_path, REDIRECT_PATH); + +#undef TC_FORK_STREAMS + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Add the tests for the "stream" type. */ + ATF_TP_ADD_TC(tp, stream_init_capture); + ATF_TP_ADD_TC(tp, stream_init_connect); + ATF_TP_ADD_TC(tp, stream_init_inherit); + ATF_TP_ADD_TC(tp, stream_init_redirect_fd); + ATF_TP_ADD_TC(tp, stream_init_redirect_path); + + /* Add the tests for the "status" type. */ + ATF_TP_ADD_TC(tp, status_exited); + ATF_TP_ADD_TC(tp, status_signaled); + ATF_TP_ADD_TC(tp, status_coredump); + + /* Add the tests for the "child" type. */ + ATF_TP_ADD_TC(tp, child_pid); + ATF_TP_ADD_TC(tp, child_wait_eintr); + + /* Add the tests for the free functions. */ + ATF_TP_ADD_TC(tp, exec_failure); + ATF_TP_ADD_TC(tp, exec_list); + ATF_TP_ADD_TC(tp, exec_prehook); + ATF_TP_ADD_TC(tp, exec_success); + ATF_TP_ADD_TC(tp, fork_cookie); + ATF_TP_ADD_TC(tp, fork_out_capture_err_capture); + ATF_TP_ADD_TC(tp, fork_out_capture_err_connect); + ATF_TP_ADD_TC(tp, fork_out_capture_err_default); + ATF_TP_ADD_TC(tp, fork_out_capture_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_capture_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_capture_err_redirect_path); + ATF_TP_ADD_TC(tp, fork_out_connect_err_capture); + ATF_TP_ADD_TC(tp, fork_out_connect_err_connect); + ATF_TP_ADD_TC(tp, fork_out_connect_err_default); + ATF_TP_ADD_TC(tp, fork_out_connect_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_connect_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_connect_err_redirect_path); + ATF_TP_ADD_TC(tp, fork_out_default_err_capture); + ATF_TP_ADD_TC(tp, fork_out_default_err_connect); + ATF_TP_ADD_TC(tp, fork_out_default_err_default); + ATF_TP_ADD_TC(tp, fork_out_default_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_default_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_default_err_redirect_path); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_capture); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_connect); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_default); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_redirect_path); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_capture); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_connect); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_default); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_redirect_path); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_capture); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_connect); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_default); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_redirect_path); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/sanity.c b/unit/atf-src/atf-c/detail/sanity.c new file mode 100644 index 0000000..cfc8676 --- /dev/null +++ b/unit/atf-src/atf-c/detail/sanity.c @@ -0,0 +1,74 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/sanity.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <err.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +static +void +fail(const char *fmt, ...) +{ + va_list ap; + char buf[4096]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + warnx("%s", buf); + warnx("%s", ""); + warnx("This is probably a bug in this application or one of the " + "libraries it uses. If you believe this problem is caused " + "by, or is related to " PACKAGE_STRING ", please report it " + "to " PACKAGE_BUGREPORT " and provide as many details as " + "possible describing how you got to this condition."); + + abort(); +} + +void +atf_sanity_inv(const char *file, int line, const char *cond) +{ + fail("Invariant check failed at %s:%d: %s", file, line, cond); +} + +void +atf_sanity_pre(const char *file, int line, const char *cond) +{ + fail("Precondition check failed at %s:%d: %s", file, line, cond); +} + +void +atf_sanity_post(const char *file, int line, const char *cond) +{ + fail("Postcondition check failed at %s:%d: %s", file, line, cond); +} diff --git a/unit/atf-src/atf-c/detail/sanity.h b/unit/atf-src/atf-c/detail/sanity.h new file mode 100644 index 0000000..33b5528 --- /dev/null +++ b/unit/atf-src/atf-c/detail/sanity.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_SANITY_H) +#define ATF_C_DETAIL_SANITY_H + +void atf_sanity_inv(const char *, int, const char *); +void atf_sanity_pre(const char *, int, const char *); +void atf_sanity_post(const char *, int, const char *); + +#if !defined(NDEBUG) + +#define INV(x) \ + do { \ + if (!(x)) \ + atf_sanity_inv(__FILE__, __LINE__, #x); \ + } while (0) +#define PRE(x) \ + do { \ + if (!(x)) \ + atf_sanity_pre(__FILE__, __LINE__, #x); \ + } while (0) +#define POST(x) \ + do { \ + if (!(x)) \ + atf_sanity_post(__FILE__, __LINE__, #x); \ + } while (0) + +#else /* defined(NDEBUG) */ + +#define INV(x) \ + do { \ + (void)(x); \ + } while (0) + +#define PRE(x) \ + do { \ + (void)(x); \ + } while (0) + +#define POST(x) \ + do { \ + (void)(x); \ + } while (0) + +#endif /* !defined(NDEBUG) */ + +#define UNREACHABLE INV(0) + +#endif /* !defined(ATF_C_DETAIL_SANITY_H) */ diff --git a/unit/atf-src/atf-c/detail/sanity_test.c b/unit/atf-src/atf-c/detail/sanity_test.c new file mode 100644 index 0000000..a986c32 --- /dev/null +++ b/unit/atf-src/atf-c/detail/sanity_test.c @@ -0,0 +1,230 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/sanity.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/wait.h> + +#include <signal.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/detail/dynstr.h" +#include "atf-c/detail/process.h" +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +enum type { inv, pre, post, unreachable }; + +struct test_data { + enum type m_type; + bool m_cond; +}; + +static void do_test_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN; + +static +void +do_test_child(void *v) +{ + struct test_data *td = v; + + switch (td->m_type) { + case inv: + INV(td->m_cond); + break; + + case pre: + PRE(td->m_cond); + break; + + case post: + POST(td->m_cond); + break; + + case unreachable: + if (!td->m_cond) + UNREACHABLE; + break; + } + + exit(EXIT_SUCCESS); +} + +static +void +do_test(enum type t, bool cond) +{ + atf_process_child_t child; + atf_process_status_t status; + int nlines; + char *lines[3]; + + { + atf_process_stream_t outsb, errsb; + struct test_data td = { t, cond }; + + RE(atf_process_stream_init_inherit(&outsb)); + RE(atf_process_stream_init_capture(&errsb)); + RE(atf_process_fork(&child, do_test_child, &outsb, &errsb, &td)); + atf_process_stream_fini(&errsb); + atf_process_stream_fini(&outsb); + } + + nlines = 0; + while (nlines < 3 && (lines[nlines] = + atf_utils_readline(atf_process_child_stderr(&child))) != NULL) + nlines++; + ATF_REQUIRE(nlines == 0 || nlines == 3); + + RE(atf_process_child_wait(&child, &status)); + if (!cond) { + ATF_REQUIRE(atf_process_status_signaled(&status)); + ATF_REQUIRE(atf_process_status_termsig(&status) == SIGABRT); + } else { + ATF_REQUIRE(atf_process_status_exited(&status)); + ATF_REQUIRE(atf_process_status_exitstatus(&status) == EXIT_SUCCESS); + } + atf_process_status_fini(&status); + + if (!cond) { + switch (t) { + case inv: + ATF_REQUIRE(atf_utils_grep_string("Invariant", lines[0])); + break; + + case pre: + ATF_REQUIRE(atf_utils_grep_string("Precondition", lines[0])); + break; + + case post: + ATF_REQUIRE(atf_utils_grep_string("Postcondition", lines[0])); + break; + + case unreachable: + ATF_REQUIRE(atf_utils_grep_string("Invariant", lines[0])); + break; + } + + ATF_REQUIRE(atf_utils_grep_string(__FILE__, lines[0])); + ATF_REQUIRE(atf_utils_grep_string(PACKAGE_BUGREPORT, lines[2])); + } + + while (nlines > 0) { + nlines--; + free(lines[nlines]); + } +} + +static +void +require_ndebug(void) +{ +#if defined(NDEBUG) + atf_tc_skip("Sanity checks not available; code built with -DNDEBUG"); +#endif +} + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(inv); +ATF_TC_HEAD(inv, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the INV macro"); +} +ATF_TC_BODY(inv, tc) +{ + require_ndebug(); + + do_test(inv, false); + do_test(inv, true); +} + +ATF_TC(pre); +ATF_TC_HEAD(pre, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the PRE macro"); +} +ATF_TC_BODY(pre, tc) +{ + require_ndebug(); + + do_test(pre, false); + do_test(pre, true); +} + +ATF_TC(post); +ATF_TC_HEAD(post, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the POST macro"); +} +ATF_TC_BODY(post, tc) +{ + require_ndebug(); + + do_test(post, false); + do_test(post, true); +} + +ATF_TC(unreachable); +ATF_TC_HEAD(unreachable, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the UNREACHABLE macro"); +} +ATF_TC_BODY(unreachable, tc) +{ + require_ndebug(); + + do_test(unreachable, false); + do_test(unreachable, true); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, inv); + ATF_TP_ADD_TC(tp, pre); + ATF_TP_ADD_TC(tp, post); + ATF_TP_ADD_TC(tp, unreachable); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/test_helpers.c b/unit/atf-src/atf-c/detail/test_helpers.c new file mode 100644 index 0000000..827d46e --- /dev/null +++ b/unit/atf-src/atf-c/detail/test_helpers.c @@ -0,0 +1,147 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/test_helpers.h" + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/build.h" +#include "atf-c/check.h" +#include "atf-c/detail/dynstr.h" +#include "atf-c/detail/env.h" +#include "atf-c/detail/fs.h" +#include "atf-c/detail/process.h" +#include "atf-c/error.h" + +bool +build_check_c_o(const char *path) +{ + bool success; + atf_dynstr_t iflag; + const char *optargs[4]; + + RE(atf_dynstr_init_fmt(&iflag, "-I%s", atf_env_get_with_default( + "ATF_INCLUDEDIR", ATF_INCLUDEDIR))); + + optargs[0] = atf_dynstr_cstring(&iflag); + optargs[1] = "-Wall"; + optargs[2] = "-Werror"; + optargs[3] = NULL; + + RE(atf_check_build_c_o(path, "test.o", optargs, &success)); + + atf_dynstr_fini(&iflag); + + return success; +} + +bool +build_check_c_o_srcdir(const atf_tc_t *tc, const char *sfile) +{ + atf_fs_path_t path; + + RE(atf_fs_path_init_fmt(&path, "%s/%s", + atf_tc_get_config_var(tc, "srcdir"), sfile)); + const bool result = build_check_c_o(atf_fs_path_cstring(&path)); + atf_fs_path_fini(&path); + return result; +} + +void +header_check(const char *hdrname) +{ + FILE *srcfile; + char failmsg[128]; + + srcfile = fopen("test.c", "w"); + ATF_REQUIRE(srcfile != NULL); + fprintf(srcfile, "#include <%s>\n", hdrname); + fclose(srcfile); + + snprintf(failmsg, sizeof(failmsg), + "Header check failed; %s is not self-contained", hdrname); + + if (!build_check_c_o("test.c")) + atf_tc_fail("%s", failmsg); +} + +void +get_process_helpers_path(const atf_tc_t *tc, const bool is_detail, + atf_fs_path_t *path) +{ + RE(atf_fs_path_init_fmt(path, "%s/%sprocess_helpers", + atf_tc_get_config_var(tc, "srcdir"), + is_detail ? "" : "detail/")); +} + +struct run_h_tc_data { + atf_tc_t *m_tc; + const char *m_resname; +}; + +static +void +run_h_tc_child(void *v) +{ + struct run_h_tc_data *data = (struct run_h_tc_data *)v; + + RE(atf_tc_run(data->m_tc, data->m_resname)); +} + +/* TODO: Investigate if it's worth to add this functionality as part of + * the public API. I.e. a function to easily run a test case body in a + * subprocess. */ +void +run_h_tc(atf_tc_t *tc, const char *outname, const char *errname, + const char *resname) +{ + atf_fs_path_t outpath, errpath; + atf_process_stream_t outb, errb; + atf_process_child_t child; + atf_process_status_t status; + + RE(atf_fs_path_init_fmt(&outpath, outname)); + RE(atf_fs_path_init_fmt(&errpath, errname)); + + struct run_h_tc_data data = { tc, resname }; + + RE(atf_process_stream_init_redirect_path(&outb, &outpath)); + RE(atf_process_stream_init_redirect_path(&errb, &errpath)); + RE(atf_process_fork(&child, run_h_tc_child, &outb, &errb, &data)); + atf_process_stream_fini(&errb); + atf_process_stream_fini(&outb); + + RE(atf_process_child_wait(&child, &status)); + ATF_CHECK(atf_process_status_exited(&status)); + atf_process_status_fini(&status); + + atf_fs_path_fini(&errpath); + atf_fs_path_fini(&outpath); +} diff --git a/unit/atf-src/atf-c/detail/test_helpers.h b/unit/atf-src/atf-c/detail/test_helpers.h new file mode 100644 index 0000000..a601c29 --- /dev/null +++ b/unit/atf-src/atf-c/detail/test_helpers.h @@ -0,0 +1,75 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if defined(ATF_C_DETAIL_TEST_HELPERS_H) +# error "Cannot include test_helpers.h more than once." +#else +# define ATF_C_DETAIL_TEST_HELPERS_H +#endif + +#include <stdbool.h> + +#include <atf-c.h> + +#include <atf-c/error_fwd.h> +#include <atf-c/tc.h> + +struct atf_dynstr; +struct atf_fs_path; + +#define CE(stm) ATF_CHECK(!atf_is_error(stm)) +#define RE(stm) ATF_REQUIRE(!atf_is_error(stm)) + +#define HEADER_TC(name, hdrname) \ + ATF_TC(name); \ + ATF_TC_HEAD(name, tc) \ + { \ + atf_tc_set_md_var(tc, "descr", "Tests that the " hdrname " file can " \ + "be included on its own, without any prerequisites"); \ + } \ + ATF_TC_BODY(name, tc) \ + { \ + header_check(hdrname); \ + } + +#define BUILD_TC(name, sfile, descr, failmsg) \ + ATF_TC(name); \ + ATF_TC_HEAD(name, tc) \ + { \ + atf_tc_set_md_var(tc, "descr", descr); \ + } \ + ATF_TC_BODY(name, tc) \ + { \ + if (!build_check_c_o_srcdir(tc, sfile)) \ + atf_tc_fail("%s", failmsg); \ + } + +bool build_check_c_o(const char *); +bool build_check_c_o_srcdir(const atf_tc_t *, const char *); +void header_check(const char *); +void get_process_helpers_path(const atf_tc_t *, const bool, + struct atf_fs_path *); +bool read_line(int, struct atf_dynstr *); +void run_h_tc(atf_tc_t *, const char *, const char *, const char *); diff --git a/unit/atf-src/atf-c/detail/text.c b/unit/atf-src/atf-c/detail/text.c new file mode 100644 index 0000000..3b1d7e7 --- /dev/null +++ b/unit/atf-src/atf-c/detail/text.c @@ -0,0 +1,180 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/text.h" + +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <stdlib.h> + +#include "atf-c/detail/dynstr.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" + +atf_error_t +atf_text_for_each_word(const char *instr, const char *sep, + atf_error_t (*func)(const char *, void *), + void *data) +{ + atf_error_t err; + char *str, *str2, *last; + + str = strdup(instr); + if (str == NULL) { + err = atf_no_memory_error(); + goto out; + } + + err = atf_no_error(); + str2 = strtok_r(str, sep, &last); + while (str2 != NULL && !atf_is_error(err)) { + err = func(str2, data); + str2 = strtok_r(NULL, sep, &last); + } + + free(str); +out: + return err; +} + +atf_error_t +atf_text_format(char **dest, const char *fmt, ...) +{ + atf_error_t err; + va_list ap; + + va_start(ap, fmt); + err = atf_text_format_ap(dest, fmt, ap); + va_end(ap); + + return err; +} + +atf_error_t +atf_text_format_ap(char **dest, const char *fmt, va_list ap) +{ + atf_error_t err; + atf_dynstr_t tmp; + va_list ap2; + + va_copy(ap2, ap); + err = atf_dynstr_init_ap(&tmp, fmt, ap2); + va_end(ap2); + if (!atf_is_error(err)) + *dest = atf_dynstr_fini_disown(&tmp); + + return err; +} + +atf_error_t +atf_text_split(const char *str, const char *delim, atf_list_t *words) +{ + atf_error_t err; + const char *end; + const char *iter; + + err = atf_list_init(words); + if (atf_is_error(err)) + goto err; + + end = str + strlen(str); + INV(*end == '\0'); + iter = str; + while (iter < end) { + const char *ptr; + + INV(iter != NULL); + ptr = strstr(iter, delim); + if (ptr == NULL) + ptr = end; + + INV(ptr >= iter); + if (ptr > iter) { + atf_dynstr_t word; + + err = atf_dynstr_init_raw(&word, iter, ptr - iter); + if (atf_is_error(err)) + goto err_list; + + err = atf_list_append(words, atf_dynstr_fini_disown(&word), true); + if (atf_is_error(err)) + goto err_list; + } + + iter = ptr + strlen(delim); + } + + INV(!atf_is_error(err)); + return err; + +err_list: + atf_list_fini(words); +err: + return err; +} + +atf_error_t +atf_text_to_bool(const char *str, bool *b) +{ + atf_error_t err; + + if (strcasecmp(str, "yes") == 0 || + strcasecmp(str, "true") == 0) { + *b = true; + err = atf_no_error(); + } else if (strcasecmp(str, "no") == 0 || + strcasecmp(str, "false") == 0) { + *b = false; + err = atf_no_error(); + } else { + /* XXX Not really a libc error. */ + err = atf_libc_error(EINVAL, "Cannot convert string '%s' " + "to boolean", str); + } + + return err; +} + +atf_error_t +atf_text_to_long(const char *str, long *l) +{ + atf_error_t err; + char *endptr; + long tmp; + + errno = 0; + tmp = strtol(str, &endptr, 10); + if (str[0] == '\0' || *endptr != '\0') + err = atf_libc_error(EINVAL, "'%s' is not a number", str); + else if (errno == ERANGE || (tmp == LONG_MAX || tmp == LONG_MIN)) + err = atf_libc_error(ERANGE, "'%s' is out of range", str); + else { + *l = tmp; + err = atf_no_error(); + } + + return err; +} diff --git a/unit/atf-src/atf-c/detail/text.h b/unit/atf-src/atf-c/detail/text.h new file mode 100644 index 0000000..b2820b6 --- /dev/null +++ b/unit/atf-src/atf-c/detail/text.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_TEXT_H) +#define ATF_C_DETAIL_TEXT_H + +#include <stdarg.h> +#include <stdbool.h> + +#include <atf-c/detail/list.h> +#include <atf-c/error_fwd.h> + +atf_error_t atf_text_for_each_word(const char *, const char *, + atf_error_t (*)(const char *, void *), + void *); +atf_error_t atf_text_format(char **, const char *, ...); +atf_error_t atf_text_format_ap(char **, const char *, va_list); +atf_error_t atf_text_split(const char *, const char *, atf_list_t *); +atf_error_t atf_text_to_bool(const char *, bool *); +atf_error_t atf_text_to_long(const char *, long *); + +#endif /* !defined(ATF_C_DETAIL_TEXT_H) */ diff --git a/unit/atf-src/atf-c/detail/text_test.c b/unit/atf-src/atf-c/detail/text_test.c new file mode 100644 index 0000000..5d6c8e5 --- /dev/null +++ b/unit/atf-src/atf-c/detail/text_test.c @@ -0,0 +1,421 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/text.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +#define REQUIRE_ERROR(exp) \ + do { \ + atf_error_t err = exp; \ + ATF_REQUIRE(atf_is_error(err)); \ + atf_error_free(err); \ + } while (0) + +static +size_t +array_size(const char *words[]) +{ + size_t count; + const char **word; + + count = 0; + for (word = words; *word != NULL; word++) + count++; + + return count; +} + +static +void +check_split(const char *str, const char *delim, const char *words[]) +{ + atf_list_t list; + const char **word; + size_t i; + + printf("Splitting '%s' with delimiter '%s'\n", str, delim); + CE(atf_text_split(str, delim, &list)); + + printf("Expecting %zd words\n", array_size(words)); + ATF_CHECK_EQ(atf_list_size(&list), array_size(words)); + + for (word = words, i = 0; *word != NULL; word++, i++) { + printf("Word at position %zd should be '%s'\n", i, words[i]); + ATF_CHECK_STREQ((const char *)atf_list_index_c(&list, i), words[i]); + } + + atf_list_fini(&list); +} + +static +atf_error_t +word_acum(const char *word, void *data) +{ + char *acum = data; + + strcat(acum, word); + + return atf_no_error(); +} + +static +atf_error_t +word_count(const char *word ATF_DEFS_ATTRIBUTE_UNUSED, void *data) +{ + size_t *counter = data; + + (*counter)++; + + return atf_no_error(); +} + +struct fail_at { + int failpos; + int curpos; +}; + +static +atf_error_t +word_fail_at(const char *word ATF_DEFS_ATTRIBUTE_UNUSED, void *data) +{ + struct fail_at *fa = data; + atf_error_t err; + + if (fa->failpos == fa->curpos) + err = atf_no_memory_error(); /* Just a random error. */ + else { + fa->curpos++; + err = atf_no_error(); + } + + return err; +} + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(for_each_word); +ATF_TC_HEAD(for_each_word, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_text_for_each_word" + "function"); +} +ATF_TC_BODY(for_each_word, tc) +{ + size_t cnt; + char acum[1024]; + + cnt = 0; + strcpy(acum, ""); + RE(atf_text_for_each_word("1 2 3", " ", word_count, &cnt)); + RE(atf_text_for_each_word("1 2 3", " ", word_acum, acum)); + ATF_REQUIRE(cnt == 3); + ATF_REQUIRE(strcmp(acum, "123") == 0); + + cnt = 0; + strcpy(acum, ""); + RE(atf_text_for_each_word("1 2 3", ".", word_count, &cnt)); + RE(atf_text_for_each_word("1 2 3", ".", word_acum, acum)); + ATF_REQUIRE(cnt == 1); + ATF_REQUIRE(strcmp(acum, "1 2 3") == 0); + + cnt = 0; + strcpy(acum, ""); + RE(atf_text_for_each_word("1 2 3 4 5", " ", word_count, &cnt)); + RE(atf_text_for_each_word("1 2 3 4 5", " ", word_acum, acum)); + ATF_REQUIRE(cnt == 5); + ATF_REQUIRE(strcmp(acum, "12345") == 0); + + cnt = 0; + strcpy(acum, ""); + RE(atf_text_for_each_word("1 2.3.4 5", " .", word_count, &cnt)); + RE(atf_text_for_each_word("1 2.3.4 5", " .", word_acum, acum)); + ATF_REQUIRE(cnt == 5); + ATF_REQUIRE(strcmp(acum, "12345") == 0); + + { + struct fail_at fa; + fa.failpos = 3; + fa.curpos = 0; + atf_error_t err = atf_text_for_each_word("a b c d e", " ", + word_fail_at, &fa); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + ATF_REQUIRE(fa.curpos == 3); + atf_error_free(err); + } +} + +ATF_TC(format); +ATF_TC_HEAD(format, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of free-form " + "strings using a variable parameters list"); +} +ATF_TC_BODY(format, tc) +{ + char *str; + atf_error_t err; + + err = atf_text_format(&str, "%s %s %d", "Test", "string", 1); + ATF_REQUIRE(!atf_is_error(err)); + ATF_REQUIRE(strcmp(str, "Test string 1") == 0); + free(str); +} + +static +void +format_ap(char **dest, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_text_format_ap(dest, fmt, ap); + va_end(ap); + + ATF_REQUIRE(!atf_is_error(err)); +} + +ATF_TC(format_ap); +ATF_TC_HEAD(format_ap, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of free-form " + "strings using a va_list argument"); +} +ATF_TC_BODY(format_ap, tc) +{ + char *str; + + format_ap(&str, "%s %s %d", "Test", "string", 1); + ATF_REQUIRE(strcmp(str, "Test string 1") == 0); + free(str); +} + +ATF_TC(split); +ATF_TC_HEAD(split, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the split function"); +} +ATF_TC_BODY(split, tc) +{ + { + const char *words[] = { NULL }; + check_split("", " ", words); + } + + { + const char *words[] = { NULL }; + check_split(" ", " ", words); + } + + { + const char *words[] = { NULL }; + check_split(" ", " ", words); + } + + { + const char *words[] = { "a", "b", NULL }; + check_split("a b", " ", words); + } + + { + const char *words[] = { "a", "b", "c", "d", NULL }; + check_split("a b c d", " ", words); + } + + { + const char *words[] = { "foo", "bar", NULL }; + check_split("foo bar", " ", words); + } + + { + const char *words[] = { "foo", "bar", "baz", "foobar", NULL }; + check_split("foo bar baz foobar", " ", words); + } + + { + const char *words[] = { "foo", "bar", NULL }; + check_split(" foo bar", " ", words); + } + + { + const char *words[] = { "foo", "bar", NULL }; + check_split("foo bar", " ", words); + } + + { + const char *words[] = { "foo", "bar", NULL }; + check_split("foo bar ", " ", words); + } + + { + const char *words[] = { "foo", "bar", NULL }; + check_split(" foo bar ", " ", words); + } +} + +ATF_TC(split_delims); +ATF_TC_HEAD(split_delims, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the split function using " + "different delimiters"); +} +ATF_TC_BODY(split_delims, tc) +{ + + { + const char *words[] = { NULL }; + check_split("", "/", words); + } + + { + const char *words[] = { " ", NULL }; + check_split(" ", "/", words); + } + + { + const char *words[] = { " ", NULL }; + check_split(" ", "/", words); + } + + { + const char *words[] = { "a", "b", NULL }; + check_split("a/b", "/", words); + } + + { + const char *words[] = { "a", "bcd", "ef", NULL }; + check_split("aLONGDELIMbcdLONGDELIMef", "LONGDELIM", words); + } +} + +ATF_TC(to_bool); +ATF_TC_HEAD(to_bool, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_text_to_bool function"); +} +ATF_TC_BODY(to_bool, tc) +{ + bool b; + + RE(atf_text_to_bool("true", &b)); ATF_REQUIRE(b); + RE(atf_text_to_bool("TRUE", &b)); ATF_REQUIRE(b); + RE(atf_text_to_bool("yes", &b)); ATF_REQUIRE(b); + RE(atf_text_to_bool("YES", &b)); ATF_REQUIRE(b); + + RE(atf_text_to_bool("false", &b)); ATF_REQUIRE(!b); + RE(atf_text_to_bool("FALSE", &b)); ATF_REQUIRE(!b); + RE(atf_text_to_bool("no", &b)); ATF_REQUIRE(!b); + RE(atf_text_to_bool("NO", &b)); ATF_REQUIRE(!b); + + b = false; + REQUIRE_ERROR(atf_text_to_bool("", &b)); + ATF_REQUIRE(!b); + b = true; + REQUIRE_ERROR(atf_text_to_bool("", &b)); + ATF_REQUIRE(b); + + b = false; + REQUIRE_ERROR(atf_text_to_bool("tru", &b)); + ATF_REQUIRE(!b); + b = true; + REQUIRE_ERROR(atf_text_to_bool("tru", &b)); + ATF_REQUIRE(b); + + b = false; + REQUIRE_ERROR(atf_text_to_bool("true2", &b)); + ATF_REQUIRE(!b); + b = true; + REQUIRE_ERROR(atf_text_to_bool("true2", &b)); + ATF_REQUIRE(b); + + b = false; + REQUIRE_ERROR(atf_text_to_bool("fals", &b)); + ATF_REQUIRE(!b); + b = true; + REQUIRE_ERROR(atf_text_to_bool("fals", &b)); + ATF_REQUIRE(b); + + b = false; + REQUIRE_ERROR(atf_text_to_bool("false2", &b)); + ATF_REQUIRE(!b); + b = true; + REQUIRE_ERROR(atf_text_to_bool("false2", &b)); + ATF_REQUIRE(b); +} + +ATF_TC(to_long); +ATF_TC_HEAD(to_long, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_text_to_long function"); +} +ATF_TC_BODY(to_long, tc) +{ + long l; + + RE(atf_text_to_long("0", &l)); ATF_REQUIRE_EQ(l, 0); + RE(atf_text_to_long("-5", &l)); ATF_REQUIRE_EQ(l, -5); + RE(atf_text_to_long("5", &l)); ATF_REQUIRE_EQ(l, 5); + RE(atf_text_to_long("123456789", &l)); ATF_REQUIRE_EQ(l, 123456789); + + l = 1212; + REQUIRE_ERROR(atf_text_to_long("", &l)); + ATF_REQUIRE_EQ(l, 1212); + REQUIRE_ERROR(atf_text_to_long("foo", &l)); + ATF_REQUIRE_EQ(l, 1212); + REQUIRE_ERROR(atf_text_to_long("1234x", &l)); + ATF_REQUIRE_EQ(l, 1212); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, for_each_word); + ATF_TP_ADD_TC(tp, format); + ATF_TP_ADD_TC(tp, format_ap); + ATF_TP_ADD_TC(tp, split); + ATF_TP_ADD_TC(tp, split_delims); + ATF_TP_ADD_TC(tp, to_bool); + ATF_TP_ADD_TC(tp, to_long); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/tp_main.c b/unit/atf-src/atf-c/detail/tp_main.c new file mode 100644 index 0000000..229615b --- /dev/null +++ b/unit/atf-src/atf-c/detail/tp_main.c @@ -0,0 +1,615 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "atf-c/detail/dynstr.h" +#include "atf-c/detail/env.h" +#include "atf-c/detail/fs.h" +#include "atf-c/detail/map.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" +#include "atf-c/tc.h" +#include "atf-c/tp.h" +#include "atf-c/utils.h" + +#if defined(HAVE_GNU_GETOPT) +# define GETOPT_POSIX "+" +#else +# define GETOPT_POSIX "" +#endif + +static const char *progname = NULL; + +/* This prototype is provided by macros.h during instantiation of the test + * program, so it can be kept private. Don't know if that's the best idea + * though. */ +int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *)); + +enum tc_part { + BODY, + CLEANUP, +}; + +/* --------------------------------------------------------------------- + * The "usage" and "user" error types. + * --------------------------------------------------------------------- */ + +#define FREE_FORM_ERROR(name) \ + struct name ## _error_data { \ + char m_what[2048]; \ + }; \ + \ + static \ + void \ + name ## _format(const atf_error_t err, char *buf, size_t buflen) \ + { \ + const struct name ## _error_data *data; \ + \ + PRE(atf_error_is(err, #name)); \ + \ + data = atf_error_data(err); \ + snprintf(buf, buflen, "%s", data->m_what); \ + } \ + \ + static \ + atf_error_t \ + name ## _error(const char *fmt, ...) \ + { \ + atf_error_t err; \ + struct name ## _error_data data; \ + va_list ap; \ + \ + va_start(ap, fmt); \ + vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \ + va_end(ap); \ + \ + err = atf_error_new(#name, &data, sizeof(data), name ## _format); \ + \ + return err; \ + } + +FREE_FORM_ERROR(usage); +FREE_FORM_ERROR(user); + +/* --------------------------------------------------------------------- + * Printing functions. + * --------------------------------------------------------------------- */ + +static +void +print_error(const atf_error_t err) +{ + char buf[4096]; + + PRE(atf_is_error(err)); + + atf_error_format(err, buf, sizeof(buf)); + fprintf(stderr, "%s: ERROR: %s\n", progname, buf); + + if (atf_error_is(err, "usage")) + fprintf(stderr, "%s: See atf-test-program(1) for usage details.\n", + progname); +} + +static +void +print_warning(const char *message) +{ + fprintf(stderr, "%s: WARNING: %s\n", progname, message); +} + +/* --------------------------------------------------------------------- + * Options handling. + * --------------------------------------------------------------------- */ + +struct params { + bool m_do_list; + atf_fs_path_t m_srcdir; + char *m_tcname; + enum tc_part m_tcpart; + atf_fs_path_t m_resfile; + atf_map_t m_config; +}; + +static +atf_error_t +argv0_to_dir(const char *argv0, atf_fs_path_t *dir) +{ + atf_error_t err; + atf_fs_path_t temp; + + err = atf_fs_path_init_fmt(&temp, "%s", argv0); + if (atf_is_error(err)) + goto out; + + err = atf_fs_path_branch_path(&temp, dir); + + atf_fs_path_fini(&temp); +out: + return err; +} + +static +atf_error_t +params_init(struct params *p, const char *argv0) +{ + atf_error_t err; + + p->m_do_list = false; + p->m_tcname = NULL; + p->m_tcpart = BODY; + + err = argv0_to_dir(argv0, &p->m_srcdir); + if (atf_is_error(err)) + return err; + + err = atf_fs_path_init_fmt(&p->m_resfile, "/dev/stdout"); + if (atf_is_error(err)) { + atf_fs_path_fini(&p->m_srcdir); + return err; + } + + err = atf_map_init(&p->m_config); + if (atf_is_error(err)) { + atf_fs_path_fini(&p->m_resfile); + atf_fs_path_fini(&p->m_srcdir); + return err; + } + + return err; +} + +static +void +params_fini(struct params *p) +{ + atf_map_fini(&p->m_config); + atf_fs_path_fini(&p->m_resfile); + atf_fs_path_fini(&p->m_srcdir); + if (p->m_tcname != NULL) + free(p->m_tcname); +} + +static +atf_error_t +parse_vflag(char *arg, atf_map_t *config) +{ + atf_error_t err; + char *split; + + split = strchr(arg, '='); + if (split == NULL) { + err = usage_error("-v requires an argument of the form var=value"); + goto out; + } + + *split = '\0'; + split++; + + err = atf_map_insert(config, arg, split, false); + +out: + return err; +} + +static +atf_error_t +replace_path_param(atf_fs_path_t *param, const char *value) +{ + atf_error_t err; + atf_fs_path_t temp; + + err = atf_fs_path_init_fmt(&temp, "%s", value); + if (!atf_is_error(err)) { + atf_fs_path_fini(param); + *param = temp; + } + + return err; +} + +/* --------------------------------------------------------------------- + * Test case listing. + * --------------------------------------------------------------------- */ + +static +void +list_tcs(const atf_tp_t *tp) +{ + const atf_tc_t *const *tcs; + const atf_tc_t *const *tcsptr; + + printf("Content-Type: application/X-atf-tp; version=\"1\"\n\n"); + + tcs = atf_tp_get_tcs(tp); + INV(tcs != NULL); /* Should be checked. */ + for (tcsptr = tcs; *tcsptr != NULL; tcsptr++) { + const atf_tc_t *tc = *tcsptr; + char **vars = atf_tc_get_md_vars(tc); + char **ptr; + + INV(vars != NULL); /* Should be checked. */ + + if (tcsptr != tcs) /* Not first. */ + printf("\n"); + + for (ptr = vars; *ptr != NULL; ptr += 2) { + if (strcmp(*ptr, "ident") == 0) { + printf("ident: %s\n", *(ptr + 1)); + break; + } + } + + for (ptr = vars; *ptr != NULL; ptr += 2) { + if (strcmp(*ptr, "ident") != 0) { + printf("%s: %s\n", *ptr, *(ptr + 1)); + } + } + + atf_utils_free_charpp(vars); + } +#define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) + free(UNCONST(tcs)); +#undef UNCONST +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +static +atf_error_t +handle_tcarg(const char *tcarg, char **tcname, enum tc_part *tcpart) +{ + atf_error_t err; + + err = atf_no_error(); + + *tcname = strdup(tcarg); + if (*tcname == NULL) { + err = atf_no_memory_error(); + goto out; + } + + char *delim = strchr(*tcname, ':'); + if (delim != NULL) { + *delim = '\0'; + + delim++; + if (strcmp(delim, "body") == 0) { + *tcpart = BODY; + } else if (strcmp(delim, "cleanup") == 0) { + *tcpart = CLEANUP; + } else { + err = usage_error("Invalid test case part `%s'", delim); + goto out; + } + } + +out: + return err; +} + +static +atf_error_t +process_params(int argc, char **argv, struct params *p) +{ + atf_error_t err; + int ch; + int old_opterr; + + err = params_init(p, argv[0]); + if (atf_is_error(err)) + goto out; + + old_opterr = opterr; + opterr = 0; + while (!atf_is_error(err) && + (ch = getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) { + switch (ch) { + case 'l': + p->m_do_list = true; + break; + + case 'r': + err = replace_path_param(&p->m_resfile, optarg); + break; + + case 's': + err = replace_path_param(&p->m_srcdir, optarg); + break; + + case 'v': + err = parse_vflag(optarg, &p->m_config); + break; + + case ':': + err = usage_error("Option -%c requires an argument.", optopt); + break; + + case '?': + default: + err = usage_error("Unknown option -%c.", optopt); + } + } + argc -= optind; + argv += optind; + + /* Clear getopt state just in case the test wants to use it. */ + opterr = old_opterr; + optind = 1; +#if defined(HAVE_OPTRESET) + optreset = 1; +#endif + + if (!atf_is_error(err)) { + if (p->m_do_list) { + if (argc > 0) + err = usage_error("Cannot provide test case names with -l"); + } else { + if (argc == 0) + err = usage_error("Must provide a test case name"); + else if (argc == 1) + err = handle_tcarg(argv[0], &p->m_tcname, &p->m_tcpart); + else if (argc > 1) { + err = usage_error("Cannot provide more than one test case " + "name"); + } + } + } + + if (atf_is_error(err)) + params_fini(p); + +out: + return err; +} + +static +atf_error_t +srcdir_strip_libtool(atf_fs_path_t *srcdir) +{ + atf_error_t err; + atf_fs_path_t parent; + + err = atf_fs_path_branch_path(srcdir, &parent); + if (atf_is_error(err)) + goto out; + + atf_fs_path_fini(srcdir); + *srcdir = parent; + + INV(!atf_is_error(err)); +out: + return err; +} + +static +atf_error_t +handle_srcdir(struct params *p) +{ + atf_error_t err; + atf_dynstr_t leafname; + atf_fs_path_t exe, srcdir; + bool b; + + err = atf_fs_path_copy(&srcdir, &p->m_srcdir); + if (atf_is_error(err)) + goto out; + + if (!atf_fs_path_is_absolute(&srcdir)) { + atf_fs_path_t srcdirabs; + + err = atf_fs_path_to_absolute(&srcdir, &srcdirabs); + if (atf_is_error(err)) + goto out_srcdir; + + atf_fs_path_fini(&srcdir); + srcdir = srcdirabs; + } + + err = atf_fs_path_leaf_name(&srcdir, &leafname); + if (atf_is_error(err)) + goto out_srcdir; + else { + const bool libs = atf_equal_dynstr_cstring(&leafname, ".libs"); + atf_dynstr_fini(&leafname); + + if (libs) { + err = srcdir_strip_libtool(&srcdir); + if (atf_is_error(err)) + goto out; + } + } + + err = atf_fs_path_copy(&exe, &srcdir); + if (atf_is_error(err)) + goto out_srcdir; + + err = atf_fs_path_append_fmt(&exe, "%s", progname); + if (atf_is_error(err)) + goto out_exe; + + err = atf_fs_exists(&exe, &b); + if (!atf_is_error(err)) { + if (b) { + err = atf_map_insert(&p->m_config, "srcdir", + strdup(atf_fs_path_cstring(&srcdir)), true); + } else { + err = user_error("Cannot find the test program in the source " + "directory `%s'", atf_fs_path_cstring(&srcdir)); + } + } + +out_exe: + atf_fs_path_fini(&exe); +out_srcdir: + atf_fs_path_fini(&srcdir); +out: + return err; +} + +static +atf_error_t +run_tc(const atf_tp_t *tp, struct params *p, int *exitcode) +{ + atf_error_t err; + + err = atf_no_error(); + + if (!atf_tp_has_tc(tp, p->m_tcname)) { + err = usage_error("Unknown test case `%s'", p->m_tcname); + goto out; + } + + if (!atf_env_has("__RUNNING_INSIDE_ATF_RUN") || strcmp(atf_env_get( + "__RUNNING_INSIDE_ATF_RUN"), "internal-yes-value") != 0) + { + print_warning("Running test cases outside of kyua(1) is unsupported"); + print_warning("No isolation nor timeout control is being applied; you " + "may get unexpected failures; see atf-test-case(4)"); + } + + switch (p->m_tcpart) { + case BODY: + err = atf_tp_run(tp, p->m_tcname, atf_fs_path_cstring(&p->m_resfile)); + if (atf_is_error(err)) { + /* TODO: Handle error */ + *exitcode = EXIT_FAILURE; + atf_error_free(err); + } else { + *exitcode = EXIT_SUCCESS; + } + + break; + + case CLEANUP: + err = atf_tp_cleanup(tp, p->m_tcname); + if (atf_is_error(err)) { + /* TODO: Handle error */ + *exitcode = EXIT_FAILURE; + atf_error_free(err); + } else { + *exitcode = EXIT_SUCCESS; + } + + break; + + default: + UNREACHABLE; + } + + INV(!atf_is_error(err)); +out: + return err; +} + +static +atf_error_t +controlled_main(int argc, char **argv, + atf_error_t (*add_tcs_hook)(atf_tp_t *), + int *exitcode) +{ + atf_error_t err; + struct params p; + atf_tp_t tp; + char **raw_config; + + err = process_params(argc, argv, &p); + if (atf_is_error(err)) + goto out; + + err = handle_srcdir(&p); + if (atf_is_error(err)) + goto out_p; + + raw_config = atf_map_to_charpp(&p.m_config); + if (raw_config == NULL) { + err = atf_no_memory_error(); + goto out_p; + } + err = atf_tp_init(&tp, (const char* const*)raw_config); + atf_utils_free_charpp(raw_config); + if (atf_is_error(err)) + goto out_p; + + err = add_tcs_hook(&tp); + if (atf_is_error(err)) + goto out_tp; + + if (p.m_do_list) { + list_tcs(&tp); + INV(!atf_is_error(err)); + *exitcode = EXIT_SUCCESS; + } else { + err = run_tc(&tp, &p, exitcode); + } + +out_tp: + atf_tp_fini(&tp); +out_p: + params_fini(&p); +out: + return err; +} + +int +atf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *)) +{ + atf_error_t err; + int exitcode; + + progname = strrchr(argv[0], '/'); + if (progname == NULL) + progname = argv[0]; + else + progname++; + + /* Libtool workaround: if running from within the source tree (binaries + * that are not installed yet), skip the "lt-" prefix added to files in + * the ".libs" directory to show the real (not temporary) name. */ + if (strncmp(progname, "lt-", 3) == 0) + progname += 3; + + exitcode = EXIT_FAILURE; /* Silence GCC warning. */ + err = controlled_main(argc, argv, add_tcs_hook, &exitcode); + if (atf_is_error(err)) { + print_error(err); + atf_error_free(err); + exitcode = EXIT_FAILURE; + } + + return exitcode; +} diff --git a/unit/atf-src/atf-c/detail/user.c b/unit/atf-src/atf-c/detail/user.c new file mode 100644 index 0000000..bbb4ded --- /dev/null +++ b/unit/atf-src/atf-c/detail/user.c @@ -0,0 +1,75 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/user.h" + +#include <sys/param.h> +#include <sys/types.h> +#include <limits.h> +#include <unistd.h> + +#include "atf-c/detail/sanity.h" + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +uid_t +atf_user_euid(void) +{ + return geteuid(); +} + +bool +atf_user_is_member_of_group(gid_t gid) +{ + static gid_t groups[NGROUPS_MAX]; + static int ngroups = -1; + bool found; + int i; + + if (ngroups == -1) { + ngroups = getgroups(NGROUPS_MAX, groups); + INV(ngroups >= 0); + } + + found = false; + for (i = 0; !found && i < ngroups; i++) + if (groups[i] == gid) + found = true; + return found; +} + +bool +atf_user_is_root(void) +{ + return geteuid() == 0; +} + +bool +atf_user_is_unprivileged(void) +{ + return geteuid() != 0; +} diff --git a/unit/atf-src/atf-c/detail/user.h b/unit/atf-src/atf-c/detail/user.h new file mode 100644 index 0000000..14a3e96 --- /dev/null +++ b/unit/atf-src/atf-c/detail/user.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_USER_H) +#define ATF_C_DETAIL_USER_H + +#include <sys/types.h> + +#include <stdbool.h> + +/* TODO: Would be nice to have an atf_user_t type and transform all of + * the functions below to methods. */ + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +uid_t atf_user_euid(void); +bool atf_user_is_member_of_group(gid_t); +bool atf_user_is_root(void); +bool atf_user_is_unprivileged(void); + +#endif /* !defined(ATF_C_DETAIL_USER_H) */ diff --git a/unit/atf-src/atf-c/detail/user_test.c b/unit/atf-src/atf-c/detail/user_test.c new file mode 100644 index 0000000..6a0f6e1 --- /dev/null +++ b/unit/atf-src/atf-c/detail/user_test.c @@ -0,0 +1,146 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/user.h" + +#include <sys/param.h> +#include <sys/types.h> + +#include <limits.h> +#include <stdio.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(euid); +ATF_TC_HEAD(euid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_user_euid function"); +} +ATF_TC_BODY(euid, tc) +{ + ATF_REQUIRE_EQ(atf_user_euid(), geteuid()); +} + +ATF_TC(is_member_of_group); +ATF_TC_HEAD(is_member_of_group, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_member_of_group " + "function"); +} +ATF_TC_BODY(is_member_of_group, tc) +{ + gid_t gids[NGROUPS_MAX]; + gid_t g, maxgid; + int ngids; + const gid_t maxgid_limit = 1 << 16; + + { + int i; + + ngids = getgroups(NGROUPS_MAX, gids); + if (ngids == -1) + atf_tc_fail("Call to getgroups failed"); + maxgid = 0; + for (i = 0; i < ngids; i++) { + printf("User group %d is %u\n", i, gids[i]); + if (maxgid < gids[i]) + maxgid = gids[i]; + } + printf("User belongs to %d groups\n", ngids); + printf("Last GID is %u\n", maxgid); + } + + if (maxgid > maxgid_limit) { + printf("Test truncated from %u groups to %u to keep the run time " + "reasonable enough\n", maxgid, maxgid_limit); + maxgid = maxgid_limit; + } + + for (g = 0; g < maxgid; g++) { + bool found = false; + int i; + + for (i = 0; !found && i < ngids; i++) { + if (gids[i] == g) + found = true; + } + + if (found) { + printf("Checking if user belongs to group %d\n", g); + ATF_REQUIRE(atf_user_is_member_of_group(g)); + } else { + printf("Checking if user does not belong to group %d\n", g); + ATF_REQUIRE(!atf_user_is_member_of_group(g)); + } + } +} + +ATF_TC(is_root); +ATF_TC_HEAD(is_root, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_root function"); +} +ATF_TC_BODY(is_root, tc) +{ + if (geteuid() == 0) + ATF_REQUIRE(atf_user_is_root()); + else + ATF_REQUIRE(!atf_user_is_root()); +} + +ATF_TC(is_unprivileged); +ATF_TC_HEAD(is_unprivileged, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_unprivileged " + "function"); +} +ATF_TC_BODY(is_unprivileged, tc) +{ + if (geteuid() != 0) + ATF_REQUIRE(atf_user_is_unprivileged()); + else + ATF_REQUIRE(!atf_user_is_unprivileged()); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, euid); + ATF_TP_ADD_TC(tp, is_member_of_group); + ATF_TP_ADD_TC(tp, is_root); + ATF_TP_ADD_TC(tp, is_unprivileged); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/version_helper.c b/unit/atf-src/atf-c/detail/version_helper.c new file mode 100644 index 0000000..fd48466 --- /dev/null +++ b/unit/atf-src/atf-c/detail/version_helper.c @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> + +int +main(void) +{ + printf("%s\n", PACKAGE_VERSION); + return EXIT_SUCCESS; +} |