diff options
author | Renato Botelho <renato@netgate.com> | 2016-01-07 18:08:08 -0200 |
---|---|---|
committer | Renato Botelho <renato@netgate.com> | 2016-01-07 18:08:08 -0200 |
commit | 7a579754077b3460c9509c75cdd1b78769c1db3f (patch) | |
tree | c15015685ccb82b7db1ac19d663a3c6e41e64587 /lib | |
parent | d8ff3484131f428fcc0727cd504acb5050a36490 (diff) | |
parent | bc6ee646001a22150936ff06bf11cd08195e208d (diff) | |
download | FreeBSD-src-7a579754077b3460c9509c75cdd1b78769c1db3f.zip FreeBSD-src-7a579754077b3460c9509c75cdd1b78769c1db3f.tar.gz |
Merge remote-tracking branch 'origin/stable/10' into devel
Diffstat (limited to 'lib')
33 files changed, 7982 insertions, 57 deletions
diff --git a/lib/Makefile b/lib/Makefile index ee83d59..250d583 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -70,6 +70,7 @@ SUBDIR= ${SUBDIR_ORDERED} \ libnetbsd \ ${_libnetgraph} \ ${_libngatm} \ + libnv \ libopie \ libpam \ libpcap \ diff --git a/lib/libbsm/Makefile b/lib/libbsm/Makefile index eec2c40..2e0dbe1 100644 --- a/lib/libbsm/Makefile +++ b/lib/libbsm/Makefile @@ -48,6 +48,7 @@ MAN= libbsm.3 \ au_free_token.3 \ au_io.3 \ au_mask.3 \ + au_notify.3 \ au_open.3 \ au_socket_type.3 \ au_token.3 \ @@ -112,6 +113,9 @@ MLINKS= libbsm.3 bsm.3 \ au_mask.3 au_preselect.3 \ au_mask.3 getauditflagsbin.3 \ au_mask.3 getauditflagschar.3 \ + au_notify.3 au_get_state.3 \ + au_notify.3 au_notify_initialize.3 \ + au_notify.3 au_notify_terminate.3 \ au_open.3 au_close.3 \ au_open.3 au_close_buffer.3 \ au_open.3 au_close_token.3 \ diff --git a/lib/libc/net/gethostbynis.c b/lib/libc/net/gethostbynis.c index c0d5177..6bafdb1 100644 --- a/lib/libc/net/gethostbynis.c +++ b/lib/libc/net/gethostbynis.c @@ -198,61 +198,6 @@ _gethostbynisaddr_r(const void *addr, socklen_t len, int af, } #endif /* YP */ -/* XXX _gethostbynisname/_gethostbynisaddr only used by getipnodeby*() */ -struct hostent * -_gethostbynisname(const char *name, int af) -{ -#ifdef YP - struct hostent *he; - struct hostent_data *hed; - u_long oresopt; - int error; - res_state statp; - - statp = __res_state(); - if ((he = __hostent_init()) == NULL || - (hed = __hostent_data_init()) == NULL) { - RES_SET_H_ERRNO(statp, NETDB_INTERNAL); - return (NULL); - } - - oresopt = statp->options; - statp->options &= ~RES_USE_INET6; - error = _gethostbynisname_r(name, af, he, hed); - statp->options = oresopt; - return (error == 0) ? he : NULL; -#else - return (NULL); -#endif -} - -struct hostent * -_gethostbynisaddr(const void *addr, socklen_t len, int af) -{ -#ifdef YP - struct hostent *he; - struct hostent_data *hed; - u_long oresopt; - int error; - res_state statp; - - statp = __res_state(); - if ((he = __hostent_init()) == NULL || - (hed = __hostent_data_init()) == NULL) { - RES_SET_H_ERRNO(statp, NETDB_INTERNAL); - return (NULL); - } - - oresopt = statp->options; - statp->options &= ~RES_USE_INET6; - error = _gethostbynisaddr_r(addr, len, af, he, hed); - statp->options = oresopt; - return (error == 0) ? he : NULL; -#else - return (NULL); -#endif -} - int _nis_gethostbyname(void *rval, void *cb_data, va_list ap) { diff --git a/lib/libc/net/netdb_private.h b/lib/libc/net/netdb_private.h index b48dd7b..4ed598c 100644 --- a/lib/libc/net/netdb_private.h +++ b/lib/libc/net/netdb_private.h @@ -133,8 +133,6 @@ void _endhostdnsent(void); void _endhosthtent(struct hostent_data *); void _endnetdnsent(void); void _endnethtent(struct netent_data *); -struct hostent *_gethostbynisaddr(const void *, socklen_t, int); -struct hostent *_gethostbynisname(const char *, int); void _map_v4v6_address(const char *, char *); void _map_v4v6_hostent(struct hostent *, char **, char *); void _sethostdnsent(int); diff --git a/lib/libnv/Makefile b/lib/libnv/Makefile new file mode 100644 index 0000000..47dfe04 --- /dev/null +++ b/lib/libnv/Makefile @@ -0,0 +1,92 @@ +# $FreeBSD$ + +SHLIBDIR?= /lib + +.include <bsd.own.mk> + +LIB= nv +SHLIB_MAJOR= 0 + +.PATH: ${.CURDIR}/../../sys/kern ${.CURDIR}/../../sys/sys +CFLAGS+=-I${.CURDIR}/../../sys -I${.CURDIR} + +SRCS= subr_dnvlist.c +SRCS+= msgio.c +SRCS+= subr_nvlist.c +SRCS+= subr_nvpair.c + +INCS= dnv.h +INCS+= nv.h + +MAN+= nv.3 + +MLINKS+=nv.3 libnv.3 \ + nv.3 nvlist.3 +MLINKS+=nv.3 nvlist_add_binary.3 \ + nv.3 nvlist_add_bool.3 \ + nv.3 nvlist_add_descriptor.3 \ + nv.3 nvlist_add_null.3 \ + nv.3 nvlist_add_number.3 \ + nv.3 nvlist_add_nvlist.3 \ + nv.3 nvlist_add_string.3 \ + nv.3 nvlist_add_stringf.3 \ + nv.3 nvlist_add_stringv.3 \ + nv.3 nvlist_clone.3 \ + nv.3 nvlist_create.3 \ + nv.3 nvlist_destroy.3 \ + nv.3 nvlist_dump.3 \ + nv.3 nvlist_empty.3 \ + nv.3 nvlist_error.3 \ + nv.3 nvlist_exists.3 \ + nv.3 nvlist_exists_binary.3 \ + nv.3 nvlist_exists_bool.3 \ + nv.3 nvlist_exists_descriptor.3 \ + nv.3 nvlist_exists_null.3 \ + nv.3 nvlist_exists_number.3 \ + nv.3 nvlist_exists_nvlist.3 \ + nv.3 nvlist_exists_string.3 \ + nv.3 nvlist_exists_type.3 \ + nv.3 nvlist_fdump.3 \ + nv.3 nvlist_flags.3 \ + nv.3 nvlist_free.3 \ + nv.3 nvlist_free_binary.3 \ + nv.3 nvlist_free_bool.3 \ + nv.3 nvlist_free_descriptor.3 \ + nv.3 nvlist_free_null.3 \ + nv.3 nvlist_free_number.3 \ + nv.3 nvlist_free_nvlist.3 \ + nv.3 nvlist_free_string.3 \ + nv.3 nvlist_free_type.3 \ + nv.3 nvlist_get_binary.3 \ + nv.3 nvlist_get_bool.3 \ + nv.3 nvlist_get_descriptor.3 \ + nv.3 nvlist_get_number.3 \ + nv.3 nvlist_get_nvlist.3 \ + nv.3 nvlist_get_parent.3 \ + nv.3 nvlist_get_string.3 \ + nv.3 nvlist_move_binary.3 \ + nv.3 nvlist_move_descriptor.3 \ + nv.3 nvlist_move_nvlist.3 \ + nv.3 nvlist_move_string.3 \ + nv.3 nvlist_next.3 \ + nv.3 nvlist_pack.3 \ + nv.3 nvlist_recv.3 \ + nv.3 nvlist_send.3 \ + nv.3 nvlist_set_error.3 \ + nv.3 nvlist_size.3 \ + nv.3 nvlist_take_binary.3 \ + nv.3 nvlist_take_bool.3 \ + nv.3 nvlist_take_descriptor.3 \ + nv.3 nvlist_take_number.3 \ + nv.3 nvlist_take_nvlist.3 \ + nv.3 nvlist_take_string.3 \ + nv.3 nvlist_unpack.3 \ + nv.3 nvlist_xfer.3 + +WARNS?= 6 + +.if ${MK_TESTS} != "no" +SUBDIR+= tests +.endif + +.include <bsd.lib.mk> diff --git a/lib/libnv/common_impl.h b/lib/libnv/common_impl.h new file mode 100644 index 0000000..5af4db2 --- /dev/null +++ b/lib/libnv/common_impl.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#ifndef _COMMON_IMPL_H_ +#define _COMMON_IMPL_H_ + +#define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) + +#endif /* !_COMMON_IMPL_H_ */ diff --git a/lib/libnv/msgio.c b/lib/libnv/msgio.c new file mode 100644 index 0000000..27620a1 --- /dev/null +++ b/lib/libnv/msgio.c @@ -0,0 +1,469 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org> + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef HAVE_PJDLOG +#include <pjdlog.h> +#endif + +#include "common_impl.h" +#include "msgio.h" + +#ifndef HAVE_PJDLOG +#include <assert.h> +#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) assert(expr) +#define PJDLOG_ABORT(...) abort() +#endif + +#define PKG_MAX_SIZE (MCLBYTES / CMSG_SPACE(sizeof(int)) - 1) + +static int +msghdr_add_fd(struct cmsghdr *cmsg, int fd) +{ + + PJDLOG_ASSERT(fd >= 0); + + if (!fd_is_valid(fd)) { + errno = EBADF; + return (-1); + } + + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd)); + + return (0); +} + +static int +msghdr_get_fd(struct cmsghdr *cmsg) +{ + int fd; + + if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS || + cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) { + errno = EINVAL; + return (-1); + } + + bcopy(CMSG_DATA(cmsg), &fd, sizeof(fd)); +#ifndef MSG_CMSG_CLOEXEC + /* + * If the MSG_CMSG_CLOEXEC flag is not available we cannot set the + * close-on-exec flag atomically, but we still want to set it for + * consistency. + */ + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + + return (fd); +} + +static void +fd_wait(int fd, bool doread) +{ + fd_set fds; + + PJDLOG_ASSERT(fd >= 0); + + FD_ZERO(&fds); + FD_SET(fd, &fds); + (void)select(fd + 1, doread ? &fds : NULL, doread ? NULL : &fds, + NULL, NULL); +} + +static int +msg_recv(int sock, struct msghdr *msg) +{ + int flags; + + PJDLOG_ASSERT(sock >= 0); + +#ifdef MSG_CMSG_CLOEXEC + flags = MSG_CMSG_CLOEXEC; +#else + flags = 0; +#endif + + for (;;) { + fd_wait(sock, true); + if (recvmsg(sock, msg, flags) == -1) { + if (errno == EINTR) + continue; + return (-1); + } + break; + } + + return (0); +} + +static int +msg_send(int sock, const struct msghdr *msg) +{ + + PJDLOG_ASSERT(sock >= 0); + + for (;;) { + fd_wait(sock, false); + if (sendmsg(sock, msg, 0) == -1) { + if (errno == EINTR) + continue; + return (-1); + } + break; + } + + return (0); +} + +int +cred_send(int sock) +{ + unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + uint8_t dummy; + + bzero(credbuf, sizeof(credbuf)); + bzero(&msg, sizeof(msg)); + bzero(&iov, sizeof(iov)); + + /* + * XXX: We send one byte along with the control message, because + * setting msg_iov to NULL only works if this is the first + * packet send over the socket. Once we send some data we + * won't be able to send credentials anymore. This is most + * likely a kernel bug. + */ + dummy = 0; + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = credbuf; + msg.msg_controllen = sizeof(credbuf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDS; + + if (msg_send(sock, &msg) == -1) + return (-1); + + return (0); +} + +int +cred_recv(int sock, struct cmsgcred *cred) +{ + unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + uint8_t dummy; + + bzero(credbuf, sizeof(credbuf)); + bzero(&msg, sizeof(msg)); + bzero(&iov, sizeof(iov)); + + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = credbuf; + msg.msg_controllen = sizeof(credbuf); + + if (msg_recv(sock, &msg) == -1) + return (-1); + + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == NULL || + cmsg->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred)) || + cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDS) { + errno = EINVAL; + return (-1); + } + bcopy(CMSG_DATA(cmsg), cred, sizeof(*cred)); + + return (0); +} + +static int +fd_package_send(int sock, const int *fds, size_t nfds) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + unsigned int i; + int serrno, ret; + uint8_t dummy; + + PJDLOG_ASSERT(sock >= 0); + PJDLOG_ASSERT(fds != NULL); + PJDLOG_ASSERT(nfds > 0); + + bzero(&msg, sizeof(msg)); + + /* + * XXX: Look into cred_send function for more details. + */ + dummy = 0; + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int)); + msg.msg_control = calloc(1, msg.msg_controllen); + if (msg.msg_control == NULL) + return (-1); + + ret = -1; + + for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL; + i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (msghdr_add_fd(cmsg, fds[i]) == -1) + goto end; + } + + if (msg_send(sock, &msg) == -1) + goto end; + + ret = 0; +end: + serrno = errno; + free(msg.msg_control); + errno = serrno; + return (ret); +} + +static int +fd_package_recv(int sock, int *fds, size_t nfds) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + unsigned int i; + int serrno, ret; + struct iovec iov; + uint8_t dummy; + + PJDLOG_ASSERT(sock >= 0); + PJDLOG_ASSERT(nfds > 0); + PJDLOG_ASSERT(fds != NULL); + + i = 0; + bzero(&msg, sizeof(msg)); + bzero(&iov, sizeof(iov)); + + /* + * XXX: Look into cred_send function for more details. + */ + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int)); + msg.msg_control = calloc(1, msg.msg_controllen); + if (msg.msg_control == NULL) + return (-1); + + ret = -1; + + if (msg_recv(sock, &msg) == -1) + goto end; + + for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL; + i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) { + fds[i] = msghdr_get_fd(cmsg); + if (fds[i] < 0) + break; + } + + if (cmsg != NULL || i < nfds) { + int fd; + + /* + * We need to close all received descriptors, even if we have + * different control message (eg. SCM_CREDS) in between. + */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + fd = msghdr_get_fd(cmsg); + if (fd >= 0) + close(fd); + } + errno = EINVAL; + goto end; + } + + ret = 0; +end: + serrno = errno; + free(msg.msg_control); + errno = serrno; + return (ret); +} + +int +fd_recv(int sock, int *fds, size_t nfds) +{ + unsigned int i, step, j; + int ret, serrno; + + if (nfds == 0 || fds == NULL) { + errno = EINVAL; + return (-1); + } + + ret = i = step = 0; + while (i < nfds) { + if (PKG_MAX_SIZE < nfds - i) + step = PKG_MAX_SIZE; + else + step = nfds - i; + ret = fd_package_recv(sock, fds + i, step); + if (ret != 0) { + /* Close all received descriptors. */ + serrno = errno; + for (j = 0; j < i; j++) + close(fds[j]); + errno = serrno; + break; + } + i += step; + } + + return (ret); +} + +int +fd_send(int sock, const int *fds, size_t nfds) +{ + unsigned int i, step; + int ret; + + if (nfds == 0 || fds == NULL) { + errno = EINVAL; + return (-1); + } + + ret = i = step = 0; + while (i < nfds) { + if (PKG_MAX_SIZE < nfds - i) + step = PKG_MAX_SIZE; + else + step = nfds - i; + ret = fd_package_send(sock, fds + i, step); + if (ret != 0) + break; + i += step; + } + + return (ret); +} + +int +buf_send(int sock, void *buf, size_t size) +{ + ssize_t done; + unsigned char *ptr; + + PJDLOG_ASSERT(sock >= 0); + PJDLOG_ASSERT(size > 0); + PJDLOG_ASSERT(buf != NULL); + + ptr = buf; + do { + fd_wait(sock, false); + done = send(sock, ptr, size, 0); + if (done == -1) { + if (errno == EINTR) + continue; + return (-1); + } else if (done == 0) { + errno = ENOTCONN; + return (-1); + } + size -= done; + ptr += done; + } while (size > 0); + + return (0); +} + +int +buf_recv(int sock, void *buf, size_t size) +{ + ssize_t done; + unsigned char *ptr; + + PJDLOG_ASSERT(sock >= 0); + PJDLOG_ASSERT(buf != NULL); + + ptr = buf; + while (size > 0) { + fd_wait(sock, true); + done = recv(sock, ptr, size, 0); + if (done == -1) { + if (errno == EINTR) + continue; + return (-1); + } else if (done == 0) { + errno = ENOTCONN; + return (-1); + } + size -= done; + ptr += done; + } + + return (0); +} diff --git a/lib/libnv/msgio.h b/lib/libnv/msgio.h new file mode 100644 index 0000000..fd5e462 --- /dev/null +++ b/lib/libnv/msgio.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org> + * + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#ifndef _MSGIO_H_ +#define _MSGIO_H_ + +struct cmsgcred; +struct iovec; +struct msghdr; + +int cred_send(int sock); +int cred_recv(int sock, struct cmsgcred *cred); + +int fd_send(int sock, const int *fds, size_t nfds); +int fd_recv(int sock, int *fds, size_t nfds); + +int buf_send(int sock, void *buf, size_t size); +int buf_recv(int sock, void *buf, size_t size); + +#endif /* !_MSGIO_H_ */ diff --git a/lib/libnv/nv.3 b/lib/libnv/nv.3 new file mode 100644 index 0000000..947bca8 --- /dev/null +++ b/lib/libnv/nv.3 @@ -0,0 +1,653 @@ +.\" +.\" Copyright (c) 2013 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This documentation was written by Pawel Jakub Dawidek under sponsorship +.\" the FreeBSD Foundation. +.\" +.\" 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 AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd May 1, 2015 +.Dt NV 3 +.Os +.Sh NAME +.Nm nvlist_create , +.Nm nvlist_destroy , +.Nm nvlist_error , +.Nm nvlist_set_error , +.Nm nvlist_empty , +.Nm nvlist_flags , +.Nm nvlist_exists , +.Nm nvlist_free , +.Nm nvlist_clone , +.Nm nvlist_dump , +.Nm nvlist_fdump , +.Nm nvlist_size , +.Nm nvlist_pack , +.Nm nvlist_unpack , +.Nm nvlist_send , +.Nm nvlist_recv , +.Nm nvlist_xfer , +.Nm nvlist_next , +.Nm nvlist_add , +.Nm nvlist_move , +.Nm nvlist_get , +.Nm nvlist_take +.Nd "library for name/value pairs" +.Sh LIBRARY +.Lb libnv +.Sh SYNOPSIS +.In nv.h +.Ft "nvlist_t *" +.Fn nvlist_create "int flags" +.Ft void +.Fn nvlist_destroy "nvlist_t *nvl" +.Ft int +.Fn nvlist_error "const nvlist_t *nvl" +.Ft void +.Fn nvlist_set_error "nvlist_t *nvl, int error" +.Ft bool +.Fn nvlist_empty "const nvlist_t *nvl" +.Ft int +.Fn nvlist_flags "const nvlist_t *nvl" +.\" +.Ft "nvlist_t *" +.Fn nvlist_clone "const nvlist_t *nvl" +.\" +.Ft void +.Fn nvlist_dump "const nvlist_t *nvl, int fd" +.Ft void +.Fn nvlist_fdump "const nvlist_t *nvl, FILE *fp" +.\" +.Ft size_t +.Fn nvlist_size "const nvlist_t *nvl" +.Ft "void *" +.Fn nvlist_pack "const nvlist_t *nvl" "size_t *sizep" +.Ft "nvlist_t *" +.Fn nvlist_unpack "const void *buf" "size_t size" +.\" +.Ft int +.Fn nvlist_send "int sock" "const nvlist_t *nvl" +.Ft "nvlist_t *" +.Fn nvlist_recv "int sock" +.Ft "nvlist_t *" +.Fn nvlist_xfer "int sock" "nvlist_t *nvl" +.\" +.Ft "const char *" +.Fn nvlist_next "const nvlist_t *nvl" "int *typep" "void **cookiep" +.\" +.Ft bool +.Fn nvlist_exists "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_type "const nvlist_t *nvl" "const char *name" "int type" +.Ft bool +.Fn nvlist_exists_null "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_bool "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_number "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_string "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_nvlist "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_descriptor "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_binary "const nvlist_t *nvl" "const char *name" +.\" +.Ft void +.Fn nvlist_add_null "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_add_bool "nvlist_t *nvl" "const char *name" "bool value" +.Ft void +.Fn nvlist_add_number "nvlist_t *nvl" "const char *name" "uint64_t value" +.Ft void +.Fn nvlist_add_string "nvlist_t *nvl" "const char *name" "const char *value" +.Ft void +.Fn nvlist_add_stringf "nvlist_t *nvl" "const char *name" "const char *valuefmt" "..." +.Ft void +.Fn nvlist_add_stringv "nvlist_t *nvl" "const char *name" "const char *valuefmt" "va_list valueap" +.Ft void +.Fn nvlist_add_nvlist "nvlist_t *nvl" "const char *name" "const nvlist_t *value" +.Ft void +.Fn nvlist_add_descriptor "nvlist_t *nvl" "const char *name" "int value" +.Ft void +.Fn nvlist_add_binary "nvlist_t *nvl" "const char *name" "const void *value" "size_t size" +.\" +.Ft void +.Fn nvlist_move_string "nvlist_t *nvl" "const char *name" "char *value" +.Ft void +.Fn nvlist_move_nvlist "nvlist_t *nvl" "const char *name" "nvlist_t *value" +.Ft void +.Fn nvlist_move_descriptor "nvlist_t *nvl" "const char *name" "int value" +.Ft void +.Fn nvlist_move_binary "nvlist_t *nvl" "const char *name" "void *value" "size_t size" +.\" +.Ft bool +.Fn nvlist_get_bool "const nvlist_t *nvl" "const char *name" +.Ft uint64_t +.Fn nvlist_get_number "const nvlist_t *nvl" "const char *name" +.Ft "const char *" +.Fn nvlist_get_string "const nvlist_t *nvl" "const char *name" +.Ft "const nvlist_t *" +.Fn nvlist_get_nvlist "const nvlist_t *nvl" "const char *name" +.Ft int +.Fn nvlist_get_descriptor "const nvlist_t *nvl" "const char *name" +.Ft "const void *" +.Fn nvlist_get_binary "const nvlist_t *nvl" "const char *name" "size_t *sizep" +.Ft "const nvlist_t *" +.Fn nvlist_get_parent "const nvlist_t *nvl" "void **cookiep" +.\" +.Ft bool +.Fn nvlist_take_bool "nvlist_t *nvl" "const char *name" +.Ft uint64_t +.Fn nvlist_take_number "nvlist_t *nvl" "const char *name" +.Ft "char *" +.Fn nvlist_take_string "nvlist_t *nvl" "const char *name" +.Ft "nvlist_t *" +.Fn nvlist_take_nvlist "nvlist_t *nvl" "const char *name" +.Ft int +.Fn nvlist_take_descriptor "nvlist_t *nvl" "const char *name" +.Ft "void *" +.Fn nvlist_take_binary "nvlist_t *nvl" "const char *name" "size_t *sizep" +.\" +.Ft void +.Fn nvlist_free "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_type "nvlist_t *nvl" "const char *name" "int type" +.\" +.Ft void +.Fn nvlist_free_null "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_bool "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_number "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_string "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_nvlist "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_descriptor "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_binary "nvlist_t *nvl" "const char *name" +.Sh DESCRIPTION +The +.Nm libnv +library allows to easily manage name value pairs as well as send and receive +them over sockets. +A group (list) of name value pairs is called an +.Nm nvlist . +The API supports the following data types: +.Bl -ohang -offset indent +.It Sy null ( NV_TYPE_NULL ) +There is no data associated with the name. +.It Sy bool ( NV_TYPE_BOOL ) +The value can be either +.Dv true +or +.Dv false . +.It Sy number ( NV_TYPE_NUMBER ) +The value is a number stored as +.Vt uint64_t . +.It Sy string ( NV_TYPE_STRING ) +The value is a C string. +.It Sy nvlist ( NV_TYPE_NVLIST ) +The value is a nested nvlist. +.It Sy descriptor ( NV_TYPE_DESCRIPTOR ) +The value is a file descriptor. +Note that file descriptors can be sent only over +.Xr unix 4 +domain sockets. +.It Sy binary ( NV_TYPE_BINARY ) +The value is a binary buffer. +.El +.Pp +The +.Fn nvlist_create +function allocates memory and initializes an nvlist. +.Pp +The following flag can be provided: +.Pp +.Bl -tag -width "NV_FLAG_IGNORE_CASE" -compact -offset indent +.It Dv NV_FLAG_IGNORE_CASE +Perform case-insensitive lookups of provided names. +.El +.Pp +The +.Fn nvlist_destroy +function destroys the given nvlist. +Function does nothing if +.Dv NULL +nvlist is provided. +Function never modifies the +.Va errno +global variable. +.Pp +The +.Fn nvlist_error +function returns any error value that the nvlist accumulated. +If the given nvlist is +.Dv NULL +the +.Er ENOMEM +error will be returned. +.Pp +The +.Fn nvlist_set_error +function sets an nvlist to be in the error state. +Subsequent calls to +.Fn nvlist_error +will return the given error value. +This function cannot be used to clear the error state from an nvlist. +This function does nothing if the nvlist is already in the error state. +.Pp +The +.Fn nvlist_empty +function returns +.Dv true +if the given nvlist is empty and +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_flags +function returns flags used to create the nvlist with the +.Fn nvlist_create +function. +.Pp +The +.Fn nvlist_clone +functions clones the given nvlist. +The clone shares no resources with its origin. +This also means that all file descriptors that are part of the nvlist will be +duplicated with the +.Xr dup 2 +system call before placing them in the clone. +.Pp +The +.Fn nvlist_dump +dumps nvlist content for debugging purposes to the given file descriptor +.Fa fd . +.Pp +The +.Fn nvlist_fdump +dumps nvlist content for debugging purposes to the given file stream +.Fa fp . +.Pp +The +.Fn nvlist_size +function returns the size of the given nvlist after converting it to binary +buffer with the +.Fn nvlist_pack +function. +.Pp +The +.Fn nvlist_pack +function converts the given nvlist to a binary buffer. +The function allocates memory for the buffer, which should be freed with the +.Xr free 3 +function. +If the +.Fa sizep +argument is not +.Dv NULL , +the size of the buffer will be stored there. +The function returns +.Dv NULL +in case of an error (allocation failure). +If the nvlist contains any file descriptors +.Dv NULL +will be returned. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_unpack +function converts the given buffer to the nvlist. +The function returns +.Dv NULL +in case of an error. +.Pp +The +.Fn nvlist_send +function sends the given nvlist over the socket given by the +.Fa sock +argument. +Note that nvlist that contains file descriptors can only be send over +.Xr unix 4 +domain sockets. +.Pp +The +.Fn nvlist_recv +function receives nvlist over the socket given by the +.Fa sock +argument. +.Pp +The +.Fn nvlist_xfer +function sends the given nvlist over the socket given by the +.Fa sock +argument and receives nvlist over the same socket. +The given nvlist is always destroyed. +.Pp +The +.Fn nvlist_next +function iterates over the given nvlist returning names and types of subsequent +elements. +The +.Fa cookiep +argument allows the function to figure out which element should be returned +next. +The +.Va *cookiep +should be set to +.Dv NULL +for the first call and should not be changed later. +Returning +.Dv NULL +means there are no more elements on the nvlist. +The +.Fa typep +argument can be NULL. +Elements may not be removed from the nvlist while traversing it. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_exists +function returns +.Dv true +if element of the given name exists (besides of its type) or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_exists_type +function returns +.Dv true +if element of the given name and the given type exists or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_exists_null , +.Fn nvlist_exists_bool , +.Fn nvlist_exists_number , +.Fn nvlist_exists_string , +.Fn nvlist_exists_nvlist , +.Fn nvlist_exists_descriptor , +.Fn nvlist_exists_binary +functions return +.Dv true +if element of the given name and the given type determined by the function name +exists or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_add_null , +.Fn nvlist_add_bool , +.Fn nvlist_add_number , +.Fn nvlist_add_string , +.Fn nvlist_add_stringf , +.Fn nvlist_add_stringv , +.Fn nvlist_add_nvlist , +.Fn nvlist_add_descriptor , +.Fn nvlist_add_binary +functions add element to the given nvlist. +When adding string or binary buffor the functions will allocate memory +and copy the data over. +When adding nvlist, the nvlist will be cloned and clone will be added. +When adding descriptor, the descriptor will be duplicated using the +.Xr dup 2 +system call and the new descriptor will be added. +If an error occurs while adding new element, internal error is set which can be +examined using the +.Fn nvlist_error +function. +.Pp +The +.Fn nvlist_move_string , +.Fn nvlist_move_nvlist , +.Fn nvlist_move_descriptor , +.Fn nvlist_move_binary +functions add new element to the given nvlist, but unlike +.Fn nvlist_add_<type> +functions they will consume the given resource. +If an error occurs while adding new element, the resource is destroyed and +internal error is set which can be examined using the +.Fn nvlist_error +function. +.Pp +The +.Fn nvlist_get_bool , +.Fn nvlist_get_number , +.Fn nvlist_get_string , +.Fn nvlist_get_nvlist , +.Fn nvlist_get_descriptor , +.Fn nvlist_get_binary +functions allow to obtain value of the given name. +In case of string, nvlist, descriptor or binary, returned resource should +not be modified - it still belongs to the nvlist. +If element of the given name does not exist, the program will be aborted. +To avoid that the caller should check for existence before trying to obtain +the value or use +.Xr dnvlist 3 +extension, which allows to provide default value for a missing element. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_get_parent +function allows to obtain the parent nvlist from the nested nvlist. +.Pp +The +.Fn nvlist_take_bool , +.Fn nvlist_take_number , +.Fn nvlist_take_string , +.Fn nvlist_take_nvlist , +.Fn nvlist_take_descriptor , +.Fn nvlist_take_binary +functions return value associated with the given name and remove the element +from the nvlist. +In case of string and binary values, the caller is responsible for free returned +memory using the +.Xr free 3 +function. +In case of nvlist, the caller is responsible for destroying returned nvlist +using the +.Fn nvlist_destroy +function. +In case of descriptor, the caller is responsible for closing returned descriptor +using the +.Fn close 2 +system call. +If element of the given name does not exist, the program will be aborted. +To avoid that the caller should check for existence before trying to obtain +the value or use +.Xr dnvlist 3 +extension, which allows to provide default value for a missing element. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free +function removes element of the given name from the nvlist (besides of its type) +and frees all resources associated with it. +If element of the given name does not exist, the program will be aborted. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free_type +function removes element of the given name and the given type from the nvlist +and frees all resources associated with it. +If element of the given name and the given type does not exist, the program +will be aborted. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free_null , +.Fn nvlist_free_bool , +.Fn nvlist_free_number , +.Fn nvlist_free_string , +.Fn nvlist_free_nvlist , +.Fn nvlist_free_descriptor , +.Fn nvlist_free_binary +functions remove element of the given name and the given type determined by the +function name from the nvlist and free all resources associated with it. +If element of the given name and the given type does not exist, the program +will be aborted. +The nvlist must not be in error state. +.Sh EXAMPLES +The following example demonstrates how to prepare an nvlist and send it over +.Xr unix 4 +domain socket. +.Bd -literal +nvlist_t *nvl; +int fd; + +fd = open("/tmp/foo", O_RDONLY); +if (fd < 0) + err(1, "open(\\"/tmp/foo\\") failed"); + +nvl = nvlist_create(0); +/* + * There is no need to check if nvlist_create() succeeded, + * as the nvlist_add_<type>() functions can cope. + * If it failed, nvlist_send() will fail. + */ +nvlist_add_string(nvl, "filename", "/tmp/foo"); +nvlist_add_number(nvl, "flags", O_RDONLY); +/* + * We just want to send the descriptor, so we can give it + * for the nvlist to consume (that's why we use nvlist_move + * not nvlist_add). + */ +nvlist_move_descriptor(nvl, "fd", fd); +if (nvlist_send(sock, nvl) < 0) { + nvlist_destroy(nvl); + err(1, "nvlist_send() failed"); +} +nvlist_destroy(nvl); +.Ed +.Pp +Receiving nvlist and getting data: +.Bd -literal +nvlist_t *nvl; +const char *command; +char *filename; +int fd; + +nvl = nvlist_recv(sock); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +/* For command we take pointer to nvlist's buffer. */ +command = nvlist_get_string(nvl, "command"); +/* + * For filename we remove it from the nvlist and take + * ownership of the buffer. + */ +filename = nvlist_take_string(nvl, "filename"); +/* The same for the descriptor. */ +fd = nvlist_take_descriptor(nvl, "fd"); + +printf("command=%s filename=%s fd=%d\n", command, filename, fd); + +nvlist_destroy(nvl); +free(filename); +close(fd); +/* command was freed by nvlist_destroy() */ +.Ed +.Pp +Iterating over nvlist: +.Bd -literal +nvlist_t *nvl; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +cookie = NULL; +while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + printf("%s=", name); + switch (type) { + case NV_TYPE_NUMBER: + printf("%ju", (uintmax_t)nvlist_get_number(nvl, name)); + break; + case NV_TYPE_STRING: + printf("%s", nvlist_get_string(nvl, name)); + break; + default: + printf("N/A"); + break; + } + printf("\\n"); +} +.Ed +.Pp +Iterating over every nested nvlist: +.Bd -literal +nvlist_t *nvl; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +cookie = NULL; +do { + while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + if (type == NV_TYPE_NVLIST) { + nvl = nvlist_get_nvlist(nvl, name); + cookie = NULL; + } + } +} while ((nvl = nvlist_get_parent(nvl, &cookie)) != NULL); +.Ed +.Sh SEE ALSO +.Xr close 2 , +.Xr dup 2 , +.Xr open 2 , +.Xr err 3 , +.Xr free 3 , +.Xr printf 3 , +.Xr unix 4 +.Sh HISTORY +The +.Nm libnv +library appeared in +.Fx 11.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm libnv +library was implemented by +.An Pawel Jakub Dawidek Aq pawel@dawidek.net +under sponsorship from the FreeBSD Foundation. diff --git a/lib/libnv/tests/Makefile b/lib/libnv/tests/Makefile new file mode 100644 index 0000000..d8e0904 --- /dev/null +++ b/lib/libnv/tests/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +TESTSDIR= ${TESTSBASE}/lib/libnv + +ATF_TESTS_CXX= \ + dnv_tests \ + nv_tests \ + +TAP_TESTS_C+= nvlist_add_test +TAP_TESTS_C+= nvlist_exists_test +TAP_TESTS_C+= nvlist_free_test +TAP_TESTS_C+= nvlist_get_test +TAP_TESTS_C+= nvlist_move_test +TAP_TESTS_C+= nvlist_send_recv_test + +DPADD+= ${LIBNV} +LDADD+= -lnv + +WARNS?= 3 + +.include <bsd.test.mk> diff --git a/lib/libnv/tests/dnv_tests.cc b/lib/libnv/tests/dnv_tests.cc new file mode 100644 index 0000000..2f92d9d --- /dev/null +++ b/lib/libnv/tests/dnv_tests.cc @@ -0,0 +1,567 @@ +/*- + * Copyright (c) 2014-2015 Sandvine Inc. All rights reserved. + * 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 AUTHOR 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 AUTHOR 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/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <atf-c++.hpp> +#include <dnv.h> +#include <nv.h> + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_bool__present); +ATF_TEST_CASE_BODY(dnvlist_get_bool__present) +{ + nvlist_t *nvl; + const char *key; + bool value; + + nvl = nvlist_create(0); + + key = "name"; + value = true; + nvlist_add_bool(nvl, key, value); + + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, key, false), value); + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, "name", false), value); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_bool__default_value); +ATF_TEST_CASE_BODY(dnvlist_get_bool__default_value) +{ + nvlist_t *nvl; + const char *key; + + key = "123"; + nvl = nvlist_create(0); + + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, key, false), false); + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, "123", true), true); + + nvlist_add_bool(nvl, key, true); + + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, "otherkey", true), true); + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, "12c", false), false); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_number__present); +ATF_TEST_CASE_BODY(dnvlist_get_number__present) +{ + nvlist_t *nvl; + const char *key; + uint64_t value; + + nvl = nvlist_create(0); + + key = "key"; + value = 48952; + nvlist_add_number(nvl, key, value); + + ATF_REQUIRE_EQ(dnvlist_get_number(nvl, key, 19), value); + ATF_REQUIRE_EQ(dnvlist_get_number(nvl, "key", 65), value); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_number__default_value); +ATF_TEST_CASE_BODY(dnvlist_get_number__default_value) +{ + nvlist_t *nvl; + const char *key; + + key = "123"; + nvl = nvlist_create(0); + + ATF_REQUIRE_EQ(dnvlist_get_number(nvl, key, 5), 5); + ATF_REQUIRE_EQ(dnvlist_get_number(nvl, "1234", 5), 5); + + nvlist_add_number(nvl, key, 24841); + + ATF_REQUIRE_EQ(dnvlist_get_number(nvl, "1234", 5641), 5641); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_string__present); +ATF_TEST_CASE_BODY(dnvlist_get_string__present) +{ + nvlist_t *nvl; + const char *key; + const char *value, *actual_value; + + nvl = nvlist_create(0); + + key = "string"; + value = "fjdojfdi"; + nvlist_add_string(nvl, key, value); + + ATF_REQUIRE_EQ(strcmp(dnvlist_get_string(nvl, key, "g"), value), 0); + + actual_value = dnvlist_get_string(nvl, key, "rs"); + ATF_REQUIRE_EQ(strcmp(actual_value, value), 0); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_string__default_value); +ATF_TEST_CASE_BODY(dnvlist_get_string__default_value) +{ + nvlist_t *nvl; + const char *key; + const char *actual_value; + + key = "123"; + nvl = nvlist_create(0); + + ATF_REQUIRE_EQ(strcmp(dnvlist_get_string(nvl, key, "bar"), "bar"), 0); + + actual_value = dnvlist_get_string(nvl, key, "d"); + ATF_REQUIRE_EQ(strcmp(actual_value, "d"), 0); + + nvlist_add_string(nvl, key, "cxhweh"); + + ATF_REQUIRE_EQ(strcmp(dnvlist_get_string(nvl, "hthth", "fd"), "fd"), 0); + actual_value = dnvlist_get_string(nvl, "5", "5"); + ATF_REQUIRE_EQ(strcmp("5", "5"), 0); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_nvlist__present); +ATF_TEST_CASE_BODY(dnvlist_get_nvlist__present) +{ + nvlist_t *nvl; + const char *key; + nvlist_t *value; + const nvlist_t *actual_value; + + nvl = nvlist_create(0); + + key = "nvlist"; + value = nvlist_create(0); + nvlist_move_nvlist(nvl, key, value); + + actual_value = dnvlist_get_nvlist(nvl, key, NULL); + ATF_REQUIRE(actual_value != NULL); + ATF_REQUIRE(nvlist_empty(actual_value)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_nvlist__default_value); +ATF_TEST_CASE_BODY(dnvlist_get_nvlist__default_value) +{ + nvlist_t *nvl; + const char *key; + nvlist_t *dummy; + + key = "123"; + nvl = nvlist_create(0); + dummy = nvlist_create(0); + + ATF_REQUIRE_EQ(dnvlist_get_nvlist(nvl, key, dummy), dummy); + + nvlist_move_nvlist(nvl, key, nvlist_create(0)); + ATF_REQUIRE_EQ(dnvlist_get_nvlist(nvl, "456", dummy), dummy); + ATF_REQUIRE_EQ(dnvlist_get_nvlist(nvl, "gh", dummy), dummy); + + nvlist_destroy(nvl); +} + +static void +set_const_binary_value(const void *&value, size_t &size, const char *str) +{ + + value = str; + size = strlen(str) + 1; /* +1 to include '\0' */ +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_binary__present); +ATF_TEST_CASE_BODY(dnvlist_get_binary__present) +{ + nvlist_t *nvl; + const char *k; + const void *value, *actual_value; + size_t value_size, actual_size; + + nvl = nvlist_create(0); + + k = "binary"; + set_const_binary_value(value, value_size, "fjdojfdi"); + nvlist_add_binary(nvl, k, value, value_size); + + actual_value = dnvlist_get_binary(nvl, k, &actual_size, "g", 1); + ATF_REQUIRE_EQ(value_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, value, actual_size), 0); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_binary__default_value); +ATF_TEST_CASE_BODY(dnvlist_get_binary__default_value) +{ + nvlist_t *nvl; + const char *key; + const void *default_value, *actual_value; + size_t default_size, actual_size; + + key = "123"; + nvl = nvlist_create(0); + + set_const_binary_value(default_value, default_size, "bar"); + actual_value = dnvlist_get_binary(nvl, key, &actual_size, default_value, + default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, default_value, actual_size), 0); + + set_const_binary_value(default_value, default_size, "atf"); + actual_value = dnvlist_get_binary(nvl, key, &actual_size, default_value, + default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, default_value, actual_size), 0); + + nvlist_add_binary(nvl, key, "test", 4); + + set_const_binary_value(default_value, default_size, "bthrg"); + actual_value = dnvlist_get_binary(nvl, "k", &actual_size, default_value, + default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, default_value, actual_size), 0); + + set_const_binary_value(default_value, default_size, + "rrhgrythtyrtgbrhgrtdsvdfbtjlkul"); + actual_value = dnvlist_get_binary(nvl, "s", &actual_size, default_value, + default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, default_value, actual_size), 0); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_bool__present); +ATF_TEST_CASE_BODY(dnvlist_take_bool__present) +{ + nvlist_t *nvl; + const char *key; + bool value; + + nvl = nvlist_create(0); + + key = "name"; + value = true; + nvlist_add_bool(nvl, key, value); + + ATF_REQUIRE_EQ(dnvlist_take_bool(nvl, key, false), value); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_bool__empty); +ATF_TEST_CASE_BODY(dnvlist_take_bool__empty) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + + ATF_REQUIRE_EQ(dnvlist_take_bool(nvl, "123", false), false); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_bool__default_value); +ATF_TEST_CASE_BODY(dnvlist_take_bool__default_value) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + nvlist_add_bool(nvl, "key", true); + + ATF_REQUIRE_EQ(dnvlist_take_bool(nvl, "otherkey", true), true); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_number__present); +ATF_TEST_CASE_BODY(dnvlist_take_number__present) +{ + nvlist_t *nvl; + const char *key; + uint64_t value; + + nvl = nvlist_create(0); + + key = "name"; + value = 194154; + nvlist_add_number(nvl, key, value); + + ATF_REQUIRE_EQ(dnvlist_take_number(nvl, key, 2), value); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_number__empty); +ATF_TEST_CASE_BODY(dnvlist_take_number__empty) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + + ATF_REQUIRE_EQ(dnvlist_take_number(nvl, "123", 126484), 126484); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_number__default_value); +ATF_TEST_CASE_BODY(dnvlist_take_number__default_value) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + nvlist_add_number(nvl, "key", 12); + + ATF_REQUIRE_EQ(dnvlist_take_number(nvl, "otherkey", 13), 13); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_string__present); +ATF_TEST_CASE_BODY(dnvlist_take_string__present) +{ + nvlist_t *nvl; + const char *key; + const char *value; + char *default_val, *actual_val; + + nvl = nvlist_create(0); + + key = "name"; + value = "wrowm"; + default_val = strdup("default"); + nvlist_add_string(nvl, key, value); + + actual_val = dnvlist_take_string(nvl, key, default_val); + ATF_REQUIRE_EQ(strcmp(actual_val, value), 0); + ATF_REQUIRE(nvlist_empty(nvl)); + + free(actual_val); + free(default_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_string__empty); +ATF_TEST_CASE_BODY(dnvlist_take_string__empty) +{ + nvlist_t *nvl; + char *default_val, *actual_val; + + nvl = nvlist_create(0); + default_val = strdup(""); + + actual_val = dnvlist_take_string(nvl, "123", default_val); + ATF_REQUIRE_EQ(strcmp(actual_val, default_val), 0); + + free(actual_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_string__default_value); +ATF_TEST_CASE_BODY(dnvlist_take_string__default_value) +{ + nvlist_t *nvl; + char *default_val, *actual_val; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "key", "foobar"); + default_val = strdup("other"); + + actual_val = dnvlist_take_string(nvl, "otherkey", default_val); + ATF_REQUIRE_EQ(strcmp(actual_val, default_val), 0); + + free(actual_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_nvlist__present); +ATF_TEST_CASE_BODY(dnvlist_take_nvlist__present) +{ + nvlist_t *nvl; + const char *key; + nvlist_t *value, *default_val, *actual_val; + + nvl = nvlist_create(0); + + key = "name"; + value = nvlist_create(0); + default_val = nvlist_create(0); + nvlist_move_nvlist(nvl, key, value); + + actual_val = dnvlist_take_nvlist(nvl, key, default_val); + ATF_REQUIRE_EQ(actual_val, value); + ATF_REQUIRE(nvlist_empty(nvl)); + + free(actual_val); + free(default_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_nvlist__empty); +ATF_TEST_CASE_BODY(dnvlist_take_nvlist__empty) +{ + nvlist_t *nvl, *actual_val; + + nvl = nvlist_create(0); + + actual_val = dnvlist_take_nvlist(nvl, "123", NULL); + ATF_REQUIRE_EQ(actual_val, static_cast<nvlist_t *>(NULL)); + + free(actual_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_nvlist__default_value); +ATF_TEST_CASE_BODY(dnvlist_take_nvlist__default_value) +{ + nvlist_t *nvl; + nvlist_t *default_val, *actual_val; + + nvl = nvlist_create(0); + nvlist_move_nvlist(nvl, "key", nvlist_create(0)); + default_val = nvlist_create(0); + + actual_val = dnvlist_take_nvlist(nvl, "otherkey", default_val); + ATF_REQUIRE_EQ(actual_val, default_val); + + free(actual_val); + nvlist_destroy(nvl); +} + +static void +set_binary_value(void *&value, size_t &size, const char *str) +{ + + value = strdup(str); + size = strlen(str) + 1; /* +1 to include '\0' */ +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_binary__present); +ATF_TEST_CASE_BODY(dnvlist_take_binary__present) +{ + nvlist_t *nvl; + const char *key; + void *value, *default_val, *actual_val; + size_t value_size, default_size, actual_size; + + nvl = nvlist_create(0); + + key = "name"; + set_binary_value(value, value_size, "fkdojvmo908"); + set_binary_value(default_val, default_size, "16546"); + nvlist_add_binary(nvl, key, value, value_size); + + actual_val = dnvlist_take_binary(nvl, key, &actual_size, default_val, + default_size); + ATF_REQUIRE_EQ(value_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_val, value, value_size), 0); + ATF_REQUIRE(nvlist_empty(nvl)); + + free(actual_val); + free(default_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_binary__empty); +ATF_TEST_CASE_BODY(dnvlist_take_binary__empty) +{ + nvlist_t *nvl; + void *default_val, *actual_val; + size_t default_size, actual_size; + + nvl = nvlist_create(0); + set_binary_value(default_val, default_size, "\xa8\x89\x49\xff\xe2\x08"); + + actual_val = dnvlist_take_binary(nvl, "123", &actual_size, default_val, + default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_val, default_val, actual_size), 0); + + free(actual_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_binary__default_value); +ATF_TEST_CASE_BODY(dnvlist_take_binary__default_value) +{ + nvlist_t *nvl; + void *default_val, *actual_val; + size_t default_size, actual_size; + + nvl = nvlist_create(0); + nvlist_add_binary(nvl, "key", "foobar", 6); + set_binary_value(default_val, default_size, "vbhag"); + + actual_val = dnvlist_take_binary(nvl, "otherkey", &actual_size, + default_val, default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_val, default_val, default_size), 0); + + free(actual_val); + nvlist_destroy(nvl); +} + +ATF_INIT_TEST_CASES(tp) +{ + ATF_ADD_TEST_CASE(tp, dnvlist_get_bool__present); + ATF_ADD_TEST_CASE(tp, dnvlist_get_bool__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_get_number__present); + ATF_ADD_TEST_CASE(tp, dnvlist_get_number__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_get_string__present); + ATF_ADD_TEST_CASE(tp, dnvlist_get_string__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_get_nvlist__present); + ATF_ADD_TEST_CASE(tp, dnvlist_get_nvlist__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_get_binary__present); + ATF_ADD_TEST_CASE(tp, dnvlist_get_binary__default_value); + + ATF_ADD_TEST_CASE(tp, dnvlist_take_bool__present); + ATF_ADD_TEST_CASE(tp, dnvlist_take_bool__empty); + ATF_ADD_TEST_CASE(tp, dnvlist_take_bool__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_take_number__present); + ATF_ADD_TEST_CASE(tp, dnvlist_take_number__empty); + ATF_ADD_TEST_CASE(tp, dnvlist_take_number__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_take_string__present); + ATF_ADD_TEST_CASE(tp, dnvlist_take_string__empty); + ATF_ADD_TEST_CASE(tp, dnvlist_take_string__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_take_nvlist__present); + ATF_ADD_TEST_CASE(tp, dnvlist_take_nvlist__empty); + ATF_ADD_TEST_CASE(tp, dnvlist_take_nvlist__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_take_binary__present); + ATF_ADD_TEST_CASE(tp, dnvlist_take_binary__empty); + ATF_ADD_TEST_CASE(tp, dnvlist_take_binary__default_value); +} diff --git a/lib/libnv/tests/nv_tests.cc b/lib/libnv/tests/nv_tests.cc new file mode 100644 index 0000000..2d9fd97 --- /dev/null +++ b/lib/libnv/tests/nv_tests.cc @@ -0,0 +1,1240 @@ +/*- + * Copyright (c) 2014-2015 Sandvine Inc. All rights reserved. + * 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 AUTHOR 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 AUTHOR 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/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <atf-c++.hpp> +#include <nv.h> + +#include <errno.h> +#include <limits> +#include <set> +#include <sstream> +#include <string> + +/* + * Test that a newly created nvlist has no errors, and is empty. + */ +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_create__is_empty); +ATF_TEST_CASE_BODY(nvlist_create__is_empty) +{ + nvlist_t *nvl; + int type; + void *it; + + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(nvlist_empty(nvl)); + + it = NULL; + ATF_REQUIRE_EQ(nvlist_next(nvl, &type, &it), static_cast<const char *>(NULL)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_null__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_null__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key; + int type; + + key = "key"; + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_null(nvl, key); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists_null(nvl, key)); + ATF_REQUIRE(nvlist_exists_null(nvl, "key")); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_NULL); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast<const char *>(NULL)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_bool__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_bool__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key; + int type; + + key = "name"; + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_bool(nvl, key, true); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists(nvl, "name")); + ATF_REQUIRE(nvlist_exists_bool(nvl, key)); + ATF_REQUIRE(nvlist_exists_bool(nvl, "name")); + ATF_REQUIRE_EQ(nvlist_get_bool(nvl, key), true); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_BOOL); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast<const char *>(NULL)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_number__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_number__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key; + uint64_t value; + int type; + + key = "foo123"; + value = 71965; + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_number(nvl, key, value); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists(nvl, "foo123")); + ATF_REQUIRE(nvlist_exists_number(nvl, key)); + ATF_REQUIRE_EQ(nvlist_get_number(nvl, key), value); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_NUMBER); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast<const char *>(NULL)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_string__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_string__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key; + const char *value; + int type; + + key = "test"; + value = "fgjdkgjdk"; + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_string(nvl, key, value); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists(nvl, "test")); + ATF_REQUIRE(nvlist_exists_string(nvl, key)); + ATF_REQUIRE(nvlist_exists_string(nvl, "test")); + ATF_REQUIRE_EQ(strcmp(nvlist_get_string(nvl, key), value), 0); + + /* nvlist_add_* is required to clone the value, so check for that. */ + ATF_REQUIRE(nvlist_get_string(nvl, key) != value); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_STRING); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast<const char *>(NULL)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_nvlist__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_nvlist__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key, *subkey; + nvlist_t *sublist; + const nvlist_t *value; + int type; + + key = "test"; + subkey = "subkey"; + sublist = nvlist_create(0); + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_null(sublist, subkey); + nvlist_add_nvlist(nvl, key, sublist); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists(nvl, "test")); + ATF_REQUIRE(nvlist_exists_nvlist(nvl, key)); + ATF_REQUIRE(nvlist_exists_nvlist(nvl, "test")); + + value = nvlist_get_nvlist(nvl, key); + ATF_REQUIRE(nvlist_exists_null(value, subkey)); + + /* nvlist_add_* is required to clone the value, so check for that. */ + ATF_REQUIRE(sublist != value); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_NVLIST); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast<const char *>(NULL)); + + nvlist_destroy(sublist); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_nvlist__child_with_error); +ATF_TEST_CASE_BODY(nvlist_add_nvlist__child_with_error) +{ + nvlist_t *nvl, *parent; + + nvl = nvlist_create(0); + parent = nvlist_create(0); + + nvlist_set_error(nvl, EBADF); + nvlist_add_nvlist(parent, "test", nvl); + ATF_REQUIRE_EQ(nvlist_error(parent), EBADF); + + nvlist_destroy(nvl); + nvlist_destroy(parent); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_binary__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_binary__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key; + void *value; + const void *ret_value; + size_t value_size, ret_size; + int type; + + key = "binary"; + value_size = 13; + value = malloc(value_size); + memset(value, 0xa5, value_size); + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_binary(nvl, key, value, value_size); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists(nvl, "binary")); + ATF_REQUIRE(nvlist_exists_binary(nvl, key)); + ATF_REQUIRE(nvlist_exists_binary(nvl, "binary")); + + ret_value = nvlist_get_binary(nvl, key, &ret_size); + ATF_REQUIRE_EQ(value_size, ret_size); + ATF_REQUIRE_EQ(memcmp(value, ret_value, ret_size), 0); + + /* nvlist_add_* is required to clone the value, so check for that. */ + ATF_REQUIRE(value != ret_value); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_BINARY); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast<const char *>(NULL)); + + nvlist_destroy(nvl); + free(value); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_clone__empty_nvlist); +ATF_TEST_CASE_BODY(nvlist_clone__empty_nvlist) +{ + nvlist_t *nvl, *clone; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + clone = nvlist_clone(nvl); + ATF_REQUIRE(clone != NULL); + ATF_REQUIRE(clone != nvl); + ATF_REQUIRE(nvlist_empty(clone)); + + nvlist_destroy(clone); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_clone__nonempty_nvlist); +ATF_TEST_CASE_BODY(nvlist_clone__nonempty_nvlist) +{ + nvlist_t *nvl, *clone; + const char *key; + void *it; + uint64_t value; + int type; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + key = "testkey"; + value = 684874; + nvlist_add_number(nvl, key, value); + + clone = nvlist_clone(nvl); + ATF_REQUIRE(clone != NULL); + ATF_REQUIRE(clone != nvl); + ATF_REQUIRE(nvlist_exists_number(clone, key)); + ATF_REQUIRE_EQ(nvlist_get_number(clone, key), value); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(clone, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_NUMBER); + ATF_REQUIRE_EQ(nvlist_next(clone, &type, &it), static_cast<const char *>(NULL)); + + nvlist_destroy(clone); + nvlist_destroy(nvl); +} + +static const char * const test_subnvlist_key = "nvlist"; + +static const char * const test_string_key = "string"; +static const char * const test_string_val = "59525"; + +static nvlist_t* +create_test_nvlist(void) +{ + nvlist_t *nvl, *sublist; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + sublist = nvlist_create(0); + ATF_REQUIRE(sublist != NULL); + + nvlist_add_string(sublist, test_string_key, test_string_val); + nvlist_move_nvlist(nvl, test_subnvlist_key, sublist); + + return (nvl); +} + +static void +verify_test_nvlist(const nvlist_t *nvl) +{ + void *it; + const nvlist_t *value; + int type; + + ATF_REQUIRE(nvlist_exists_nvlist(nvl, test_subnvlist_key)); + + value = nvlist_get_nvlist(nvl, test_subnvlist_key); + + ATF_REQUIRE(nvlist_exists_string(value, test_string_key)); + ATF_REQUIRE_EQ(strcmp(nvlist_get_string(value, test_string_key), test_string_val), 0); + ATF_REQUIRE(nvlist_get_string(value, test_string_key) != test_string_val); + + /* Iterate over both nvlists; ensure that each has only the one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(value, &type, &it), + test_string_key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_STRING); + ATF_REQUIRE_EQ(nvlist_next(value, &type, &it), static_cast<const char *>(NULL)); + + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), + test_subnvlist_key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_NVLIST); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type, &it), static_cast<const char *>(NULL)); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_clone__nested_nvlist); +ATF_TEST_CASE_BODY(nvlist_clone__nested_nvlist) +{ + nvlist_t *nvl, *clone; + + nvl = create_test_nvlist(); + clone = nvlist_clone(nvl); + + ATF_REQUIRE(clone != NULL); + ATF_REQUIRE(clone != nvl); + verify_test_nvlist(clone); + + nvlist_destroy(clone); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_clone__error_nvlist); +ATF_TEST_CASE_BODY(nvlist_clone__error_nvlist) +{ + nvlist_t *nvl, *clone; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + nvlist_set_error(nvl, ENOMEM); + + clone = nvlist_clone(nvl); + ATF_REQUIRE(clone == NULL); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_pack__empty_nvlist); +ATF_TEST_CASE_BODY(nvlist_pack__empty_nvlist) +{ + nvlist_t *nvl, *unpacked; + void *packed; + size_t packed_size; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE(unpacked != nvl); + ATF_REQUIRE(nvlist_empty(unpacked)); + + nvlist_destroy(unpacked); + nvlist_destroy(nvl); + free(packed); +} + +static void +verify_null(const nvlist_t *nvl, int type) +{ + + ATF_REQUIRE_EQ(type, NV_TYPE_NULL); +} + +static void +verify_number(const nvlist_t *nvl, const char *name, int type, uint64_t value) +{ + + ATF_REQUIRE_EQ(type, NV_TYPE_NUMBER); + ATF_REQUIRE_EQ(nvlist_get_number(nvl, name), value); +} + +static void +verify_string(const nvlist_t *nvl, const char *name, int type, + const char * value) +{ + + ATF_REQUIRE_EQ(type, NV_TYPE_STRING); + ATF_REQUIRE_EQ(strcmp(nvlist_get_string(nvl, name), value), 0); +} + +static void +verify_nvlist(const nvlist_t *nvl, const char *name, int type) +{ + + ATF_REQUIRE_EQ(type, NV_TYPE_NVLIST); + verify_test_nvlist(nvlist_get_nvlist(nvl, name)); +} + +static void +verify_binary(const nvlist_t *nvl, const char *name, int type, + const void * value, size_t size) +{ + const void *actual_value; + size_t actual_size; + + ATF_REQUIRE_EQ(type, NV_TYPE_BINARY); + actual_value = nvlist_get_binary(nvl, name, &actual_size); + ATF_REQUIRE_EQ(size, actual_size); + ATF_REQUIRE_EQ(memcmp(value, actual_value, size), 0); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_pack__multiple_values); +ATF_TEST_CASE_BODY(nvlist_pack__multiple_values) +{ + std::ostringstream msg; + std::set<std::string> keys_seen; + nvlist_t *nvl, *unpacked, *nvvalue; + const char *nullkey, *numkey, *strkey, *nvkey, *binkey, *name; + int numvalue; + const char * strvalue; + void *binvalue, *packed, *it; + size_t binsize, packed_size; + int type; + + nvl = nvlist_create(0); + + nullkey = "null"; + nvlist_add_null(nvl, nullkey); + + numkey = "number"; + numvalue = 939853984; + nvlist_add_number(nvl, numkey, numvalue); + + strkey = "string"; + strvalue = "jfieutijf"; + nvlist_add_string(nvl, strkey, strvalue); + + nvkey = "nvlist"; + nvvalue = create_test_nvlist(); + nvlist_move_nvlist(nvl, nvkey, nvvalue); + + binkey = "binary"; + binsize = 4; + binvalue = malloc(binsize); + memset(binvalue, 'b', binsize); + nvlist_move_binary(nvl, binkey, binvalue, binsize); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size); + ATF_REQUIRE(unpacked != 0); + + it = NULL; + while ((name = nvlist_next(unpacked, &type, &it)) != NULL) { + /* Ensure that we see every key only once. */ + ATF_REQUIRE_EQ(keys_seen.count(name), 0); + + if (strcmp(name, nullkey) == 0) + verify_null(unpacked, type); + else if (strcmp(name, numkey) == 0) + verify_number(unpacked, name, type, numvalue); + else if (strcmp(name, strkey) == 0) + verify_string(unpacked, name, type, strvalue); + else if (strcmp(name, nvkey) == 0) + verify_nvlist(unpacked, name, type); + else if (strcmp(name, binkey) == 0) + verify_binary(unpacked, name, type, binvalue, binsize); + else { + msg << "Unexpected key :'" << name << "'"; + ATF_FAIL(msg.str().c_str()); + } + + keys_seen.insert(name); + } + + /* Ensure that we saw every key. */ + ATF_REQUIRE_EQ(keys_seen.size(), 5); + + nvlist_destroy(nvl); + nvlist_destroy(unpacked); + free(packed); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_pack__error_nvlist); +ATF_TEST_CASE_BODY(nvlist_pack__error_nvlist) +{ + nvlist_t *nvl; + void *packed; + size_t size; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + nvlist_set_error(nvl, ENOMEM); + + packed = nvlist_pack(nvl, &size); + ATF_REQUIRE(packed == NULL); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_unpack__duplicate_key); +ATF_TEST_CASE_BODY(nvlist_unpack__duplicate_key) +{ + nvlist_t *nvl, *unpacked; + const char *key1, *key2; + void *packed, *keypos; + size_t size, keylen; + + nvl = nvlist_create(0); + + key1 = "key1"; + keylen = strlen(key1); + nvlist_add_number(nvl, key1, 5); + + key2 = "key2"; + ATF_REQUIRE_EQ(keylen, strlen(key2)); + nvlist_add_number(nvl, key2, 10); + + packed = nvlist_pack(nvl, &size); + + /* + * Mangle the packed nvlist by replacing key1 with key2, creating a + * packed nvlist with a duplicate key. + */ + keypos = memmem(packed, size, key1, keylen); + ATF_REQUIRE(keypos != NULL); + memcpy(keypos, key2, keylen); + + unpacked = nvlist_unpack(packed, size); + ATF_REQUIRE(nvlist_error(unpacked) != 0); + + free(packed); + nvlist_destroy(nvl); + nvlist_destroy(unpacked); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_move_string__single_insert); +ATF_TEST_CASE_BODY(nvlist_move_string__single_insert) +{ + nvlist_t *nvl; + const char *key; + char *value; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + key = "testkey"; + value = strdup("testval"); + ATF_REQUIRE(value != NULL); + + nvlist_move_string(nvl, key, value); + ATF_REQUIRE_EQ(nvlist_get_string(nvl, key), value); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_move_nvlist__null_child); +ATF_TEST_CASE_BODY(nvlist_move_nvlist__null_child) +{ + nvlist_t *parent; + + parent = nvlist_create(0); + + nvlist_move_nvlist(parent, "test", NULL); + + ATF_REQUIRE(nvlist_error(parent) != 0); + + nvlist_destroy(parent); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_move_nvlist__child_with_error); +ATF_TEST_CASE_BODY(nvlist_move_nvlist__child_with_error) +{ + nvlist_t *nvl, *parent; + + nvl = nvlist_create(0); + parent = nvlist_create(0); + + nvlist_set_error(nvl, EBADF); + nvlist_move_nvlist(parent, "test", nvl); + ATF_REQUIRE_EQ(nvlist_error(parent), EBADF); + + nvlist_destroy(parent); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_move_nvlist__single_insert); +ATF_TEST_CASE_BODY(nvlist_move_nvlist__single_insert) +{ + nvlist_t *nvl; + const char *key; + nvlist_t *value; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + key = "testkey"; + value = nvlist_create(0); + ATF_REQUIRE(value != NULL); + + nvlist_move_nvlist(nvl, key, value); + ATF_REQUIRE_EQ(nvlist_get_nvlist(nvl, key), value); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_move_binary__single_insert); +ATF_TEST_CASE_BODY(nvlist_move_binary__single_insert) +{ + nvlist_t *nvl; + const char *key; + void *value; + size_t size, actual_size; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + key = "testkey"; + size = 73; + value = malloc(size); + ATF_REQUIRE(value != NULL); + + nvlist_move_binary(nvl, key, value, size); + ATF_REQUIRE_EQ(nvlist_get_binary(nvl, key, &actual_size), value); + ATF_REQUIRE_EQ(size, actual_size); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_bool__single_remove); +ATF_TEST_CASE_BODY(nvlist_take_bool__single_remove) +{ + nvlist_t *nvl; + const char *testkey; + bool testval; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "boolkey"; + testval = false; + nvlist_add_bool(nvl, testkey, testval); + + ATF_REQUIRE_EQ(nvlist_take_bool(nvl, testkey), testval); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_bool__other_keys_unchanged); +ATF_TEST_CASE_BODY(nvlist_take_bool__other_keys_unchanged) +{ + nvlist_t *nvl; + const char *testkey, *otherkey1, *otherkey2; + bool testval, otherval1; + nvlist_t *otherval2; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "boolkey"; + testval = true; + nvlist_add_bool(nvl, testkey, testval); + + otherkey1 = "key1"; + otherval1 = false; + nvlist_add_bool(nvl, otherkey1, otherval1); + + otherkey2 = "key2"; + otherval2 = create_test_nvlist(); + nvlist_move_nvlist(nvl, otherkey2, otherval2); + + ATF_REQUIRE_EQ(nvlist_take_bool(nvl, testkey), testval); + + ATF_REQUIRE(nvlist_exists_bool(nvl, otherkey1)); + ATF_REQUIRE_EQ(nvlist_get_bool(nvl, otherkey1), otherval1); + + ATF_REQUIRE(nvlist_exists_nvlist(nvl, otherkey2)); + verify_test_nvlist(nvlist_get_nvlist(nvl, otherkey2)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_number__single_remove); +ATF_TEST_CASE_BODY(nvlist_take_number__single_remove) +{ + nvlist_t *nvl; + const char *testkey; + uint64_t testval; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "numkey"; + testval = std::numeric_limits<uint64_t>::max(); + nvlist_add_number(nvl, testkey, testval); + + ATF_REQUIRE_EQ(nvlist_take_number(nvl, testkey), testval); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_number__other_keys_unchanged); +ATF_TEST_CASE_BODY(nvlist_take_number__other_keys_unchanged) +{ + nvlist_t *nvl; + const char *testkey, *otherkey1, *otherkey2; + uint64_t testval, otherval1; + const char *otherval2; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + otherkey1 = "key1"; + otherval1 = 5; + nvlist_add_number(nvl, otherkey1, otherval1); + + testkey = "numkey"; + testval = 1654; + nvlist_add_number(nvl, testkey, testval); + + otherkey2 = "key2"; + otherval2 = "string"; + nvlist_add_string(nvl, otherkey2, otherval2); + + ATF_REQUIRE_EQ(nvlist_take_number(nvl, testkey), testval); + + ATF_REQUIRE(nvlist_exists_number(nvl, otherkey1)); + ATF_REQUIRE_EQ(nvlist_get_number(nvl, otherkey1), otherval1); + + ATF_REQUIRE(nvlist_exists_string(nvl, otherkey2)); + ATF_REQUIRE_EQ(strcmp(nvlist_get_string(nvl, otherkey2), otherval2), 0); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_string__single_remove); +ATF_TEST_CASE_BODY(nvlist_take_string__single_remove) +{ + nvlist_t *nvl; + const char *testkey; + const char *testval; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "numkey"; + testval = "nvlist"; + nvlist_add_string(nvl, testkey, testval); + + ATF_REQUIRE_EQ(strcmp(nvlist_take_string(nvl, testkey), testval), 0); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_string__other_keys_unchanged); +ATF_TEST_CASE_BODY(nvlist_take_string__other_keys_unchanged) +{ + nvlist_t *nvl; + const char *testkey, *otherkey1, *otherkey2; + const char *testval, *otherval1; + bool otherval2; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + otherkey1 = "key1"; + otherval1 = "fjdifjdk"; + nvlist_add_string(nvl, otherkey1, otherval1); + + otherkey2 = "key2"; + otherval2 = true; + nvlist_add_bool(nvl, otherkey2, otherval2); + + testkey = "strkey"; + testval = "1654"; + nvlist_add_string(nvl, testkey, testval); + + ATF_REQUIRE_EQ(strcmp(nvlist_take_string(nvl, testkey), testval), 0); + + ATF_REQUIRE(nvlist_exists_string(nvl, otherkey1)); + ATF_REQUIRE_EQ(strcmp(nvlist_get_string(nvl, otherkey1), otherval1), 0); + + ATF_REQUIRE(nvlist_exists_bool(nvl, otherkey2)); + ATF_REQUIRE_EQ(nvlist_get_bool(nvl, otherkey2), otherval2); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_nvlist__single_remove); +ATF_TEST_CASE_BODY(nvlist_take_nvlist__single_remove) +{ + nvlist_t *nvl; + const char *testkey; + nvlist_t *testval; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "numkey"; + testval = create_test_nvlist(); + nvlist_move_nvlist(nvl, testkey, testval); + + verify_test_nvlist(nvlist_take_nvlist(nvl, testkey)); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_nvlist__other_keys_unchanged); +ATF_TEST_CASE_BODY(nvlist_take_nvlist__other_keys_unchanged) +{ + nvlist_t *nvl; + const char *testkey, *otherkey1, *otherkey2; + nvlist_t *testval, *otherval1; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "strkey"; + testval = create_test_nvlist(); + nvlist_move_nvlist(nvl, testkey, testval); + + otherkey1 = "key1"; + otherval1 = nvlist_create(0); + nvlist_move_nvlist(nvl, otherkey1, otherval1); + + otherkey2 = "key2"; + nvlist_add_null(nvl, otherkey2); + + verify_test_nvlist(nvlist_take_nvlist(nvl, testkey)); + + ATF_REQUIRE(nvlist_exists_nvlist(nvl, otherkey1)); + ATF_REQUIRE(nvlist_empty(nvlist_get_nvlist(nvl, otherkey1))); + + ATF_REQUIRE(nvlist_exists_null(nvl, otherkey2)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_binary__single_remove); +ATF_TEST_CASE_BODY(nvlist_take_binary__single_remove) +{ + nvlist_t *nvl; + const char *testkey; + void *testval; + const void *actual_val; + size_t testsize, actual_size; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "numkey"; + testsize = 457; + testval = malloc(testsize); + memset(testval, '5', testsize); + nvlist_move_binary(nvl, testkey, testval, testsize); + + actual_val = nvlist_take_binary(nvl, testkey, &actual_size); + ATF_REQUIRE_EQ(testsize, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_val, testval, testsize), 0); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_binary__other_keys_unchanged); +ATF_TEST_CASE_BODY(nvlist_take_binary__other_keys_unchanged) +{ + nvlist_t *nvl; + const char *testkey, *otherkey1, *otherkey2; + const void *actual_value; + char testval[] = "gjiertj"; + char otherval1[] = "fdreg"; + size_t testsize, othersize, actual_size; + bool otherval2; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + otherkey1 = "key1"; + othersize = sizeof(otherval1); + nvlist_add_binary(nvl, otherkey1, otherval1, othersize); + + otherkey2 = "key2"; + otherval2 = true; + nvlist_add_bool(nvl, otherkey2, otherval2); + + testkey = "strkey"; + testsize = sizeof(testval); + nvlist_add_binary(nvl, testkey, testval, testsize); + + actual_value = nvlist_take_binary(nvl, testkey, &actual_size); + ATF_REQUIRE_EQ(testsize, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, testval, testsize), 0); + + ATF_REQUIRE(nvlist_exists_binary(nvl, otherkey1)); + actual_value = nvlist_get_binary(nvl, otherkey1, &actual_size); + ATF_REQUIRE_EQ(othersize, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, otherval1, othersize), 0); + + ATF_REQUIRE(nvlist_exists_bool(nvl, otherkey2)); + ATF_REQUIRE_EQ(nvlist_get_bool(nvl, otherkey2), otherval2); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_null); +ATF_TEST_CASE_BODY(nvlist_free__single_null) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_null(nvl, key); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_bool); +ATF_TEST_CASE_BODY(nvlist_free__single_bool) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_bool(nvl, key, true); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_number); +ATF_TEST_CASE_BODY(nvlist_free__single_number) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_number(nvl, key, 584); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_string); +ATF_TEST_CASE_BODY(nvlist_free__single_string) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_string(nvl, key, "gjkfkjd"); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_nvlist); +ATF_TEST_CASE_BODY(nvlist_free__single_nvlist) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_nvlist(nvl, key, nvlist_create(0)); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_binary); +ATF_TEST_CASE_BODY(nvlist_free__single_binary) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_binary(nvl, key, "jgjgfd", 6); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_null__single_null); +ATF_TEST_CASE_BODY(nvlist_free_null__single_null) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_null(nvl, key); + + nvlist_free_null(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_bool__single_bool); +ATF_TEST_CASE_BODY(nvlist_free_bool__single_bool) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_bool(nvl, key, true); + + nvlist_free_bool(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_number__single_number); +ATF_TEST_CASE_BODY(nvlist_free_number__single_number) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_number(nvl, key, 584); + + nvlist_free_number(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_string__single_string); +ATF_TEST_CASE_BODY(nvlist_free_string__single_string) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_string(nvl, key, "gjkfkjd"); + + nvlist_free_string(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_nvlist__single_nvlist); +ATF_TEST_CASE_BODY(nvlist_free_nvlist__single_nvlist) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_nvlist(nvl, key, nvlist_create(0)); + + nvlist_free_nvlist(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_binary__single_binary); +ATF_TEST_CASE_BODY(nvlist_free_binary__single_binary) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_binary(nvl, key, "jgjgfd", 6); + + nvlist_free_binary(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_INIT_TEST_CASES(tp) +{ + ATF_ADD_TEST_CASE(tp, nvlist_create__is_empty); + ATF_ADD_TEST_CASE(tp, nvlist_add_null__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_add_bool__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_add_number__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_add_string__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_add_nvlist__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_add_nvlist__child_with_error); + ATF_ADD_TEST_CASE(tp, nvlist_add_binary__single_insert); + + ATF_ADD_TEST_CASE(tp, nvlist_clone__empty_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_clone__nonempty_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_clone__nested_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_clone__error_nvlist); + + ATF_ADD_TEST_CASE(tp, nvlist_pack__empty_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_pack__multiple_values); + ATF_ADD_TEST_CASE(tp, nvlist_pack__error_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_unpack__duplicate_key); + + ATF_ADD_TEST_CASE(tp, nvlist_move_string__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_move_nvlist__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_move_nvlist__null_child); + ATF_ADD_TEST_CASE(tp, nvlist_move_nvlist__child_with_error); + ATF_ADD_TEST_CASE(tp, nvlist_move_binary__single_insert); + + ATF_ADD_TEST_CASE(tp, nvlist_take_bool__single_remove); + ATF_ADD_TEST_CASE(tp, nvlist_take_bool__other_keys_unchanged); + ATF_ADD_TEST_CASE(tp, nvlist_take_number__single_remove); + ATF_ADD_TEST_CASE(tp, nvlist_take_number__other_keys_unchanged); + ATF_ADD_TEST_CASE(tp, nvlist_take_string__single_remove); + ATF_ADD_TEST_CASE(tp, nvlist_take_string__other_keys_unchanged); + ATF_ADD_TEST_CASE(tp, nvlist_take_nvlist__single_remove); + ATF_ADD_TEST_CASE(tp, nvlist_take_nvlist__other_keys_unchanged); + ATF_ADD_TEST_CASE(tp, nvlist_take_binary__single_remove); + ATF_ADD_TEST_CASE(tp, nvlist_take_binary__other_keys_unchanged); + + ATF_ADD_TEST_CASE(tp, nvlist_free__single_null); + ATF_ADD_TEST_CASE(tp, nvlist_free__single_bool); + ATF_ADD_TEST_CASE(tp, nvlist_free__single_number); + ATF_ADD_TEST_CASE(tp, nvlist_free__single_string); + ATF_ADD_TEST_CASE(tp, nvlist_free__single_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_free__single_binary); + + ATF_ADD_TEST_CASE(tp, nvlist_free_null__single_null); + ATF_ADD_TEST_CASE(tp, nvlist_free_bool__single_bool); + ATF_ADD_TEST_CASE(tp, nvlist_free_number__single_number); + ATF_ADD_TEST_CASE(tp, nvlist_free_string__single_string); + ATF_ADD_TEST_CASE(tp, nvlist_free_nvlist__single_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_free_binary__single_binary); +} diff --git a/lib/libnv/tests/nvlist_add_test.c b/lib/libnv/tests/nvlist_add_test.c new file mode 100644 index 0000000..06bcc63 --- /dev/null +++ b/lib/libnv/tests/nvlist_add_test.c @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +#include <nv.h> + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +int +main(void) +{ + const nvlist_t *cnvl; + nvlist_t *nvl; + + printf("1..94\n"); + + nvl = nvlist_create(0); + + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + nvlist_add_null(nvl, "nvlist/null"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/true")); + nvlist_add_bool(nvl, "nvlist/bool/true", true); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool/true")); + + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/false")); + nvlist_add_bool(nvl, "nvlist/bool/false", false); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool/false")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/0")); + nvlist_add_number(nvl, "nvlist/number/0", 0); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/0")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/1")); + nvlist_add_number(nvl, "nvlist/number/1", 1); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/1")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/-1")); + nvlist_add_number(nvl, "nvlist/number/-1", -1); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/-1")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); + nvlist_add_number(nvl, "nvlist/number/UINT64_MAX", UINT64_MAX); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); + nvlist_add_number(nvl, "nvlist/number/INT64_MIN", INT64_MIN); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); + nvlist_add_number(nvl, "nvlist/number/INT64_MAX", INT64_MAX); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/")); + nvlist_add_string(nvl, "nvlist/string/", ""); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/string/")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/x")); + nvlist_add_string(nvl, "nvlist/string/x", "x"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + nvlist_add_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/stringf/")); + nvlist_add_stringf(nvl, "nvlist/stringf/", "%s", ""); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/stringf/x")); + nvlist_add_stringf(nvl, "nvlist/stringf/x", "%s", "x"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/x")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/stringf/666Xabc")); + nvlist_add_stringf(nvl, "nvlist/stringf/666Xabc", "%d%c%s", 666, 'X', "abc"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/666Xabc")); + + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + nvlist_add_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", STDERR_FILENO); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/x")); + nvlist_add_binary(nvl, "nvlist/binary/x", "x", 1); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); + + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + nvlist_add_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool/true")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool/false")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/0")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/1")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/-1")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); + CHECK(nvlist_exists_string(nvl, "nvlist/string/")); + CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); + CHECK(nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/")); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/x")); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/666Xabc")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + + cnvl = nvlist_get_nvlist(nvl, "nvlist/nvlist"); + CHECK(nvlist_exists_null(cnvl, "nvlist/null")); + CHECK(nvlist_exists_bool(cnvl, "nvlist/bool/true")); + CHECK(nvlist_exists_bool(cnvl, "nvlist/bool/false")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/0")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/1")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/-1")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/UINT64_MAX")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/INT64_MIN")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/INT64_MAX")); + CHECK(nvlist_exists_string(cnvl, "nvlist/string/")); + CHECK(nvlist_exists_string(cnvl, "nvlist/string/x")); + CHECK(nvlist_exists_string(cnvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_string(cnvl, "nvlist/stringf/")); + CHECK(nvlist_exists_string(cnvl, "nvlist/stringf/x")); + CHECK(nvlist_exists_string(cnvl, "nvlist/stringf/666Xabc")); + CHECK(nvlist_exists_descriptor(cnvl, "nvlist/descriptor/STDERR_FILENO")); + CHECK(nvlist_exists_binary(cnvl, "nvlist/binary/x")); + CHECK(nvlist_exists_binary(cnvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + + nvlist_destroy(nvl); + + return (0); +} diff --git a/lib/libnv/tests/nvlist_exists_test.c b/lib/libnv/tests/nvlist_exists_test.c new file mode 100644 index 0000000..cb595d7 --- /dev/null +++ b/lib/libnv/tests/nvlist_exists_test.c @@ -0,0 +1,321 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#include <stdio.h> +#include <unistd.h> + +#include <nv.h> + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +int +main(void) +{ + nvlist_t *nvl; + + printf("1..232\n"); + + nvl = nvlist_create(0); + + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/null")); + CHECK(!nvlist_exists_number(nvl, "nvlist/null")); + CHECK(!nvlist_exists_string(nvl, "nvlist/null")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/null")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/null")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/null")); + nvlist_add_null(nvl, "nvlist/null"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/null")); + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/null")); + CHECK(!nvlist_exists_number(nvl, "nvlist/null")); + CHECK(!nvlist_exists_string(nvl, "nvlist/null")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/null")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/null")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/null")); + + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_null(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_string(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/bool")); + nvlist_add_bool(nvl, "nvlist/bool", true); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_null(nvl, "nvlist/bool")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_string(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/bool")); + + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists_null(nvl, "nvlist/number")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/number")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/number")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/number")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/number")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/number")); + nvlist_add_number(nvl, "nvlist/number", 0); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists_null(nvl, "nvlist/number")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/number")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/number")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/number")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/number")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/number")); + + CHECK(!nvlist_exists(nvl, "nvlist/string")); + CHECK(!nvlist_exists_null(nvl, "nvlist/string")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/string")); + CHECK(!nvlist_exists_number(nvl, "nvlist/string")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/string")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/string")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/string")); + nvlist_add_string(nvl, "nvlist/string", "test"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/string")); + CHECK(!nvlist_exists_null(nvl, "nvlist/string")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/string")); + CHECK(!nvlist_exists_number(nvl, "nvlist/string")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/string")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/string")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/string")); + + CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_null(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_number(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_string(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/nvlist")); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_null(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_number(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_string(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/nvlist")); + + CHECK(!nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_null(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_number(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_string(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/descriptor")); + nvlist_add_descriptor(nvl, "nvlist/descriptor", STDERR_FILENO); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_null(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_number(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_string(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/descriptor")); + + CHECK(!nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_number(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_string(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); + nvlist_add_binary(nvl, "nvlist/binary", "test", 4); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_number(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_string(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/binary")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + CHECK(nvlist_exists(nvl, "nvlist/null")); + CHECK(nvlist_exists(nvl, "nvlist/bool")); + CHECK(nvlist_exists(nvl, "nvlist/number")); + CHECK(nvlist_exists(nvl, "nvlist/string")); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_null(nvl, "nvlist/null"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(nvlist_exists(nvl, "nvlist/bool")); + CHECK(nvlist_exists(nvl, "nvlist/number")); + CHECK(nvlist_exists(nvl, "nvlist/string")); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_bool(nvl, "nvlist/bool"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(nvlist_exists(nvl, "nvlist/number")); + CHECK(nvlist_exists(nvl, "nvlist/string")); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_number(nvl, "nvlist/number"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(nvlist_exists(nvl, "nvlist/string")); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_string(nvl, "nvlist/string"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists(nvl, "nvlist/string")); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_nvlist(nvl, "nvlist/nvlist"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists(nvl, "nvlist/string")); + CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_descriptor(nvl, "nvlist/descriptor"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists(nvl, "nvlist/string")); + CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_binary(nvl, "nvlist/binary"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists(nvl, "nvlist/string")); + CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); + + CHECK(nvlist_empty(nvl)); + + nvlist_destroy(nvl); + + return (0); +} diff --git a/lib/libnv/tests/nvlist_free_test.c b/lib/libnv/tests/nvlist_free_test.c new file mode 100644 index 0000000..4417a44 --- /dev/null +++ b/lib/libnv/tests/nvlist_free_test.c @@ -0,0 +1,221 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#include <stdio.h> +#include <unistd.h> + +#include <nv.h> + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +int +main(void) +{ + nvlist_t *nvl; + + printf("1..114\n"); + + nvl = nvlist_create(0); + + nvlist_add_null(nvl, "nvlist/null"); + nvlist_add_bool(nvl, "nvlist/bool", true); + nvlist_add_number(nvl, "nvlist/number", 0); + nvlist_add_string(nvl, "nvlist/string", "test"); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + nvlist_add_descriptor(nvl, "nvlist/descriptor", STDERR_FILENO); + nvlist_add_binary(nvl, "nvlist/binary", "test", 4); + + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_null(nvl, "nvlist/null"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_bool(nvl, "nvlist/bool"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_number(nvl, "nvlist/number"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_string(nvl, "nvlist/string"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_nvlist(nvl, "nvlist/nvlist"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_descriptor(nvl, "nvlist/descriptor"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_binary(nvl, "nvlist/binary"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); + + CHECK(nvlist_empty(nvl)); + + nvlist_add_null(nvl, "nvlist/null"); + nvlist_add_bool(nvl, "nvlist/bool", true); + nvlist_add_number(nvl, "nvlist/number", 0); + nvlist_add_string(nvl, "nvlist/string", "test"); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + nvlist_add_descriptor(nvl, "nvlist/descriptor", STDERR_FILENO); + nvlist_add_binary(nvl, "nvlist/binary", "test", 4); + + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/null"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/bool"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/number"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/string"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/nvlist"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/descriptor"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/binary"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); + + CHECK(nvlist_empty(nvl)); + + nvlist_destroy(nvl); + + return (0); +} diff --git a/lib/libnv/tests/nvlist_get_test.c b/lib/libnv/tests/nvlist_get_test.c new file mode 100644 index 0000000..b4468db --- /dev/null +++ b/lib/libnv/tests/nvlist_get_test.c @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <nv.h> + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +#define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) + +int +main(void) +{ + const nvlist_t *cnvl; + nvlist_t *nvl; + size_t size; + + printf("1..83\n"); + + nvl = nvlist_create(0); + + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/true")); + nvlist_add_bool(nvl, "nvlist/bool/true", true); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_get_bool(nvl, "nvlist/bool/true") == true); + + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/false")); + nvlist_add_bool(nvl, "nvlist/bool/false", false); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_get_bool(nvl, "nvlist/bool/false") == false); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/0")); + nvlist_add_number(nvl, "nvlist/number/0", 0); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_get_number(nvl, "nvlist/number/0") == 0); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/1")); + nvlist_add_number(nvl, "nvlist/number/1", 1); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_get_number(nvl, "nvlist/number/1") == 1); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/-1")); + nvlist_add_number(nvl, "nvlist/number/-1", -1); + CHECK(nvlist_error(nvl) == 0); + CHECK((int)nvlist_get_number(nvl, "nvlist/number/-1") == -1); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); + nvlist_add_number(nvl, "nvlist/number/UINT64_MAX", UINT64_MAX); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_get_number(nvl, "nvlist/number/UINT64_MAX") == UINT64_MAX); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); + nvlist_add_number(nvl, "nvlist/number/INT64_MIN", INT64_MIN); + CHECK(nvlist_error(nvl) == 0); + CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MIN") == INT64_MIN); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); + nvlist_add_number(nvl, "nvlist/number/INT64_MAX", INT64_MAX); + CHECK(nvlist_error(nvl) == 0); + CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MAX") == INT64_MAX); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/")); + nvlist_add_string(nvl, "nvlist/string/", ""); + CHECK(nvlist_error(nvl) == 0); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/"), "") == 0); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/x")); + nvlist_add_string(nvl, "nvlist/string/x", "x"); + CHECK(nvlist_error(nvl) == 0); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/x"), "x") == 0); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + nvlist_add_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"); + CHECK(nvlist_error(nvl) == 0); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz") == 0); + + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + nvlist_add_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", STDERR_FILENO); + CHECK(nvlist_error(nvl) == 0); + CHECK(fd_is_valid(nvlist_get_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO"))); + + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/x")); + nvlist_add_binary(nvl, "nvlist/binary/x", "x", 1); + CHECK(nvlist_error(nvl) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", NULL), "x", 1) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", &size), "x", 1) == 0); + CHECK(size == 1); + + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + nvlist_add_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_error(nvl) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + CHECK(nvlist_error(nvl) == 0); + cnvl = nvlist_get_nvlist(nvl, "nvlist/nvlist"); + CHECK(nvlist_get_bool(cnvl, "nvlist/bool/true") == true); + CHECK(nvlist_get_bool(cnvl, "nvlist/bool/false") == false); + CHECK(nvlist_get_number(cnvl, "nvlist/number/0") == 0); + CHECK(nvlist_get_number(cnvl, "nvlist/number/1") == 1); + CHECK((int)nvlist_get_number(cnvl, "nvlist/number/-1") == -1); + CHECK(nvlist_get_number(cnvl, "nvlist/number/UINT64_MAX") == UINT64_MAX); + CHECK((int64_t)nvlist_get_number(cnvl, "nvlist/number/INT64_MIN") == INT64_MIN); + CHECK((int64_t)nvlist_get_number(cnvl, "nvlist/number/INT64_MAX") == INT64_MAX); + CHECK(strcmp(nvlist_get_string(cnvl, "nvlist/string/"), "") == 0); + CHECK(strcmp(nvlist_get_string(cnvl, "nvlist/string/x"), "x") == 0); + CHECK(strcmp(nvlist_get_string(cnvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz") == 0); + /* TODO */ + CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/x", NULL), "x", 1) == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/x", &size), "x", 1) == 0); + CHECK(size == 1); + CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + CHECK(nvlist_get_bool(nvl, "nvlist/bool/true") == true); + CHECK(nvlist_get_bool(nvl, "nvlist/bool/false") == false); + CHECK(nvlist_get_number(nvl, "nvlist/number/0") == 0); + CHECK(nvlist_get_number(nvl, "nvlist/number/1") == 1); + CHECK((int)nvlist_get_number(nvl, "nvlist/number/-1") == -1); + CHECK(nvlist_get_number(nvl, "nvlist/number/UINT64_MAX") == UINT64_MAX); + CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MIN") == INT64_MIN); + CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MAX") == INT64_MAX); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/"), "") == 0); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/x"), "x") == 0); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz") == 0); + CHECK(fd_is_valid(nvlist_get_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO"))); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", NULL), "x", 1) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", &size), "x", 1) == 0); + CHECK(size == 1); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + nvlist_destroy(nvl); + + return (0); +} diff --git a/lib/libnv/tests/nvlist_move_test.c b/lib/libnv/tests/nvlist_move_test.c new file mode 100644 index 0000000..760399d --- /dev/null +++ b/lib/libnv/tests/nvlist_move_test.c @@ -0,0 +1,161 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <nv.h> + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +int +main(void) +{ + const nvlist_t *cnvl; + nvlist_t *nvl; + void *ptr; + size_t size; + int fd; + + printf("1..52\n"); + + nvl = nvlist_create(0); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/")); + ptr = strdup(""); + CHECK(ptr != NULL); + nvlist_move_string(nvl, "nvlist/string/", ptr); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/string/")); + CHECK(ptr == nvlist_get_string(nvl, "nvlist/string/")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/x")); + ptr = strdup("x"); + CHECK(ptr != NULL); + nvlist_move_string(nvl, "nvlist/string/x", ptr); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); + CHECK(ptr == nvlist_get_string(nvl, "nvlist/string/x")); + + CHECK(!nvlist_exists_string(nvl, + "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + ptr = strdup("abcdefghijklmnopqrstuvwxyz"); + CHECK(ptr != NULL); + nvlist_move_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", + ptr); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, + "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + CHECK(ptr == + nvlist_get_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + + CHECK(!nvlist_exists_descriptor(nvl, + "nvlist/descriptor/STDERR_FILENO")); + fd = dup(STDERR_FILENO); + CHECK(fd >= 0); + nvlist_move_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", fd); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + CHECK(fd == + nvlist_get_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/x")); + ptr = malloc(1); + CHECK(ptr != NULL); + memcpy(ptr, "x", 1); + nvlist_move_binary(nvl, "nvlist/binary/x", ptr, 1); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); + CHECK(ptr == nvlist_get_binary(nvl, "nvlist/binary/x", NULL)); + CHECK(ptr == nvlist_get_binary(nvl, "nvlist/binary/x", &size)); + CHECK(size == 1); + + CHECK(!nvlist_exists_binary(nvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + ptr = malloc(sizeof("abcdefghijklmnopqrstuvwxyz")); + CHECK(ptr != NULL); + memcpy(ptr, "abcdefghijklmnopqrstuvwxyz", + sizeof("abcdefghijklmnopqrstuvwxyz")); + nvlist_move_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", + ptr, sizeof("abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_binary(nvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + CHECK(ptr == nvlist_get_binary(nvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL)); + CHECK(ptr == nvlist_get_binary(nvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size)); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + ptr = nvlist_clone(nvl); + CHECK(ptr != NULL); + nvlist_move_nvlist(nvl, "nvlist/nvlist", ptr); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(ptr == nvlist_get_nvlist(nvl, "nvlist/nvlist")); + + CHECK(nvlist_exists_string(nvl, "nvlist/string/")); + CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); + CHECK(nvlist_exists_string(nvl, + "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); + CHECK(nvlist_exists_binary(nvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + + cnvl = nvlist_get_nvlist(nvl, "nvlist/nvlist"); + CHECK(nvlist_exists_string(cnvl, "nvlist/string/")); + CHECK(nvlist_exists_string(cnvl, "nvlist/string/x")); + CHECK(nvlist_exists_string(cnvl, + "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_descriptor(cnvl, + "nvlist/descriptor/STDERR_FILENO")); + CHECK(nvlist_exists_binary(cnvl, "nvlist/binary/x")); + CHECK(nvlist_exists_binary(cnvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + + nvlist_destroy(nvl); + + return (0); +} diff --git a/lib/libnv/tests/nvlist_send_recv_test.c b/lib/libnv/tests/nvlist_send_recv_test.c new file mode 100644 index 0000000..1b083c3 --- /dev/null +++ b/lib/libnv/tests/nvlist_send_recv_test.c @@ -0,0 +1,342 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <nv.h> + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +#define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) + +static void +child(int sock) +{ + nvlist_t *nvl; + nvlist_t *empty; + + nvl = nvlist_create(0); + empty = nvlist_create(0); + + nvlist_add_bool(nvl, "nvlist/bool/true", true); + nvlist_add_bool(nvl, "nvlist/bool/false", false); + nvlist_add_number(nvl, "nvlist/number/0", 0); + nvlist_add_number(nvl, "nvlist/number/1", 1); + nvlist_add_number(nvl, "nvlist/number/-1", -1); + nvlist_add_number(nvl, "nvlist/number/UINT64_MAX", UINT64_MAX); + nvlist_add_number(nvl, "nvlist/number/INT64_MIN", INT64_MIN); + nvlist_add_number(nvl, "nvlist/number/INT64_MAX", INT64_MAX); + nvlist_add_string(nvl, "nvlist/string/", ""); + nvlist_add_string(nvl, "nvlist/string/x", "x"); + nvlist_add_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"); + nvlist_add_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", STDERR_FILENO); + nvlist_add_binary(nvl, "nvlist/binary/x", "x", 1); + nvlist_add_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")); + nvlist_move_nvlist(nvl, "nvlist/nvlist/empty", empty); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + + nvlist_send(sock, nvl); + + nvlist_destroy(nvl); +} + +static void +parent(int sock) +{ + nvlist_t *nvl; + const nvlist_t *cnvl, *empty; + const char *name, *cname; + void *cookie, *ccookie; + int type, ctype; + size_t size; + + nvl = nvlist_recv(sock); + CHECK(nvlist_error(nvl) == 0); + if (nvlist_error(nvl) != 0) + err(1, "nvlist_recv() failed"); + + cookie = NULL; + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_BOOL); + CHECK(strcmp(name, "nvlist/bool/true") == 0); + CHECK(nvlist_get_bool(nvl, name) == true); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_BOOL); + CHECK(strcmp(name, "nvlist/bool/false") == 0); + CHECK(nvlist_get_bool(nvl, name) == false); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/0") == 0); + CHECK(nvlist_get_number(nvl, name) == 0); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/1") == 0); + CHECK(nvlist_get_number(nvl, name) == 1); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/-1") == 0); + CHECK((int)nvlist_get_number(nvl, name) == -1); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/UINT64_MAX") == 0); + CHECK(nvlist_get_number(nvl, name) == UINT64_MAX); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/INT64_MIN") == 0); + CHECK((int64_t)nvlist_get_number(nvl, name) == INT64_MIN); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/INT64_MAX") == 0); + CHECK((int64_t)nvlist_get_number(nvl, name) == INT64_MAX); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_STRING); + CHECK(strcmp(name, "nvlist/string/") == 0); + CHECK(strcmp(nvlist_get_string(nvl, name), "") == 0); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_STRING); + CHECK(strcmp(name, "nvlist/string/x") == 0); + CHECK(strcmp(nvlist_get_string(nvl, name), "x") == 0); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_STRING); + CHECK(strcmp(name, "nvlist/string/abcdefghijklmnopqrstuvwxyz") == 0); + CHECK(strcmp(nvlist_get_string(nvl, name), "abcdefghijklmnopqrstuvwxyz") == 0); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_DESCRIPTOR); + CHECK(strcmp(name, "nvlist/descriptor/STDERR_FILENO") == 0); + CHECK(fd_is_valid(nvlist_get_descriptor(nvl, name))); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_BINARY); + CHECK(strcmp(name, "nvlist/binary/x") == 0); + CHECK(memcmp(nvlist_get_binary(nvl, name, NULL), "x", 1) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, name, &size), "x", 1) == 0); + CHECK(size == 1); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_BINARY); + CHECK(strcmp(name, "nvlist/binary/abcdefghijklmnopqrstuvwxyz") == 0); + CHECK(memcmp(nvlist_get_binary(nvl, name, NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, name, &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NVLIST); + CHECK(strcmp(name, "nvlist/nvlist/empty") == 0); + cnvl = nvlist_get_nvlist(nvl, name); + CHECK(nvlist_empty(cnvl)); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NVLIST); + CHECK(strcmp(name, "nvlist/nvlist") == 0); + cnvl = nvlist_get_nvlist(nvl, name); + + ccookie = NULL; + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_BOOL); + CHECK(strcmp(cname, "nvlist/bool/true") == 0); + CHECK(nvlist_get_bool(cnvl, cname) == true); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_BOOL); + CHECK(strcmp(cname, "nvlist/bool/false") == 0); + CHECK(nvlist_get_bool(cnvl, cname) == false); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/0") == 0); + CHECK(nvlist_get_number(cnvl, cname) == 0); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/1") == 0); + CHECK(nvlist_get_number(cnvl, cname) == 1); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/-1") == 0); + CHECK((int)nvlist_get_number(cnvl, cname) == -1); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/UINT64_MAX") == 0); + CHECK(nvlist_get_number(cnvl, cname) == UINT64_MAX); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/INT64_MIN") == 0); + CHECK((int64_t)nvlist_get_number(cnvl, cname) == INT64_MIN); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/INT64_MAX") == 0); + CHECK((int64_t)nvlist_get_number(cnvl, cname) == INT64_MAX); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_STRING); + CHECK(strcmp(cname, "nvlist/string/") == 0); + CHECK(strcmp(nvlist_get_string(cnvl, cname), "") == 0); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_STRING); + CHECK(strcmp(cname, "nvlist/string/x") == 0); + CHECK(strcmp(nvlist_get_string(cnvl, cname), "x") == 0); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_STRING); + CHECK(strcmp(cname, "nvlist/string/abcdefghijklmnopqrstuvwxyz") == 0); + CHECK(strcmp(nvlist_get_string(cnvl, cname), "abcdefghijklmnopqrstuvwxyz") == 0); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_DESCRIPTOR); + CHECK(strcmp(cname, "nvlist/descriptor/STDERR_FILENO") == 0); + CHECK(fd_is_valid(nvlist_get_descriptor(cnvl, cname))); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_BINARY); + CHECK(strcmp(cname, "nvlist/binary/x") == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, cname, NULL), "x", 1) == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, cname, &size), "x", 1) == 0); + CHECK(size == 1); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_BINARY); + CHECK(strcmp(cname, "nvlist/binary/abcdefghijklmnopqrstuvwxyz") == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, cname, NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, cname, &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NVLIST); + CHECK(strcmp(cname, "nvlist/nvlist/empty") == 0); + empty = nvlist_get_nvlist(cnvl, cname); + CHECK(nvlist_empty(empty)); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname == NULL); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name == NULL); +} + +int +main(void) +{ + int status, socks[2]; + pid_t pid; + + printf("1..134\n"); + fflush(stdout); + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, socks) < 0) + err(1, "socketpair() failed"); + pid = fork(); + switch (pid) { + case -1: + /* Failure. */ + err(1, "unable to fork"); + case 0: + /* Child. */ + close(socks[0]); + child(socks[1]); + return (0); + default: + /* Parent. */ + close(socks[1]); + parent(socks[0]); + break; + } + + if (waitpid(pid, &status, 0) < 0) + err(1, "waitpid() failed"); + + return (0); +} diff --git a/lib/msun/tests/Makefile b/lib/msun/tests/Makefile index 7199ea7..ffa1765 100644 --- a/lib/msun/tests/Makefile +++ b/lib/msun/tests/Makefile @@ -36,12 +36,34 @@ NETBSD_ATF_TESTS_C+= sqrt_test NETBSD_ATF_TESTS_C+= tan_test NETBSD_ATF_TESTS_C+= tanh_test +TAP_TESTS_C+= cexp_test +TAP_TESTS_C+= conj_test +TAP_TESTS_C+= csqrt_test +TAP_TESTS_C+= fenv_test +TAP_TESTS_C+= fmaxmin_test +TAP_TESTS_C+= ilogb_test +TAP_TESTS_C+= invctrig_test +TAP_TESTS_C+= logarithm_test +TAP_TESTS_C+= lrint_test +TAP_TESTS_C+= nan_test +TAP_TESTS_C+= nearbyint_test +TAP_TESTS_C+= next_test +TAP_TESTS_C+= rem_test +TAP_TESTS_C+= trig_test + +.for t in ${TAP_TESTS_C} +CFLAGS.$t+= -O0 +CFLAGS.$t+= -I${SRCTOP}/tools/regression/lib/msun +.endfor + CSTD= c99 LDADD+= -lm DPADD+= ${LIBM} #COPTS+= -Wfloat-equal +IGNORE_PRAGMA= + # Copied from lib/msun/Makefile .if ${MACHINE_CPUARCH} == "i386" ARCH_SUBDIR= i387 diff --git a/lib/msun/tests/cexp_test.c b/lib/msun/tests/cexp_test.c new file mode 100644 index 0000000..6be71ad --- /dev/null +++ b/lib/msun/tests/cexp_test.c @@ -0,0 +1,322 @@ +/*- + * Copyright (c) 2008-2011 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Tests for corner cases in cexp*(). + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#include <assert.h> +#include <complex.h> +#include <fenv.h> +#include <float.h> +#include <math.h> +#include <stdio.h> + +#include "test-utils.h" + +#pragma STDC FENV_ACCESS ON +#pragma STDC CX_LIMITED_RANGE OFF + +/* + * Test that a function returns the correct value and sets the + * exception flags correctly. The exceptmask specifies which + * exceptions we should check. We need to be lenient for several + * reasons, but mainly because on some architectures it's impossible + * to raise FE_OVERFLOW without raising FE_INEXACT. In some cases, + * whether cexp() raises an invalid exception is unspecified. + * + * These are macros instead of functions so that assert provides more + * meaningful error messages. + * + * XXX The volatile here is to avoid gcc's bogus constant folding and work + * around the lack of support for the FENV_ACCESS pragma. + */ +#define test(func, z, result, exceptmask, excepts, checksign) do { \ + volatile long double complex _d = z; \ + assert(feclearexcept(FE_ALL_EXCEPT) == 0); \ + assert(cfpequal_cs((func)(_d), (result), (checksign))); \ + assert(((void)(func), fetestexcept(exceptmask) == (excepts))); \ +} while (0) + +/* Test within a given tolerance. */ +#define test_tol(func, z, result, tol) do { \ + volatile long double complex _d = z; \ + assert(cfpequal_tol((func)(_d), (result), (tol), \ + FPE_ABS_ZERO | CS_BOTH)); \ +} while (0) + +/* Test all the functions that compute cexp(x). */ +#define testall(x, result, exceptmask, excepts, checksign) do { \ + test(cexp, x, result, exceptmask, excepts, checksign); \ + test(cexpf, x, result, exceptmask, excepts, checksign); \ +} while (0) + +/* + * Test all the functions that compute cexp(x), within a given tolerance. + * The tolerance is specified in ulps. + */ +#define testall_tol(x, result, tol) do { \ + test_tol(cexp, x, result, tol * DBL_ULP()); \ + test_tol(cexpf, x, result, tol * FLT_ULP()); \ +} while (0) + +/* Various finite non-zero numbers to test. */ +static const float finites[] = +{ -42.0e20, -1.0, -1.0e-10, -0.0, 0.0, 1.0e-10, 1.0, 42.0e20 }; + + +/* Tests for 0 */ +void +test_zero(void) +{ + + /* cexp(0) = 1, no exceptions raised */ + testall(0.0, 1.0, ALL_STD_EXCEPT, 0, 1); + testall(-0.0, 1.0, ALL_STD_EXCEPT, 0, 1); + testall(CMPLXL(0.0, -0.0), CMPLXL(1.0, -0.0), ALL_STD_EXCEPT, 0, 1); + testall(CMPLXL(-0.0, -0.0), CMPLXL(1.0, -0.0), ALL_STD_EXCEPT, 0, 1); +} + +/* + * Tests for NaN. The signs of the results are indeterminate unless the + * imaginary part is 0. + */ +void +test_nan() +{ + int i; + + /* cexp(x + NaNi) = NaN + NaNi and optionally raises invalid */ + /* cexp(NaN + yi) = NaN + NaNi and optionally raises invalid (|y|>0) */ + for (i = 0; i < nitems(finites); i++) { + printf("# Run %d..\n", i); + testall(CMPLXL(finites[i], NAN), CMPLXL(NAN, NAN), + ALL_STD_EXCEPT & ~FE_INVALID, 0, 0); + if (finites[i] == 0.0) + continue; + /* XXX FE_INEXACT shouldn't be raised here */ + testall(CMPLXL(NAN, finites[i]), CMPLXL(NAN, NAN), + ALL_STD_EXCEPT & ~(FE_INVALID | FE_INEXACT), 0, 0); + } + + /* cexp(NaN +- 0i) = NaN +- 0i */ + testall(CMPLXL(NAN, 0.0), CMPLXL(NAN, 0.0), ALL_STD_EXCEPT, 0, 1); + testall(CMPLXL(NAN, -0.0), CMPLXL(NAN, -0.0), ALL_STD_EXCEPT, 0, 1); + + /* cexp(inf + NaN i) = inf + nan i */ + testall(CMPLXL(INFINITY, NAN), CMPLXL(INFINITY, NAN), + ALL_STD_EXCEPT, 0, 0); + /* cexp(-inf + NaN i) = 0 */ + testall(CMPLXL(-INFINITY, NAN), CMPLXL(0.0, 0.0), + ALL_STD_EXCEPT, 0, 0); + /* cexp(NaN + NaN i) = NaN + NaN i */ + testall(CMPLXL(NAN, NAN), CMPLXL(NAN, NAN), + ALL_STD_EXCEPT, 0, 0); +} + +void +test_inf(void) +{ + int i; + + /* cexp(x + inf i) = NaN + NaNi and raises invalid */ + for (i = 0; i < nitems(finites); i++) { + printf("# Run %d..\n", i); + testall(CMPLXL(finites[i], INFINITY), CMPLXL(NAN, NAN), + ALL_STD_EXCEPT, FE_INVALID, 1); + } + /* cexp(-inf + yi) = 0 * (cos(y) + sin(y)i) */ + /* XXX shouldn't raise an inexact exception */ + testall(CMPLXL(-INFINITY, M_PI_4), CMPLXL(0.0, 0.0), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + testall(CMPLXL(-INFINITY, 3 * M_PI_4), CMPLXL(-0.0, 0.0), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + testall(CMPLXL(-INFINITY, 5 * M_PI_4), CMPLXL(-0.0, -0.0), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + testall(CMPLXL(-INFINITY, 7 * M_PI_4), CMPLXL(0.0, -0.0), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + testall(CMPLXL(-INFINITY, 0.0), CMPLXL(0.0, 0.0), + ALL_STD_EXCEPT, 0, 1); + testall(CMPLXL(-INFINITY, -0.0), CMPLXL(0.0, -0.0), + ALL_STD_EXCEPT, 0, 1); + /* cexp(inf + yi) = inf * (cos(y) + sin(y)i) (except y=0) */ + /* XXX shouldn't raise an inexact exception */ + testall(CMPLXL(INFINITY, M_PI_4), CMPLXL(INFINITY, INFINITY), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + testall(CMPLXL(INFINITY, 3 * M_PI_4), CMPLXL(-INFINITY, INFINITY), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + testall(CMPLXL(INFINITY, 5 * M_PI_4), CMPLXL(-INFINITY, -INFINITY), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + testall(CMPLXL(INFINITY, 7 * M_PI_4), CMPLXL(INFINITY, -INFINITY), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + /* cexp(inf + 0i) = inf + 0i */ + testall(CMPLXL(INFINITY, 0.0), CMPLXL(INFINITY, 0.0), + ALL_STD_EXCEPT, 0, 1); + testall(CMPLXL(INFINITY, -0.0), CMPLXL(INFINITY, -0.0), + ALL_STD_EXCEPT, 0, 1); +} + +void +test_reals(void) +{ + int i; + + for (i = 0; i < nitems(finites); i++) { + /* XXX could check exceptions more meticulously */ + printf("# Run %d..\n", i); + test(cexp, CMPLXL(finites[i], 0.0), + CMPLXL(exp(finites[i]), 0.0), + FE_INVALID | FE_DIVBYZERO, 0, 1); + test(cexp, CMPLXL(finites[i], -0.0), + CMPLXL(exp(finites[i]), -0.0), + FE_INVALID | FE_DIVBYZERO, 0, 1); + test(cexpf, CMPLXL(finites[i], 0.0), + CMPLXL(expf(finites[i]), 0.0), + FE_INVALID | FE_DIVBYZERO, 0, 1); + test(cexpf, CMPLXL(finites[i], -0.0), + CMPLXL(expf(finites[i]), -0.0), + FE_INVALID | FE_DIVBYZERO, 0, 1); + } +} + +void +test_imaginaries(void) +{ + int i; + + for (i = 0; i < nitems(finites); i++) { + printf("# Run %d..\n", i); + test(cexp, CMPLXL(0.0, finites[i]), + CMPLXL(cos(finites[i]), sin(finites[i])), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + test(cexp, CMPLXL(-0.0, finites[i]), + CMPLXL(cos(finites[i]), sin(finites[i])), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + test(cexpf, CMPLXL(0.0, finites[i]), + CMPLXL(cosf(finites[i]), sinf(finites[i])), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + test(cexpf, CMPLXL(-0.0, finites[i]), + CMPLXL(cosf(finites[i]), sinf(finites[i])), + ALL_STD_EXCEPT & ~FE_INEXACT, 0, 1); + } +} + +void +test_small(void) +{ + static const double tests[] = { + /* csqrt(a + bI) = x + yI */ + /* a b x y */ + 1.0, M_PI_4, M_SQRT2 * 0.5 * M_E, M_SQRT2 * 0.5 * M_E, + -1.0, M_PI_4, M_SQRT2 * 0.5 / M_E, M_SQRT2 * 0.5 / M_E, + 2.0, M_PI_2, 0.0, M_E * M_E, + M_LN2, M_PI, -2.0, 0.0, + }; + double a, b; + double x, y; + int i; + + for (i = 0; i < nitems(tests); i += 4) { + printf("# Run %d..\n", i); + a = tests[i]; + b = tests[i + 1]; + x = tests[i + 2]; + y = tests[i + 3]; + test_tol(cexp, CMPLXL(a, b), CMPLXL(x, y), 3 * DBL_ULP()); + + /* float doesn't have enough precision to pass these tests */ + if (x == 0 || y == 0) + continue; + test_tol(cexpf, CMPLXL(a, b), CMPLXL(x, y), 1 * FLT_ULP()); + } +} + +/* Test inputs with a real part r that would overflow exp(r). */ +void +test_large(void) +{ + + test_tol(cexp, CMPLXL(709.79, 0x1p-1074), + CMPLXL(INFINITY, 8.94674309915433533273e-16), DBL_ULP()); + test_tol(cexp, CMPLXL(1000, 0x1p-1074), + CMPLXL(INFINITY, 9.73344457300016401328e+110), DBL_ULP()); + test_tol(cexp, CMPLXL(1400, 0x1p-1074), + CMPLXL(INFINITY, 5.08228858149196559681e+284), DBL_ULP()); + test_tol(cexp, CMPLXL(900, 0x1.23456789abcdep-1020), + CMPLXL(INFINITY, 7.42156649354218408074e+83), DBL_ULP()); + test_tol(cexp, CMPLXL(1300, 0x1.23456789abcdep-1020), + CMPLXL(INFINITY, 3.87514844965996756704e+257), DBL_ULP()); + + test_tol(cexpf, CMPLXL(88.73, 0x1p-149), + CMPLXL(INFINITY, 4.80265603e-07), 2 * FLT_ULP()); + test_tol(cexpf, CMPLXL(90, 0x1p-149), + CMPLXL(INFINITY, 1.7101492622e-06f), 2 * FLT_ULP()); + test_tol(cexpf, CMPLXL(192, 0x1p-149), + CMPLXL(INFINITY, 3.396809344e+38f), 2 * FLT_ULP()); + test_tol(cexpf, CMPLXL(120, 0x1.234568p-120), + CMPLXL(INFINITY, 1.1163382522e+16f), 2 * FLT_ULP()); + test_tol(cexpf, CMPLXL(170, 0x1.234568p-120), + CMPLXL(INFINITY, 5.7878851079e+37f), 2 * FLT_ULP()); +} + +int +main(int argc, char *argv[]) +{ + + printf("1..7\n"); + + test_zero(); + printf("ok 1 - cexp zero\n"); + + test_nan(); + printf("ok 2 - cexp nan\n"); + + test_inf(); + printf("ok 3 - cexp inf\n"); + +#if defined(__i386__) + printf("not ok 4 - cexp reals # TODO: PR # 191676 fails assertion on i386\n"); +#else + test_reals(); + printf("ok 4 - cexp reals\n"); +#endif + + test_imaginaries(); + printf("ok 5 - cexp imaginaries\n"); + + test_small(); + printf("ok 6 - cexp small\n"); + + test_large(); + printf("ok 7 - cexp large\n"); + + return (0); +} diff --git a/lib/msun/tests/conj_test.c b/lib/msun/tests/conj_test.c new file mode 100644 index 0000000..7426f9e --- /dev/null +++ b/lib/msun/tests/conj_test.c @@ -0,0 +1,139 @@ +/*- + * Copyright (c) 2008 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Tests for conj{,f,l}() + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <complex.h> +#include <fenv.h> +#include <math.h> +#include <stdio.h> + +#include "test-utils.h" + +#pragma STDC CX_LIMITED_RANGE OFF + +/* Make sure gcc doesn't use builtin versions of these or honor __pure2. */ +static float complex (*libconjf)(float complex) = conjf; +static double complex (*libconj)(double complex) = conj; +static long double complex (*libconjl)(long double complex) = conjl; +static float (*libcrealf)(float complex) = crealf; +static double (*libcreal)(double complex) = creal; +static long double (*libcreall)(long double complex) = creall; +static float (*libcimagf)(float complex) = cimagf; +static double (*libcimag)(double complex) = cimag; +static long double (*libcimagl)(long double complex) = cimagl; + +static const double tests[] = { + /* a + bI */ + 0.0, 0.0, + 0.0, 1.0, + 1.0, 0.0, + -1.0, 0.0, + 1.0, -0.0, + 0.0, -1.0, + 2.0, 4.0, + 0.0, INFINITY, + 0.0, -INFINITY, + INFINITY, 0.0, + NAN, 1.0, + 1.0, NAN, + NAN, NAN, + -INFINITY, INFINITY, +}; + +int +main(int argc, char *argv[]) +{ + static const int ntests = sizeof(tests) / sizeof(tests[0]) / 2; + complex float in; + complex long double expected; + int i; + + printf("1..%d\n", ntests * 3); + + for (i = 0; i < ntests; i++) { + __real__ expected = __real__ in = tests[2 * i]; + __imag__ in = tests[2 * i + 1]; + __imag__ expected = -cimag(in); + + assert(fpequal(libcrealf(in), __real__ in)); + assert(fpequal(libcreal(in), __real__ in)); + assert(fpequal(libcreall(in), __real__ in)); + assert(fpequal(libcimagf(in), __imag__ in)); + assert(fpequal(libcimag(in), __imag__ in)); + assert(fpequal(libcimagl(in), __imag__ in)); + + feclearexcept(FE_ALL_EXCEPT); + if (!cfpequal(libconjf(in), expected)) { + printf("not ok %d\t# conjf(%#.2g + %#.2gI): " + "wrong value\n", + 3 * i + 1, creal(in), cimag(in)); + } else if (fetestexcept(FE_ALL_EXCEPT)) { + printf("not ok %d\t# conjf(%#.2g + %#.2gI): " + "threw an exception\n", + 3 * i + 1, creal(in), cimag(in)); + } else { + printf("ok %d\t\t# conjf(%#.2g + %#.2gI)\n", + 3 * i + 1, creal(in), cimag(in)); + } + + feclearexcept(FE_ALL_EXCEPT); + if (!cfpequal(libconj(in), expected)) { + printf("not ok %d\t# conj(%#.2g + %#.2gI): " + "wrong value\n", + 3 * i + 2, creal(in), cimag(in)); + } else if (fetestexcept(FE_ALL_EXCEPT)) { + printf("not ok %d\t# conj(%#.2g + %#.2gI): " + "threw an exception\n", + 3 * i + 2, creal(in), cimag(in)); + } else { + printf("ok %d\t\t# conj(%#.2g + %#.2gI)\n", + 3 * i + 2, creal(in), cimag(in)); + } + + feclearexcept(FE_ALL_EXCEPT); + if (!cfpequal(libconjl(in), expected)) { + printf("not ok %d\t# conjl(%#.2g + %#.2gI): " + "wrong value\n", + 3 * i + 3, creal(in), cimag(in)); + } else if (fetestexcept(FE_ALL_EXCEPT)) { + printf("not ok %d\t# conjl(%#.2g + %#.2gI): " + "threw an exception\n", + 3 * i + 3, creal(in), cimag(in)); + } else { + printf("ok %d\t\t# conjl(%#.2g + %#.2gI)\n", + 3 * i + 3, creal(in), cimag(in)); + } + } + + return (0); +} diff --git a/lib/msun/tests/csqrt_test.c b/lib/msun/tests/csqrt_test.c new file mode 100644 index 0000000..aa119d1 --- /dev/null +++ b/lib/msun/tests/csqrt_test.c @@ -0,0 +1,295 @@ +/*- + * Copyright (c) 2007 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Tests for csqrt{,f}() + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#include <assert.h> +#include <complex.h> +#include <float.h> +#include <math.h> +#include <stdio.h> + +#include "test-utils.h" + +/* + * This is a test hook that can point to csqrtl(), _csqrt(), or to _csqrtf(). + * The latter two convert to float or double, respectively, and test csqrtf() + * and csqrt() with the same arguments. + */ +long double complex (*t_csqrt)(long double complex); + +static long double complex +_csqrtf(long double complex d) +{ + + return (csqrtf((float complex)d)); +} + +static long double complex +_csqrt(long double complex d) +{ + + return (csqrt((double complex)d)); +} + +#pragma STDC CX_LIMITED_RANGE OFF + +/* + * Compare d1 and d2 using special rules: NaN == NaN and +0 != -0. + * Fail an assertion if they differ. + */ +static void +assert_equal(long double complex d1, long double complex d2) +{ + + assert(cfpequal(d1, d2)); +} + +/* + * Test csqrt for some finite arguments where the answer is exact. + * (We do not test if it produces correctly rounded answers when the + * result is inexact, nor do we check whether it throws spurious + * exceptions.) + */ +static void +test_finite() +{ + static const double tests[] = { + /* csqrt(a + bI) = x + yI */ + /* a b x y */ + 0, 8, 2, 2, + 0, -8, 2, -2, + 4, 0, 2, 0, + -4, 0, 0, 2, + 3, 4, 2, 1, + 3, -4, 2, -1, + -3, 4, 1, 2, + -3, -4, 1, -2, + 5, 12, 3, 2, + 7, 24, 4, 3, + 9, 40, 5, 4, + 11, 60, 6, 5, + 13, 84, 7, 6, + 33, 56, 7, 4, + 39, 80, 8, 5, + 65, 72, 9, 4, + 987, 9916, 74, 67, + 5289, 6640, 83, 40, + 460766389075.0, 16762287900.0, 678910, 12345 + }; + /* + * We also test some multiples of the above arguments. This + * array defines which multiples we use. Note that these have + * to be small enough to not cause overflow for float precision + * with all of the constants in the above table. + */ + static const double mults[] = { + 1, + 2, + 3, + 13, + 16, + 0x1.p30, + 0x1.p-30, + }; + + double a, b; + double x, y; + int i, j; + + for (i = 0; i < nitems(tests); i += 4) { + for (j = 0; j < nitems(mults); j++) { + a = tests[i] * mults[j] * mults[j]; + b = tests[i + 1] * mults[j] * mults[j]; + x = tests[i + 2] * mults[j]; + y = tests[i + 3] * mults[j]; + assert(t_csqrt(CMPLXL(a, b)) == CMPLXL(x, y)); + } + } + +} + +/* + * Test the handling of +/- 0. + */ +static void +test_zeros() +{ + + assert_equal(t_csqrt(CMPLXL(0.0, 0.0)), CMPLXL(0.0, 0.0)); + assert_equal(t_csqrt(CMPLXL(-0.0, 0.0)), CMPLXL(0.0, 0.0)); + assert_equal(t_csqrt(CMPLXL(0.0, -0.0)), CMPLXL(0.0, -0.0)); + assert_equal(t_csqrt(CMPLXL(-0.0, -0.0)), CMPLXL(0.0, -0.0)); +} + +/* + * Test the handling of infinities when the other argument is not NaN. + */ +static void +test_infinities() +{ + static const double vals[] = { + 0.0, + -0.0, + 42.0, + -42.0, + INFINITY, + -INFINITY, + }; + + int i; + + for (i = 0; i < nitems(vals); i++) { + if (isfinite(vals[i])) { + assert_equal(t_csqrt(CMPLXL(-INFINITY, vals[i])), + CMPLXL(0.0, copysignl(INFINITY, vals[i]))); + assert_equal(t_csqrt(CMPLXL(INFINITY, vals[i])), + CMPLXL(INFINITY, copysignl(0.0, vals[i]))); + } + assert_equal(t_csqrt(CMPLXL(vals[i], INFINITY)), + CMPLXL(INFINITY, INFINITY)); + assert_equal(t_csqrt(CMPLXL(vals[i], -INFINITY)), + CMPLXL(INFINITY, -INFINITY)); + } +} + +/* + * Test the handling of NaNs. + */ +static void +test_nans() +{ + + assert(creall(t_csqrt(CMPLXL(INFINITY, NAN))) == INFINITY); + assert(isnan(cimagl(t_csqrt(CMPLXL(INFINITY, NAN))))); + + assert(isnan(creall(t_csqrt(CMPLXL(-INFINITY, NAN))))); + assert(isinf(cimagl(t_csqrt(CMPLXL(-INFINITY, NAN))))); + + assert_equal(t_csqrt(CMPLXL(NAN, INFINITY)), + CMPLXL(INFINITY, INFINITY)); + assert_equal(t_csqrt(CMPLXL(NAN, -INFINITY)), + CMPLXL(INFINITY, -INFINITY)); + + assert_equal(t_csqrt(CMPLXL(0.0, NAN)), CMPLXL(NAN, NAN)); + assert_equal(t_csqrt(CMPLXL(-0.0, NAN)), CMPLXL(NAN, NAN)); + assert_equal(t_csqrt(CMPLXL(42.0, NAN)), CMPLXL(NAN, NAN)); + assert_equal(t_csqrt(CMPLXL(-42.0, NAN)), CMPLXL(NAN, NAN)); + assert_equal(t_csqrt(CMPLXL(NAN, 0.0)), CMPLXL(NAN, NAN)); + assert_equal(t_csqrt(CMPLXL(NAN, -0.0)), CMPLXL(NAN, NAN)); + assert_equal(t_csqrt(CMPLXL(NAN, 42.0)), CMPLXL(NAN, NAN)); + assert_equal(t_csqrt(CMPLXL(NAN, -42.0)), CMPLXL(NAN, NAN)); + assert_equal(t_csqrt(CMPLXL(NAN, NAN)), CMPLXL(NAN, NAN)); +} + +/* + * Test whether csqrt(a + bi) works for inputs that are large enough to + * cause overflow in hypot(a, b) + a. In this case we are using + * csqrt(115 + 252*I) == 14 + 9*I + * scaled up to near MAX_EXP. + */ +static void +test_overflow(int maxexp) +{ + long double a, b; + long double complex result; + + a = ldexpl(115 * 0x1p-8, maxexp); + b = ldexpl(252 * 0x1p-8, maxexp); + result = t_csqrt(CMPLXL(a, b)); + assert(creall(result) == ldexpl(14 * 0x1p-4, maxexp / 2)); + assert(cimagl(result) == ldexpl(9 * 0x1p-4, maxexp / 2)); +} + +int +main(int argc, char *argv[]) +{ + + printf("1..15\n"); + + /* Test csqrt() */ + t_csqrt = _csqrt; + + test_finite(); + printf("ok 1 - csqrt\n"); + + test_zeros(); + printf("ok 2 - csqrt\n"); + + test_infinities(); + printf("ok 3 - csqrt\n"); + + test_nans(); + printf("ok 4 - csqrt\n"); + + test_overflow(DBL_MAX_EXP); + printf("ok 5 - csqrt\n"); + + /* Now test csqrtf() */ + t_csqrt = _csqrtf; + + test_finite(); + printf("ok 6 - csqrt\n"); + + test_zeros(); + printf("ok 7 - csqrt\n"); + + test_infinities(); + printf("ok 8 - csqrt\n"); + + test_nans(); + printf("ok 9 - csqrt\n"); + + test_overflow(FLT_MAX_EXP); + printf("ok 10 - csqrt\n"); + + /* Now test csqrtl() */ + t_csqrt = csqrtl; + + test_finite(); + printf("ok 11 - csqrt\n"); + + test_zeros(); + printf("ok 12 - csqrt\n"); + + test_infinities(); + printf("ok 13 - csqrt\n"); + + test_nans(); + printf("ok 14 - csqrt\n"); + + test_overflow(LDBL_MAX_EXP); + printf("ok 15 - csqrt\n"); + + return (0); +} diff --git a/lib/msun/tests/fenv_test.c b/lib/msun/tests/fenv_test.c new file mode 100644 index 0000000..0ea6e42 --- /dev/null +++ b/lib/msun/tests/fenv_test.c @@ -0,0 +1,576 @@ +/*- + * Copyright (c) 2004 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Test the correctness and C99-compliance of various fenv.h features. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/wait.h> +#include <assert.h> +#include <err.h> +#include <fenv.h> +#include <float.h> +#include <math.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +/* + * Implementations are permitted to define additional exception flags + * not specified in the standard, so it is not necessarily true that + * FE_ALL_EXCEPT == ALL_STD_EXCEPT. + */ +#define ALL_STD_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \ + FE_OVERFLOW | FE_UNDERFLOW) + +#define NEXCEPTS (sizeof(std_excepts) / sizeof(std_excepts[0])) + +static const int std_excepts[] = { + FE_INVALID, + FE_DIVBYZERO, + FE_OVERFLOW, + FE_UNDERFLOW, + FE_INEXACT, +}; + +/* init_exceptsets() initializes this to the power set of std_excepts[] */ +static int std_except_sets[1 << NEXCEPTS]; + +static void init_exceptsets(void); + +static void test_dfl_env(void); +static void test_fegsetenv(void); +static void test_fegsetexceptflag(void); +static void test_masking(void); +static void test_fegsetround(void); +static void test_feholdupdate(void); +static void test_feraiseexcept(void); +static void test_fetestclearexcept(void); + +static int getround(void); +static void raiseexcept(int excepts); +static void trap_handler(int sig); + +#pragma STDC FENV_ACCESS ON + +int +main(int argc, char *argv[]) +{ + + printf("1..8\n"); + init_exceptsets(); + test_dfl_env(); + printf("ok 1 - fenv\n"); + test_fetestclearexcept(); + printf("ok 2 - fenv\n"); + test_fegsetexceptflag(); + printf("ok 3 - fenv\n"); + test_feraiseexcept(); + printf("ok 4 - fenv\n"); + test_fegsetround(); + printf("ok 5 - fenv\n"); + test_fegsetenv(); + printf("ok 6 - fenv\n"); + test_masking(); + printf("ok 7 - fenv\n"); + test_feholdupdate(); + printf("ok 8 - fenv\n"); + + return (0); +} + +/* + * Initialize std_except_sets[] to the power set of std_excepts[] + */ +void +init_exceptsets(void) +{ + int i, j, sr; + + for (i = 0; i < 1 << NEXCEPTS; i++) { + for (sr = i, j = 0; sr != 0; sr >>= 1, j++) + std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1); + } +} + +/* + * This tests checks the default FP environment, so it must be first. + * The memcmp() test below may be too much to ask for, since there + * could be multiple machine-specific default environments. + */ +static void +test_dfl_env(void) +{ +#ifndef NO_STRICT_DFL_ENV + fenv_t env; + + fegetenv(&env); + +#ifdef __amd64__ + /* + * Compare the fields that the AMD [1] and Intel [2] specs say will be + * set once fnstenv returns. + * + * Not all amd64 capable processors implement the fnstenv instruction + * by zero'ing out the env.__x87.__other field (example: AMD Opteron + * 6308). The AMD64/x64 specs aren't explicit on what the + * env.__x87.__other field will contain after fnstenv is executed, so + * the values in env.__x87.__other could be filled with arbitrary + * data depending on how the CPU implements fnstenv. + * + * 1. http://support.amd.com/TechDocs/26569_APM_v5.pdf + * 2. http://www.intel.com/Assets/en_US/PDF/manual/253666.pdf + */ + assert(memcmp(&env.__mxcsr, &FE_DFL_ENV->__mxcsr, + sizeof(env.__mxcsr)) == 0); + assert(memcmp(&env.__x87.__control, &FE_DFL_ENV->__x87.__control, + sizeof(env.__x87.__control)) == 0); + assert(memcmp(&env.__x87.__status, &FE_DFL_ENV->__x87.__status, + sizeof(env.__x87.__status)) == 0); + assert(memcmp(&env.__x87.__tag, &FE_DFL_ENV->__x87.__tag, + sizeof(env.__x87.__tag)) == 0); +#else + assert(memcmp(&env, FE_DFL_ENV, sizeof(env)) == 0); +#endif + +#endif + assert(fetestexcept(FE_ALL_EXCEPT) == 0); +} + +/* + * Test fetestexcept() and feclearexcept(). + */ +static void +test_fetestclearexcept(void) +{ + int excepts, i; + + for (i = 0; i < 1 << NEXCEPTS; i++) + assert(fetestexcept(std_except_sets[i]) == 0); + for (i = 0; i < 1 << NEXCEPTS; i++) { + excepts = std_except_sets[i]; + + /* FE_ALL_EXCEPT might be special-cased, as on i386. */ + raiseexcept(excepts); + assert(fetestexcept(excepts) == excepts); + assert(feclearexcept(FE_ALL_EXCEPT) == 0); + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + + raiseexcept(excepts); + assert(fetestexcept(excepts) == excepts); + if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) { + excepts |= FE_INEXACT; + assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) == + excepts); + } else { + assert(fetestexcept(ALL_STD_EXCEPT) == excepts); + } + assert(feclearexcept(excepts) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == 0); + } +} + +/* + * Test fegetexceptflag() and fesetexceptflag(). + * + * Prerequisites: fetestexcept(), feclearexcept() + */ +static void +test_fegsetexceptflag(void) +{ + fexcept_t flag; + int excepts, i; + + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + for (i = 0; i < 1 << NEXCEPTS; i++) { + excepts = std_except_sets[i]; + + assert(fegetexceptflag(&flag, excepts) == 0); + raiseexcept(ALL_STD_EXCEPT); + assert(fesetexceptflag(&flag, excepts) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == + (ALL_STD_EXCEPT ^ excepts)); + + assert(fegetexceptflag(&flag, FE_ALL_EXCEPT) == 0); + assert(feclearexcept(FE_ALL_EXCEPT) == 0); + assert(fesetexceptflag(&flag, excepts) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == 0); + assert(fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == + (ALL_STD_EXCEPT ^ excepts)); + + assert(feclearexcept(FE_ALL_EXCEPT) == 0); + } +} + +/* + * Test feraiseexcept(). + * + * Prerequisites: fetestexcept(), feclearexcept() + */ +static void +test_feraiseexcept(void) +{ + int excepts, i; + + for (i = 0; i < 1 << NEXCEPTS; i++) { + excepts = std_except_sets[i]; + + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + assert(feraiseexcept(excepts) == 0); + if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) { + excepts |= FE_INEXACT; + assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) == + excepts); + } else { + assert(fetestexcept(ALL_STD_EXCEPT) == excepts); + } + assert(feclearexcept(FE_ALL_EXCEPT) == 0); + } + assert(feraiseexcept(FE_INVALID | FE_DIVBYZERO) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == (FE_INVALID | FE_DIVBYZERO)); + assert(feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == ALL_STD_EXCEPT); + assert(feclearexcept(FE_ALL_EXCEPT) == 0); +} + +/* + * Test fegetround() and fesetround(). + */ +static void +test_fegsetround(void) +{ + + assert(fegetround() == FE_TONEAREST); + assert(getround() == FE_TONEAREST); + assert(FLT_ROUNDS == 1); + + assert(fesetround(FE_DOWNWARD) == 0); + assert(fegetround() == FE_DOWNWARD); + assert(getround() == FE_DOWNWARD); + assert(FLT_ROUNDS == 3); + + assert(fesetround(FE_UPWARD) == 0); + assert(getround() == FE_UPWARD); + assert(fegetround() == FE_UPWARD); + assert(FLT_ROUNDS == 2); + + assert(fesetround(FE_TOWARDZERO) == 0); + assert(getround() == FE_TOWARDZERO); + assert(fegetround() == FE_TOWARDZERO); + assert(FLT_ROUNDS == 0); + + assert(fesetround(FE_TONEAREST) == 0); + assert(getround() == FE_TONEAREST); + assert(FLT_ROUNDS == 1); + + assert(feclearexcept(FE_ALL_EXCEPT) == 0); +} + +/* + * Test fegetenv() and fesetenv(). + * + * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround() + */ +static void +test_fegsetenv(void) +{ + fenv_t env1, env2; + int excepts, i; + + for (i = 0; i < 1 << NEXCEPTS; i++) { + excepts = std_except_sets[i]; + + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + assert(fegetround() == FE_TONEAREST); + assert(fegetenv(&env1) == 0); + + /* + * fe[gs]etenv() should be able to save and restore + * exception flags without the spurious inexact + * exceptions that afflict raiseexcept(). + */ + raiseexcept(excepts); + if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 && + (excepts & FE_INEXACT) == 0) + assert(feclearexcept(FE_INEXACT) == 0); + + fesetround(FE_DOWNWARD); + assert(fegetenv(&env2) == 0); + assert(fesetenv(&env1) == 0); + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + assert(fegetround() == FE_TONEAREST); + + assert(fesetenv(&env2) == 0); + assert(fetestexcept(FE_ALL_EXCEPT) == excepts); + assert(fegetround() == FE_DOWNWARD); + assert(fesetenv(&env1) == 0); + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + assert(fegetround() == FE_TONEAREST); + } +} + +/* + * Test fegetexcept(), fedisableexcept(), and feenableexcept(). + * + * Prerequisites: fetestexcept(), feraiseexcept() + */ +static void +test_masking(void) +{ + struct sigaction act; + int except, i, pass, raise, status; + + assert((fegetexcept() & ALL_STD_EXCEPT) == 0); + assert((feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT) == 0); + assert((feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT) == + (FE_INVALID | FE_OVERFLOW)); + assert((fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT) == + (FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW)); + assert((fegetexcept() & ALL_STD_EXCEPT) == (FE_INVALID | FE_UNDERFLOW)); + assert((fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT) == + (FE_INVALID | FE_UNDERFLOW)); + assert((fegetexcept() & ALL_STD_EXCEPT) == 0); + + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = trap_handler; + for (pass = 0; pass < 2; pass++) { + for (i = 0; i < NEXCEPTS; i++) { + except = std_excepts[i]; + /* over/underflow may also raise inexact */ + if (except == FE_INEXACT) + raise = FE_DIVBYZERO | FE_INVALID; + else + raise = ALL_STD_EXCEPT ^ except; + + /* + * We need to fork a child process because + * there isn't a portable way to recover from + * a floating-point exception. + */ + switch(fork()) { + case 0: /* child */ + assert((fegetexcept() & ALL_STD_EXCEPT) == 0); + assert((feenableexcept(except) + & ALL_STD_EXCEPT) == 0); + assert(fegetexcept() == except); + raiseexcept(raise); + assert(feraiseexcept(raise) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == raise); + + assert(sigaction(SIGFPE, &act, NULL) == 0); + switch (pass) { + case 0: + raiseexcept(except); + case 1: + feraiseexcept(except); + default: + assert(0); + } + assert(0); + default: /* parent */ + assert(wait(&status) > 0); + /* + * Avoid assert() here so that it's possible + * to examine a failed child's core dump. + */ + if (!WIFEXITED(status)) + errx(1, "child aborted\n"); + assert(WEXITSTATUS(status) == 0); + break; + case -1: /* error */ + assert(0); + } + } + } + assert(fetestexcept(FE_ALL_EXCEPT) == 0); +} + +/* + * Test feholdexcept() and feupdateenv(). + * + * Prerequisites: fetestexcept(), fegetround(), fesetround(), + * fedisableexcept(), feenableexcept() + */ +static void +test_feholdupdate(void) +{ + fenv_t env; + + struct sigaction act; + int except, i, pass, status, raise; + + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = trap_handler; + for (pass = 0; pass < 2; pass++) { + for (i = 0; i < NEXCEPTS; i++) { + except = std_excepts[i]; + /* over/underflow may also raise inexact */ + if (except == FE_INEXACT) + raise = FE_DIVBYZERO | FE_INVALID; + else + raise = ALL_STD_EXCEPT ^ except; + + /* + * We need to fork a child process because + * there isn't a portable way to recover from + * a floating-point exception. + */ + switch(fork()) { + case 0: /* child */ + /* + * We don't want to cause a fatal exception in + * the child until the second pass, so we can + * check other properties of feupdateenv(). + */ + if (pass == 1) + assert((feenableexcept(except) & + ALL_STD_EXCEPT) == 0); + raiseexcept(raise); + assert(fesetround(FE_DOWNWARD) == 0); + assert(feholdexcept(&env) == 0); + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + raiseexcept(except); + assert(fesetround(FE_UPWARD) == 0); + + if (pass == 1) + assert(sigaction(SIGFPE, &act, NULL) == + 0); + assert(feupdateenv(&env) == 0); + assert(fegetround() == FE_DOWNWARD); + assert(fetestexcept(ALL_STD_EXCEPT) == + (except | raise)); + + assert(pass == 0); + _exit(0); + default: /* parent */ + assert(wait(&status) > 0); + /* + * Avoid assert() here so that it's possible + * to examine a failed child's core dump. + */ + if (!WIFEXITED(status)) + errx(1, "child aborted\n"); + assert(WEXITSTATUS(status) == 0); + break; + case -1: /* error */ + assert(0); + } + } + } + assert(fetestexcept(FE_ALL_EXCEPT) == 0); +} + +/* + * Raise a floating-point exception without relying on the standard + * library routines, which we are trying to test. + * + * XXX We can't raise an {over,under}flow without also raising an + * inexact exception. + */ +static void +raiseexcept(int excepts) +{ + volatile double d; + + /* + * With a compiler that supports the FENV_ACCESS pragma + * properly, simple expressions like '0.0 / 0.0' should + * be sufficient to generate traps. Unfortunately, we + * need to bring a volatile variable into the equation + * to prevent incorrect optimizations. + */ + if (excepts & FE_INVALID) { + d = 0.0; + d = 0.0 / d; + } + if (excepts & FE_DIVBYZERO) { + d = 0.0; + d = 1.0 / d; + } + if (excepts & FE_OVERFLOW) { + d = DBL_MAX; + d *= 2.0; + } + if (excepts & FE_UNDERFLOW) { + d = DBL_MIN; + d /= DBL_MAX; + } + if (excepts & FE_INEXACT) { + d = DBL_MIN; + d += 1.0; + } + + /* + * On the x86 (and some other architectures?) the FPU and + * integer units are decoupled. We need to execute an FWAIT + * or a floating-point instruction to get synchronous exceptions. + */ + d = 1.0; + d += 1.0; +} + +/* + * Determine the current rounding mode without relying on the fenv + * routines. This function may raise an inexact exception. + */ +static int +getround(void) +{ + volatile double d; + + /* + * This test works just as well with 0.0 - 0.0, except on ia64 + * where 0.0 - 0.0 gives the wrong sign when rounding downwards. + */ + d = 1.0; + d -= 1.0; + if (copysign(1.0, d) < 0.0) + return (FE_DOWNWARD); + + d = 1.0; + if (d + (DBL_EPSILON * 3.0 / 4.0) == 1.0) + return (FE_TOWARDZERO); + if (d + (DBL_EPSILON * 1.0 / 4.0) > 1.0) + return (FE_UPWARD); + + return (FE_TONEAREST); +} + +static void +trap_handler(int sig) +{ + + assert(sig == SIGFPE); + _exit(0); +} diff --git a/lib/msun/tests/fmaxmin_test.c b/lib/msun/tests/fmaxmin_test.c new file mode 100644 index 0000000..7ddcc87 --- /dev/null +++ b/lib/msun/tests/fmaxmin_test.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 2008 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Tests for fmax{,f,l}() and fmin{,f,l}. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <fenv.h> +#include <float.h> +#include <math.h> +#include <stdio.h> + +#include "test-utils.h" + +#pragma STDC FENV_ACCESS ON + +/* + * Test whether func(x, y) has the expected result, and make sure no + * exceptions are raised. + */ +#define TEST(func, type, x, y, expected) do { \ + type __x = (x); /* convert before we clear exceptions */ \ + type __y = (y); \ + feclearexcept(ALL_STD_EXCEPT); \ + long double __result = func((__x), (__y)); \ + if (fetestexcept(ALL_STD_EXCEPT)) { \ + fprintf(stderr, #func "(%.20Lg, %.20Lg) raised 0x%x\n", \ + (x), (y), fetestexcept(FE_ALL_EXCEPT)); \ + ok = 0; \ + } \ + if (!fpequal(__result, (expected))) { \ + fprintf(stderr, #func "(%.20Lg, %.20Lg) = %.20Lg, " \ + "expected %.20Lg\n", (x), (y), __result, (expected)); \ + ok = 0; \ + } \ +} while (0) + +int +testall_r(long double big, long double small) +{ + int ok; + + long double expected_max = isnan(big) ? small : big; + long double expected_min = isnan(small) ? big : small; + ok = 1; + + TEST(fmaxf, float, big, small, expected_max); + TEST(fmaxf, float, small, big, expected_max); + TEST(fmax, double, big, small, expected_max); + TEST(fmax, double, small, big, expected_max); + TEST(fmaxl, long double, big, small, expected_max); + TEST(fmaxl, long double, small, big, expected_max); + TEST(fminf, float, big, small, expected_min); + TEST(fminf, float, small, big, expected_min); + TEST(fmin, double, big, small, expected_min); + TEST(fmin, double, small, big, expected_min); + TEST(fminl, long double, big, small, expected_min); + TEST(fminl, long double, small, big, expected_min); + + return (ok); +} + +/* + * Test all the functions: fmaxf, fmax, fmaxl, fminf, fmin, and fminl, + * in all rounding modes and with the arguments in different orders. + * The input 'big' must be >= 'small'. + */ +void +testall(int testnum, long double big, long double small) +{ + static const int rmodes[] = { + FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO + }; + int i; + + for (i = 0; i < 4; i++) { + fesetround(rmodes[i]); + if (!testall_r(big, small)) { + fprintf(stderr, "FAILURE in rounding mode %d\n", + rmodes[i]); + break; + } + } + printf("%sok %d - big = %.20Lg, small = %.20Lg\n", + (i == 4) ? "" : "not ", testnum, big, small); +} + +int +main(int argc, char *argv[]) +{ + + printf("1..12\n"); + + testall(1, 1.0, 0.0); + testall(2, 42.0, nextafterf(42.0, -INFINITY)); + testall(3, nextafterf(42.0, INFINITY), 42.0); + testall(4, -5.0, -5.0); + testall(5, -3.0, -4.0); + testall(6, 1.0, NAN); + testall(7, INFINITY, NAN); + testall(8, INFINITY, 1.0); + testall(9, -3.0, -INFINITY); + testall(10, 3.0, -INFINITY); + testall(11, NAN, NAN); + + /* This test isn't strictly required to work by C99. */ + testall(12, 0.0, -0.0); + + return (0); +} diff --git a/lib/msun/tests/ilogb_test.c b/lib/msun/tests/ilogb_test.c new file mode 100644 index 0000000..a1440c4 --- /dev/null +++ b/lib/msun/tests/ilogb_test.c @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <float.h> +#include <limits.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +int +main(void) +{ + char buf[128], *end; + double d; + float f; + long double ld; + int e, i; + + printf("1..3\n"); + assert(ilogb(0) == FP_ILOGB0); + assert(ilogb(NAN) == FP_ILOGBNAN); + assert(ilogb(INFINITY) == INT_MAX); + for (e = DBL_MIN_EXP - DBL_MANT_DIG; e < DBL_MAX_EXP; e++) { + snprintf(buf, sizeof(buf), "0x1.p%d", e); + d = strtod(buf, &end); + assert(*end == '\0'); + i = ilogb(d); + assert(i == e); + } + printf("ok 1 - ilogb\n"); + + assert(ilogbf(0) == FP_ILOGB0); + assert(ilogbf(NAN) == FP_ILOGBNAN); + assert(ilogbf(INFINITY) == INT_MAX); + for (e = FLT_MIN_EXP - FLT_MANT_DIG; e < FLT_MAX_EXP; e++) { + snprintf(buf, sizeof(buf), "0x1.p%d", e); + f = strtof(buf, &end); + assert(*end == '\0'); + i = ilogbf(f); + assert(i == e); + } + printf("ok 2 - ilogbf\n"); + + assert(ilogbl(0) == FP_ILOGB0); + assert(ilogbl(NAN) == FP_ILOGBNAN); + assert(ilogbl(INFINITY) == INT_MAX); + for (e = LDBL_MIN_EXP - LDBL_MANT_DIG; e < LDBL_MAX_EXP; e++) { + snprintf(buf, sizeof(buf), "0x1.p%d", e); + ld = strtold(buf, &end); + assert(*end == '\0'); + i = ilogbl(ld); + assert(i == e); + } + printf("ok 3 - ilogbl\n"); + + return (0); +} diff --git a/lib/msun/tests/invctrig_test.c b/lib/msun/tests/invctrig_test.c new file mode 100644 index 0000000..34e78a1 --- /dev/null +++ b/lib/msun/tests/invctrig_test.c @@ -0,0 +1,367 @@ +/*- + * Copyright (c) 2008-2013 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Tests for casin[h](), cacos[h](), and catan[h](). + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <complex.h> +#include <fenv.h> +#include <float.h> +#include <math.h> +#include <stdio.h> + +#include "test-utils.h" + +#pragma STDC FENV_ACCESS ON +#pragma STDC CX_LIMITED_RANGE OFF + +/* + * Test that a function returns the correct value and sets the + * exception flags correctly. The exceptmask specifies which + * exceptions we should check. We need to be lenient for several + * reasons, but mainly because on some architectures it's impossible + * to raise FE_OVERFLOW without raising FE_INEXACT. + * + * These are macros instead of functions so that assert provides more + * meaningful error messages. + * + * XXX The volatile here is to avoid gcc's bogus constant folding and work + * around the lack of support for the FENV_ACCESS pragma. + */ +#define test_p(func, z, result, exceptmask, excepts, checksign) do { \ + volatile long double complex _d = z; \ + debug(" testing %s(%Lg + %Lg I) == %Lg + %Lg I\n", #func, \ + creall(_d), cimagl(_d), creall(result), cimagl(result)); \ + assert(feclearexcept(FE_ALL_EXCEPT) == 0); \ + assert(cfpequal_cs((func)(_d), (result), (checksign))); \ + assert(((void)(func), fetestexcept(exceptmask) == (excepts))); \ +} while (0) + +/* + * Test within a given tolerance. The tolerance indicates relative error + * in ulps. + */ +#define test_p_tol(func, z, result, tol) do { \ + volatile long double complex _d = z; \ + debug(" testing %s(%Lg + %Lg I) ~= %Lg + %Lg I\n", #func, \ + creall(_d), cimagl(_d), creall(result), cimagl(result)); \ + assert(cfpequal_tol((func)(_d), (result), (tol), CS_BOTH)); \ +} while (0) + +/* These wrappers apply the identities f(conj(z)) = conj(f(z)). */ +#define test(func, z, result, exceptmask, excepts, checksign) do { \ + test_p(func, z, result, exceptmask, excepts, checksign); \ + test_p(func, conjl(z), conjl(result), exceptmask, excepts, checksign); \ +} while (0) +#define test_tol(func, z, result, tol) do { \ + test_p_tol(func, z, result, tol); \ + test_p_tol(func, conjl(z), conjl(result), tol); \ +} while (0) + +/* Test the given function in all precisions. */ +#define testall(func, x, result, exceptmask, excepts, checksign) do { \ + test(func, x, result, exceptmask, excepts, checksign); \ + test(func##f, x, result, exceptmask, excepts, checksign); \ +} while (0) +#define testall_odd(func, x, result, exceptmask, excepts, checksign) do { \ + testall(func, x, result, exceptmask, excepts, checksign); \ + testall(func, -(x), -result, exceptmask, excepts, checksign); \ +} while (0) +#define testall_even(func, x, result, exceptmask, excepts, checksign) do { \ + testall(func, x, result, exceptmask, excepts, checksign); \ + testall(func, -(x), result, exceptmask, excepts, checksign); \ +} while (0) + +/* + * Test the given function in all precisions, within a given tolerance. + * The tolerance is specified in ulps. + */ +#define testall_tol(func, x, result, tol) do { \ + test_tol(func, x, result, (tol) * DBL_ULP()); \ + test_tol(func##f, x, result, (tol) * FLT_ULP()); \ +} while (0) +#define testall_odd_tol(func, x, result, tol) do { \ + testall_tol(func, x, result, tol); \ + testall_tol(func, -(x), -result, tol); \ +} while (0) +#define testall_even_tol(func, x, result, tol) do { \ + testall_tol(func, x, result, tol); \ + testall_tol(func, -(x), result, tol); \ +} while (0) + +static const long double +pi = 3.14159265358979323846264338327950280L, +c3pi = 9.42477796076937971538793014983850839L; + + +/* Tests for 0 */ +void +test_zero(void) +{ + long double complex zero = CMPLXL(0.0, 0.0); + + testall_tol(cacosh, zero, CMPLXL(0.0, pi / 2), 1); + testall_tol(cacosh, -zero, CMPLXL(0.0, -pi / 2), 1); + testall_tol(cacos, zero, CMPLXL(pi / 2, -0.0), 1); + testall_tol(cacos, -zero, CMPLXL(pi / 2, 0.0), 1); + + testall_odd(casinh, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH); + testall_odd(casin, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH); + + testall_odd(catanh, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH); + testall_odd(catan, zero, zero, ALL_STD_EXCEPT, 0, CS_BOTH); +} + +/* + * Tests for NaN inputs. + */ +void +test_nan() +{ + long double complex nan_nan = CMPLXL(NAN, NAN); + long double complex z; + + /* + * IN CACOSH CACOS CASINH CATANH + * NaN,NaN NaN,NaN NaN,NaN NaN,NaN NaN,NaN + * finite,NaN NaN,NaN* NaN,NaN* NaN,NaN* NaN,NaN* + * NaN,finite NaN,NaN* NaN,NaN* NaN,NaN* NaN,NaN* + * NaN,Inf Inf,NaN NaN,-Inf ?Inf,NaN ?0,pi/2 + * +-Inf,NaN Inf,NaN NaN,?Inf +-Inf,NaN +-0,NaN + * +-0,NaN NaN,NaN* pi/2,NaN NaN,NaN* +-0,NaN + * NaN,0 NaN,NaN* NaN,NaN* NaN,0 NaN,NaN* + * + * * = raise invalid + */ + z = nan_nan; + testall(cacosh, z, nan_nan, ALL_STD_EXCEPT, 0, 0); + testall(cacos, z, nan_nan, ALL_STD_EXCEPT, 0, 0); + testall(casinh, z, nan_nan, ALL_STD_EXCEPT, 0, 0); + testall(casin, z, nan_nan, ALL_STD_EXCEPT, 0, 0); + testall(catanh, z, nan_nan, ALL_STD_EXCEPT, 0, 0); + testall(catan, z, nan_nan, ALL_STD_EXCEPT, 0, 0); + + z = CMPLXL(0.5, NAN); + testall(cacosh, z, nan_nan, OPT_INVALID, 0, 0); + testall(cacos, z, nan_nan, OPT_INVALID, 0, 0); + testall(casinh, z, nan_nan, OPT_INVALID, 0, 0); + testall(casin, z, nan_nan, OPT_INVALID, 0, 0); + testall(catanh, z, nan_nan, OPT_INVALID, 0, 0); + testall(catan, z, nan_nan, OPT_INVALID, 0, 0); + + z = CMPLXL(NAN, 0.5); + testall(cacosh, z, nan_nan, OPT_INVALID, 0, 0); + testall(cacos, z, nan_nan, OPT_INVALID, 0, 0); + testall(casinh, z, nan_nan, OPT_INVALID, 0, 0); + testall(casin, z, nan_nan, OPT_INVALID, 0, 0); + testall(catanh, z, nan_nan, OPT_INVALID, 0, 0); + testall(catan, z, nan_nan, OPT_INVALID, 0, 0); + + z = CMPLXL(NAN, INFINITY); + testall(cacosh, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0, CS_REAL); + testall(cacosh, -z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0, CS_REAL); + testall(cacos, z, CMPLXL(NAN, -INFINITY), ALL_STD_EXCEPT, 0, CS_IMAG); + testall(casinh, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0, 0); + testall(casin, z, CMPLXL(NAN, INFINITY), ALL_STD_EXCEPT, 0, CS_IMAG); + testall_tol(catanh, z, CMPLXL(0.0, pi / 2), 1); + testall(catan, z, CMPLXL(NAN, 0.0), ALL_STD_EXCEPT, 0, CS_IMAG); + + z = CMPLXL(INFINITY, NAN); + testall_even(cacosh, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0, + CS_REAL); + testall_even(cacos, z, CMPLXL(NAN, INFINITY), ALL_STD_EXCEPT, 0, 0); + testall_odd(casinh, z, CMPLXL(INFINITY, NAN), ALL_STD_EXCEPT, 0, + CS_REAL); + testall_odd(casin, z, CMPLXL(NAN, INFINITY), ALL_STD_EXCEPT, 0, 0); + testall_odd(catanh, z, CMPLXL(0.0, NAN), ALL_STD_EXCEPT, 0, CS_REAL); + testall_odd_tol(catan, z, CMPLXL(pi / 2, 0.0), 1); + + z = CMPLXL(0.0, NAN); + /* XXX We allow a spurious inexact exception here. */ + testall_even(cacosh, z, nan_nan, OPT_INVALID & ~FE_INEXACT, 0, 0); + testall_even_tol(cacos, z, CMPLXL(pi / 2, NAN), 1); + testall_odd(casinh, z, nan_nan, OPT_INVALID, 0, 0); + testall_odd(casin, z, CMPLXL(0.0, NAN), ALL_STD_EXCEPT, 0, CS_REAL); + testall_odd(catanh, z, CMPLXL(0.0, NAN), OPT_INVALID, 0, CS_REAL); + testall_odd(catan, z, nan_nan, OPT_INVALID, 0, 0); + + z = CMPLXL(NAN, 0.0); + testall(cacosh, z, nan_nan, OPT_INVALID, 0, 0); + testall(cacos, z, nan_nan, OPT_INVALID, 0, 0); + testall(casinh, z, CMPLXL(NAN, 0), ALL_STD_EXCEPT, 0, CS_IMAG); + testall(casin, z, nan_nan, OPT_INVALID, 0, 0); + testall(catanh, z, nan_nan, OPT_INVALID, 0, CS_IMAG); + testall(catan, z, CMPLXL(NAN, 0.0), ALL_STD_EXCEPT, 0, 0); +} + +void +test_inf(void) +{ + long double complex z; + + /* + * IN CACOSH CACOS CASINH CATANH + * Inf,Inf Inf,pi/4 pi/4,-Inf Inf,pi/4 0,pi/2 + * -Inf,Inf Inf,3pi/4 3pi/4,-Inf --- --- + * Inf,finite Inf,0 0,-Inf Inf,0 0,pi/2 + * -Inf,finite Inf,pi pi,-Inf --- --- + * finite,Inf Inf,pi/2 pi/2,-Inf Inf,pi/2 0,pi/2 + */ + z = CMPLXL(INFINITY, INFINITY); + testall_tol(cacosh, z, CMPLXL(INFINITY, pi / 4), 1); + testall_tol(cacosh, -z, CMPLXL(INFINITY, -c3pi / 4), 1); + testall_tol(cacos, z, CMPLXL(pi / 4, -INFINITY), 1); + testall_tol(cacos, -z, CMPLXL(c3pi / 4, INFINITY), 1); + testall_odd_tol(casinh, z, CMPLXL(INFINITY, pi / 4), 1); + testall_odd_tol(casin, z, CMPLXL(pi / 4, INFINITY), 1); + testall_odd_tol(catanh, z, CMPLXL(0, pi / 2), 1); + testall_odd_tol(catan, z, CMPLXL(pi / 2, 0), 1); + + z = CMPLXL(INFINITY, 0.5); + /* XXX We allow a spurious inexact exception here. */ + testall(cacosh, z, CMPLXL(INFINITY, 0), OPT_INEXACT, 0, CS_BOTH); + testall_tol(cacosh, -z, CMPLXL(INFINITY, -pi), 1); + testall(cacos, z, CMPLXL(0, -INFINITY), OPT_INEXACT, 0, CS_BOTH); + testall_tol(cacos, -z, CMPLXL(pi, INFINITY), 1); + testall_odd(casinh, z, CMPLXL(INFINITY, 0), OPT_INEXACT, 0, CS_BOTH); + testall_odd_tol(casin, z, CMPLXL(pi / 2, INFINITY), 1); + testall_odd_tol(catanh, z, CMPLXL(0, pi / 2), 1); + testall_odd_tol(catan, z, CMPLXL(pi / 2, 0), 1); + + z = CMPLXL(0.5, INFINITY); + testall_tol(cacosh, z, CMPLXL(INFINITY, pi / 2), 1); + testall_tol(cacosh, -z, CMPLXL(INFINITY, -pi / 2), 1); + testall_tol(cacos, z, CMPLXL(pi / 2, -INFINITY), 1); + testall_tol(cacos, -z, CMPLXL(pi / 2, INFINITY), 1); + testall_odd_tol(casinh, z, CMPLXL(INFINITY, pi / 2), 1); + /* XXX We allow a spurious inexact exception here. */ + testall_odd(casin, z, CMPLXL(0.0, INFINITY), OPT_INEXACT, 0, CS_BOTH); + testall_odd_tol(catanh, z, CMPLXL(0, pi / 2), 1); + testall_odd_tol(catan, z, CMPLXL(pi / 2, 0), 1); +} + +/* Tests along the real and imaginary axes. */ +void +test_axes(void) +{ + static const long double nums[] = { + -2, -1, -0.5, 0.5, 1, 2 + }; + long double complex z; + int i; + + for (i = 0; i < sizeof(nums) / sizeof(nums[0]); i++) { + /* Real axis */ + z = CMPLXL(nums[i], 0.0); + if (fabsl(nums[i]) <= 1) { + testall_tol(cacosh, z, CMPLXL(0.0, acos(nums[i])), 1); + testall_tol(cacos, z, CMPLXL(acosl(nums[i]), -0.0), 1); + testall_tol(casin, z, CMPLXL(asinl(nums[i]), 0.0), 1); + testall_tol(catanh, z, CMPLXL(atanh(nums[i]), 0.0), 1); + } else { + testall_tol(cacosh, z, + CMPLXL(acosh(fabsl(nums[i])), + (nums[i] < 0) ? pi : 0), 1); + testall_tol(cacos, z, + CMPLXL((nums[i] < 0) ? pi : 0, + -acosh(fabsl(nums[i]))), 1); + testall_tol(casin, z, + CMPLXL(copysign(pi / 2, nums[i]), + acosh(fabsl(nums[i]))), 1); + testall_tol(catanh, z, + CMPLXL(atanh(1 / nums[i]), pi / 2), 1); + } + testall_tol(casinh, z, CMPLXL(asinh(nums[i]), 0.0), 1); + testall_tol(catan, z, CMPLXL(atan(nums[i]), 0), 1); + + /* TODO: Test the imaginary axis. */ + } +} + +void +test_small(void) +{ + /* + * z = 0.75 + i 0.25 + * acos(z) = Pi/4 - i ln(2)/2 + * asin(z) = Pi/4 + i ln(2)/2 + * atan(z) = atan(4)/2 + i ln(17/9)/4 + */ + complex long double z; + complex long double acos_z; + complex long double asin_z; + complex long double atan_z; + + z = CMPLXL(0.75L, 0.25L); + acos_z = CMPLXL(pi / 4, -0.34657359027997265470861606072908828L); + asin_z = CMPLXL(pi / 4, 0.34657359027997265470861606072908828L); + atan_z = CMPLXL(0.66290883183401623252961960521423782L, + 0.15899719167999917436476103600701878L); + + testall_tol(cacos, z, acos_z, 2); + testall_odd_tol(casin, z, asin_z, 2); + testall_odd_tol(catan, z, atan_z, 2); +} + +/* Test inputs that might cause overflow in a sloppy implementation. */ +void +test_large(void) +{ + + /* TODO: Write these tests */ +} + +int +main(int argc, char *argv[]) +{ + + printf("1..6\n"); + + test_zero(); + printf("ok 1 - invctrig zero\n"); + + test_nan(); + printf("ok 2 - invctrig nan\n"); + + test_inf(); + printf("ok 3 - invctrig inf\n"); + + test_axes(); + printf("ok 4 - invctrig axes\n"); + + test_small(); + printf("ok 5 - invctrig small\n"); + + test_large(); + printf("ok 6 - invctrig large\n"); + + return (0); +} diff --git a/lib/msun/tests/logarithm_test.c b/lib/msun/tests/logarithm_test.c new file mode 100644 index 0000000..18b9ebe --- /dev/null +++ b/lib/msun/tests/logarithm_test.c @@ -0,0 +1,286 @@ +/*- + * Copyright (c) 2008-2010 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Tests for corner cases in log*(). + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <fenv.h> +#include <float.h> +#include <math.h> +#include <stdio.h> + +#ifdef __i386__ +#include <ieeefp.h> +#endif + +#include "test-utils.h" + +#pragma STDC FENV_ACCESS ON + +/* + * Test that a function returns the correct value and sets the + * exception flags correctly. The exceptmask specifies which + * exceptions we should check. We need to be lenient for several + * reasoons, but mainly because on some architectures it's impossible + * to raise FE_OVERFLOW without raising FE_INEXACT. + * + * These are macros instead of functions so that assert provides more + * meaningful error messages. + * + * XXX The volatile here is to avoid gcc's bogus constant folding and work + * around the lack of support for the FENV_ACCESS pragma. + */ +#define test(func, x, result, exceptmask, excepts) do { \ + volatile long double _d = x; \ + assert(feclearexcept(FE_ALL_EXCEPT) == 0); \ + assert(fpequal((func)(_d), (result))); \ + assert(((void)(func), fetestexcept(exceptmask) == (excepts))); \ +} while (0) + +#define test(func, x, result, exceptmask, excepts) do { \ + volatile long double _d = x; \ + assert(feclearexcept(FE_ALL_EXCEPT) == 0); \ + assert(fpequal((func)(_d), (result))); \ + assert(((void)(func), fetestexcept(exceptmask) == (excepts))); \ +} while (0) + +#define test_tol(func, z, result, tol) do { \ + volatile long double _d = z; \ + debug(" testing %6s(%15La) ~= % .36Le\n", #func, _d, result); \ + assert(fpequal_tol((func)(_d), (result), (tol), CS_BOTH)); \ +} while (0) + +/* Test all the functions that compute log(x). */ +#define testall0(x, result, exceptmask, excepts) do { \ + test(log, x, result, exceptmask, excepts); \ + test(logf, x, result, exceptmask, excepts); \ + test(logl, x, result, exceptmask, excepts); \ + test(log2, x, result, exceptmask, excepts); \ + test(log2f, x, result, exceptmask, excepts); \ + test(log2l, x, result, exceptmask, excepts); \ + test(log10, x, result, exceptmask, excepts); \ + test(log10f, x, result, exceptmask, excepts); \ + test(log10l, x, result, exceptmask, excepts); \ +} while (0) + +/* Test all the functions that compute log(1+x). */ +#define testall1(x, result, exceptmask, excepts) do { \ + test(log1p, x, result, exceptmask, excepts); \ + test(log1pf, x, result, exceptmask, excepts); \ + test(log1pl, x, result, exceptmask, excepts); \ +} while (0) + +void +run_generic_tests(void) +{ + + /* log(1) == 0, no exceptions raised */ + testall0(1.0, 0.0, ALL_STD_EXCEPT, 0); + testall1(0.0, 0.0, ALL_STD_EXCEPT, 0); + testall1(-0.0, -0.0, ALL_STD_EXCEPT, 0); + + /* log(NaN) == NaN, no exceptions raised */ + testall0(NAN, NAN, ALL_STD_EXCEPT, 0); + testall1(NAN, NAN, ALL_STD_EXCEPT, 0); + + /* log(Inf) == Inf, no exceptions raised */ + testall0(INFINITY, INFINITY, ALL_STD_EXCEPT, 0); + testall1(INFINITY, INFINITY, ALL_STD_EXCEPT, 0); + + /* log(x) == NaN for x < 0, invalid exception raised */ + testall0(-INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID); + testall1(-INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID); + testall0(-1.0, NAN, ALL_STD_EXCEPT, FE_INVALID); + testall1(-1.5, NAN, ALL_STD_EXCEPT, FE_INVALID); + + /* log(0) == -Inf, divide-by-zero exception */ + testall0(0.0, -INFINITY, ALL_STD_EXCEPT & ~FE_INEXACT, FE_DIVBYZERO); + testall0(-0.0, -INFINITY, ALL_STD_EXCEPT & ~FE_INEXACT, FE_DIVBYZERO); + testall1(-1.0, -INFINITY, ALL_STD_EXCEPT & ~FE_INEXACT, FE_DIVBYZERO); +} + +void +run_log2_tests(void) +{ + int i; + + /* + * We should insist that log2() return exactly the correct + * result and not raise an inexact exception for powers of 2. + */ + feclearexcept(FE_ALL_EXCEPT); + for (i = FLT_MIN_EXP - FLT_MANT_DIG; i < FLT_MAX_EXP; i++) { + assert(log2f(ldexpf(1.0, i)) == i); + assert(fetestexcept(ALL_STD_EXCEPT) == 0); + } + for (i = DBL_MIN_EXP - DBL_MANT_DIG; i < DBL_MAX_EXP; i++) { + assert(log2(ldexp(1.0, i)) == i); + assert(fetestexcept(ALL_STD_EXCEPT) == 0); + } + for (i = LDBL_MIN_EXP - LDBL_MANT_DIG; i < LDBL_MAX_EXP; i++) { + assert(log2l(ldexpl(1.0, i)) == i); +#if 0 + /* XXX This test does not pass yet. */ + assert(fetestexcept(ALL_STD_EXCEPT) == 0); +#endif + } +} + +void +run_roundingmode_tests(void) +{ + + /* + * Corner cases in other rounding modes. + */ + fesetround(FE_DOWNWARD); + /* These are still positive per IEEE 754R */ +#if 0 + testall0(1.0, 0.0, ALL_STD_EXCEPT, 0); +#else + /* logl, log2l, and log10l don't pass yet. */ + test(log, 1.0, 0.0, ALL_STD_EXCEPT, 0); + test(logf, 1.0, 0.0, ALL_STD_EXCEPT, 0); + test(log2, 1.0, 0.0, ALL_STD_EXCEPT, 0); + test(log2f, 1.0, 0.0, ALL_STD_EXCEPT, 0); + test(log10, 1.0, 0.0, ALL_STD_EXCEPT, 0); + test(log10f, 1.0, 0.0, ALL_STD_EXCEPT, 0); +#endif + testall1(0.0, 0.0, ALL_STD_EXCEPT, 0); + fesetround(FE_TOWARDZERO); + testall0(1.0, 0.0, ALL_STD_EXCEPT, 0); + testall1(0.0, 0.0, ALL_STD_EXCEPT, 0); + + fesetround(FE_UPWARD); + testall0(1.0, 0.0, ALL_STD_EXCEPT, 0); + testall1(0.0, 0.0, ALL_STD_EXCEPT, 0); + /* log1p(-0.0) == -0.0 even when rounding upwards */ + testall1(-0.0, -0.0, ALL_STD_EXCEPT, 0); + + fesetround(FE_TONEAREST); +} + +void +run_accuracy_tests(void) +{ + static const struct { + float x; + long double log2x; + long double logex; + long double log10x; + } tests[] = { + { 0x1p-120 + 0x1p-140, + -1.19999998624139449158861798943319717e2L, + -8.31776607135195754708796206665656732e1L, + -3.61235990655024477716980559136055915e1L, + }, + { 1.0 - 0x1p-20, + -1.37586186296463416424364914705656460e-6L, + -9.53674771153890007250243736279163253e-7L, + -4.14175690642480911859354110516159131e-7L, }, + { 1.0 + 0x1p-20, + 1.37586055084113820105668028340371476e-6L, + 9.53673861659188233908415514963336144e-7L, + 4.14175295653950611453333571759200697e-7L }, + { 19.75, + 4.30378074817710292442728634194115348e0L, + 2.98315349134713087533848129856505779e0L, + 1.29556709996247903756734359702926363e0L }, + { 19.75 * 0x1p100, + 1.043037807481771029244272863419411534e2L, + 7.229787154734166181706169344438271459e1L, + 3.139856666636059855894123306947856631e1L }, + }; + int i; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + test_tol(log2, tests[i].x, tests[i].log2x, DBL_ULP()); + test_tol(log2f, tests[i].x, tests[i].log2x, FLT_ULP()); + test_tol(log2l, tests[i].x, tests[i].log2x, LDBL_ULP()); + test_tol(log, tests[i].x, tests[i].logex, DBL_ULP()); + test_tol(logf, tests[i].x, tests[i].logex, FLT_ULP()); + test_tol(logl, tests[i].x, tests[i].logex, LDBL_ULP()); + test_tol(log10, tests[i].x, tests[i].log10x, DBL_ULP()); + test_tol(log10f, tests[i].x, tests[i].log10x, FLT_ULP()); + test_tol(log10l, tests[i].x, tests[i].log10x, LDBL_ULP()); + if (tests[i].x >= 0.5) { + test_tol(log1p, tests[i].x - 1, tests[i].logex, + DBL_ULP()); + test_tol(log1pf, tests[i].x - 1, tests[i].logex, + FLT_ULP()); + test_tol(log1pl, tests[i].x - 1, tests[i].logex, + LDBL_ULP()); + } + } +} + +void +run_log1p_accuracy_tests(void) +{ + + test_tol(log1pf, 0x0.333333p0F, + 1.82321546859847114303367992804596800640e-1L, FLT_ULP()); + test_tol(log1p, 0x0.3333333333333p0, + 1.82321556793954589204283870982629267635e-1L, DBL_ULP()); + test_tol(log1pl, 0x0.33333333333333332p0L, + 1.82321556793954626202683007050468762914e-1L, LDBL_ULP()); + + test_tol(log1pf, -0x0.333333p0F, + -2.23143536413048672940940199918017467652e-1L, FLT_ULP()); + test_tol(log1p, -0x0.3333333333333p0, + -2.23143551314209700255143859052009022937e-1L, DBL_ULP()); + test_tol(log1pl, -0x0.33333333333333332p0L, + -2.23143551314209755752742563153765697950e-1L, LDBL_ULP()); +} + +int +main(int argc, char *argv[]) +{ + + printf("1..5\n"); + + run_generic_tests(); + printf("ok 1 - logarithm\n"); + + run_log2_tests(); + printf("ok 2 - logarithm\n"); + + run_roundingmode_tests(); + printf("ok 3 - logarithm\n"); + + run_accuracy_tests(); + printf("ok 4 - logarithm\n"); + + run_log1p_accuracy_tests(); + printf("ok 5 - logarithm\n"); + + return (0); +} diff --git a/lib/msun/tests/lrint_test.c b/lib/msun/tests/lrint_test.c new file mode 100644 index 0000000..ba099aa --- /dev/null +++ b/lib/msun/tests/lrint_test.c @@ -0,0 +1,149 @@ +/*- + * Copyright (c) 2005-2008 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Test for lrint(), lrintf(), llrint(), and llrintf(). + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <fenv.h> +#include <limits.h> +#include <math.h> +#include <stdio.h> + +#ifdef __i386__ +#include <ieeefp.h> +#endif + +/* + * XXX The volatile here is to avoid gcc's bogus constant folding and work + * around the lack of support for the FENV_ACCESS pragma. + */ +#define test(func, x, result, excepts) do { \ + volatile double _d = x; \ + assert(feclearexcept(FE_ALL_EXCEPT) == 0); \ + assert((func)(_d) == (result) || fetestexcept(FE_INVALID)); \ + assert(fetestexcept(FE_ALL_EXCEPT) == (excepts)); \ +} while (0) + +#define testall(x, result, excepts) do { \ + test(lrint, x, result, excepts); \ + test(lrintf, x, result, excepts); \ + test(lrintl, x, result, excepts); \ + test(llrint, x, result, excepts); \ + test(llrintf, x, result, excepts); \ + test(llrintl, x, result, excepts); \ +} while (0) + +#define IGNORE 0 + +#pragma STDC FENV_ACCESS ON + +void +run_tests(void) +{ + + assert(fesetround(FE_DOWNWARD) == 0); + testall(0.75, 0, FE_INEXACT); + testall(-0.5, -1, FE_INEXACT); + + assert(fesetround(FE_TONEAREST) == 0); + testall(0.0, 0, 0); + testall(0.25, 0, FE_INEXACT); + testall(0.5, 0, FE_INEXACT); + testall(-2.5, -2, FE_INEXACT); + testall(1.0, 1, 0); + testall(0x12345000p0, 0x12345000, 0); + testall(0x1234.fp0, 0x1235, FE_INEXACT); + testall(INFINITY, IGNORE, FE_INVALID); + testall(NAN, IGNORE, FE_INVALID); + +#if (LONG_MAX == 0x7fffffffl) + assert(fesetround(FE_UPWARD) == 0); + test(lrint, 0x7fffffff.8p0, IGNORE, FE_INVALID); + test(lrint, -0x80000000.4p0, -0x80000000l, FE_INEXACT); + + assert(fesetround(FE_DOWNWARD) == 0); + test(lrint, -0x80000000.8p0, IGNORE, FE_INVALID); + test(lrint, 0x80000000.0p0, IGNORE, FE_INVALID); + test(lrint, 0x7fffffff.4p0, 0x7fffffffl, FE_INEXACT); + test(lrintf, 0x80000000.0p0f, IGNORE, FE_INVALID); + test(lrintf, 0x7fffff80.0p0f, 0x7fffff80l, 0); + + assert(fesetround(FE_TOWARDZERO) == 0); + test(lrint, 0x7fffffff.8p0, 0x7fffffffl, FE_INEXACT); + test(lrint, -0x80000000.8p0, -0x80000000l, FE_INEXACT); + test(lrint, 0x80000000.0p0, IGNORE, FE_INVALID); + test(lrintf, 0x80000000.0p0f, IGNORE, FE_INVALID); + test(lrintf, 0x7fffff80.0p0f, 0x7fffff80l, 0); +#elif (LONG_MAX == 0x7fffffffffffffffll) + assert(fesetround(FE_TONEAREST) == 0); + test(lrint, 0x8000000000000000.0p0, IGNORE, FE_INVALID); + test(lrintf, 0x8000000000000000.0p0f, IGNORE, FE_INVALID); + test(lrint, 0x7ffffffffffffc00.0p0, 0x7ffffffffffffc00l, 0); + test(lrintf, 0x7fffff8000000000.0p0f, 0x7fffff8000000000l, 0); + test(lrint, -0x8000000000000800.0p0, IGNORE, FE_INVALID); + test(lrintf, -0x8000010000000000.0p0f, IGNORE, FE_INVALID); + test(lrint, -0x8000000000000000.0p0, -0x8000000000000000l, 0); + test(lrintf, -0x8000000000000000.0p0f, -0x8000000000000000l, 0); +#else +#error "Unsupported long size" +#endif + +#if (LLONG_MAX == 0x7fffffffffffffffLL) + assert(fesetround(FE_TONEAREST) == 0); + test(llrint, 0x8000000000000000.0p0, IGNORE, FE_INVALID); + test(llrintf, 0x8000000000000000.0p0f, IGNORE, FE_INVALID); + test(llrint, 0x7ffffffffffffc00.0p0, 0x7ffffffffffffc00ll, 0); + test(llrintf, 0x7fffff8000000000.0p0f, 0x7fffff8000000000ll, 0); + test(llrint, -0x8000000000000800.0p0, IGNORE, FE_INVALID); + test(llrintf, -0x8000010000000000.0p0f, IGNORE, FE_INVALID); + test(llrint, -0x8000000000000000.0p0, -0x8000000000000000ll, 0); + test(llrintf, -0x8000000000000000.0p0f, -0x8000000000000000ll, 0); +#else +#error "Unsupported long long size" +#endif +} + +int +main(int argc, char *argv[]) +{ + + printf("1..1\n"); + + run_tests(); +#ifdef __i386__ + fpsetprec(FP_PE); + run_tests(); +#endif + + printf("ok 1 - lrint\n"); + + return (0); +} diff --git a/lib/msun/tests/nan_test.c b/lib/msun/tests/nan_test.c new file mode 100644 index 0000000..c12926b --- /dev/null +++ b/lib/msun/tests/nan_test.c @@ -0,0 +1,122 @@ +/*- + * Copyright (C) 2007 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Test for nan(), nanf(), and nanl(). We also test that strtod("nan(...)") + * and sscanf("nan(...)", ...) work identically. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <fenv.h> +#include <float.h> +#include <locale.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void +testnan(const char *nan_format) +{ + char nan_str[128]; + char *end; + long double ald[4]; + double ad[4]; + float af[4]; + int i; + + snprintf(nan_str, sizeof(nan_str), "nan(%s)", nan_format); + for (i = 0; i < 4; i++) { + /* + * x86 has an 80-bit long double stored in 96 bits, + * so we need to initialize the memory for the memcmp() + * checks below to work. + */ + bzero(&af[i], sizeof(float)); + bzero(&ad[i], sizeof(double)); + bzero(&ald[i], sizeof(long double)); + + } + + af[0] = nanf(nan_format); + assert(isnan(af[0])); + af[1] = strtof(nan_str, &end); + assert(end == nan_str + strlen(nan_str)); + assert(sscanf(nan_str, "%e", &af[2]) == 1); + assert(memcmp(&af[0], &af[1], sizeof(float)) == 0); + assert(memcmp(&af[1], &af[2], sizeof(float)) == 0); + if (*nan_format == '\0') { + /* nanf("") == strtof("nan") */ + af[3] = strtof("nan", NULL); + assert(memcmp(&af[2], &af[3], sizeof(float)) == 0); + } + + ad[0] = nan(nan_format); + assert(isnan(ad[0])); + ad[1] = strtod(nan_str, &end); + assert(end == nan_str + strlen(nan_str)); + assert(sscanf(nan_str, "%le", &ad[2]) == 1); + assert(memcmp(&ad[0], &ad[1], sizeof(double)) == 0); + assert(memcmp(&ad[1], &ad[2], sizeof(double)) == 0); + if (*nan_format == '\0') { + /* nan("") == strtod("nan") */ + ad[3] = strtod("nan", NULL); + assert(memcmp(&ad[2], &ad[3], sizeof(double)) == 0); + } + + ald[0] = nanl(nan_format); + assert(isnan(ald[0])); + ald[1] = strtold(nan_str, &end); + assert(end == nan_str + strlen(nan_str)); + assert(sscanf(nan_str, "%Le", &ald[2]) == 1); + assert(memcmp(&ald[0], &ald[1], sizeof(long double)) == 0); + assert(memcmp(&ald[1], &ald[2], sizeof(long double)) == 0); + if (*nan_format == '\0') { + /* nanl("") == strtold("nan") */ + ald[3] = strtold("nan", NULL); + assert(memcmp(&ald[2], &ald[3], sizeof(long double)) == 0); + } +} + +int +main(int argc, char *argv[]) +{ + + printf("1..1\n"); + + /* Die if a signalling NaN is returned */ + feenableexcept(FE_INVALID); + + testnan("0x1234"); + testnan(""); + + printf("ok 1 - nan\n"); + + return (0); +} diff --git a/lib/msun/tests/nearbyint_test.c b/lib/msun/tests/nearbyint_test.c new file mode 100644 index 0000000..602ea2a --- /dev/null +++ b/lib/msun/tests/nearbyint_test.c @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 2010 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Tests for nearbyint{,f,l}() + * + * TODO: + * - adapt tests for rint(3) + * - tests for harder values (more mantissa bits than float) + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <fenv.h> +#include <math.h> +#include <stdio.h> + +#include "test-utils.h" + +static int testnum; + +static const int rmodes[] = { + FE_TONEAREST, FE_DOWNWARD, FE_UPWARD, FE_TOWARDZERO, +}; + +/* Make sure we're testing the library, not some broken compiler built-ins. */ +double (*libnearbyint)(double) = nearbyint; +float (*libnearbyintf)(float) = nearbyintf; +long double (*libnearbyintl)(long double) = nearbyintl; +#define nearbyintf libnearbyintf +#define nearbyint libnearbyint +#define nearbyintl libnearbyintl + +static const struct { + float in; + float out[3]; /* one answer per rounding mode except towardzero */ +} tests[] = { +/* input output (expected) */ + { 0.0, { 0.0, 0.0, 0.0 }}, + { 0.5, { 0.0, 0.0, 1.0 }}, + { M_PI, { 3.0, 3.0, 4.0 }}, + { 65536.5, { 65536, 65536, 65537 }}, + { INFINITY, { INFINITY, INFINITY, INFINITY }}, + { NAN, { NAN, NAN, NAN }}, +}; + +static const int ntests = sizeof(tests) / sizeof(tests[0]); + +/* Get the appropriate result for the current rounding mode. */ +static float +get_output(int testindex, int rmodeindex, int negative) +{ + double out; + + if (negative) { /* swap downwards and upwards if input is negative */ + if (rmodeindex == 1) + rmodeindex = 2; + else if (rmodeindex == 2) + rmodeindex = 1; + } + if (rmodeindex == 3) /* FE_TOWARDZERO uses the value for downwards */ + rmodeindex = 1; + out = tests[testindex].out[rmodeindex]; + return (negative ? -out : out); +} + +static void +test_nearby(int testindex) +{ + float in, out; + int i; + + for (i = 0; i < sizeof(rmodes) / sizeof(rmodes[0]); i++) { + fesetround(rmodes[i]); + feclearexcept(ALL_STD_EXCEPT); + + in = tests[testindex].in; + out = get_output(testindex, i, 0); + assert(fpequal(out, libnearbyintf(in))); + assert(fpequal(out, nearbyint(in))); + assert(fpequal(out, nearbyintl(in))); + assert(fetestexcept(ALL_STD_EXCEPT) == 0); + + in = -tests[testindex].in; + out = get_output(testindex, i, 1); + assert(fpequal(out, nearbyintf(in))); + assert(fpequal(out, nearbyint(in))); + assert(fpequal(out, nearbyintl(in))); + assert(fetestexcept(ALL_STD_EXCEPT) == 0); + } + + printf("ok %d\t\t# nearbyint(+%g)\n", testnum++, in); +} + +static void +test_modf(int testindex) +{ + float in, out; + float ipartf, ipart_expected; + double ipart; + long double ipartl; + int i; + + for (i = 0; i < sizeof(rmodes) / sizeof(rmodes[0]); i++) { + fesetround(rmodes[i]); + feclearexcept(ALL_STD_EXCEPT); + + in = tests[testindex].in; + ipart_expected = tests[testindex].out[1]; + out = copysignf( + isinf(ipart_expected) ? 0.0 : in - ipart_expected, in); + ipartl = ipart = ipartf = 42.0; + + assert(fpequal(out, modff(in, &ipartf))); + assert(fpequal(ipart_expected, ipartf)); + assert(fpequal(out, modf(in, &ipart))); + assert(fpequal(ipart_expected, ipart)); + assert(fpequal(out, modfl(in, &ipartl))); + assert(fpequal(ipart_expected, ipartl)); + assert(fetestexcept(ALL_STD_EXCEPT) == 0); + + in = -in; + ipart_expected = -ipart_expected; + out = -out; + ipartl = ipart = ipartf = 42.0; + assert(fpequal(out, modff(in, &ipartf))); + assert(fpequal(ipart_expected, ipartf)); + assert(fpequal(out, modf(in, &ipart))); + assert(fpequal(ipart_expected, ipart)); + assert(fpequal(out, modfl(in, &ipartl))); + assert(fpequal(ipart_expected, ipartl)); + assert(fetestexcept(ALL_STD_EXCEPT) == 0); + } + + printf("ok %d\t\t# modf(+%g)\n", testnum++, in); +} + +int +main(int argc, char *argv[]) +{ + int i; + + printf("1..%d\n", ntests * 2); + testnum = 1; + for (i = 0; i < ntests; i++) { + test_nearby(i); + test_modf(i); + } + + return (0); +} diff --git a/lib/msun/tests/next_test.c b/lib/msun/tests/next_test.c new file mode 100644 index 0000000..d16fa77 --- /dev/null +++ b/lib/msun/tests/next_test.c @@ -0,0 +1,265 @@ +/*- + * Copyright (c) 2005 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Test the correctness of nextafter{,f,l} and nexttoward{,f,l}. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <fenv.h> +#include <float.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef __i386__ +#include <ieeefp.h> +#endif + +#include "test-utils.h" + +#define test(exp, ans, ex) do { \ + double __ans = (ans); \ + feclearexcept(ALL_STD_EXCEPT); \ + _testl(#exp, __LINE__, (exp), __ans, (ex)); \ +} while (0) +#define testf(exp, ans, ex) do { \ + float __ans = (ans); \ + feclearexcept(ALL_STD_EXCEPT); \ + _testl(#exp, __LINE__, (exp), __ans, (ex)); \ +} while (0) +#define testl(exp, ans, ex) do { \ + long double __ans = (ans); \ + feclearexcept(ALL_STD_EXCEPT); \ + _testl(#exp, __LINE__, (exp), __ans, (ex)); \ +} while (0) +#define testboth(arg1, arg2, ans, ex, prec) do { \ + test##prec(nextafter##prec((arg1), (arg2)), (ans), (ex)); \ + test##prec(nexttoward##prec((arg1), (arg2)), (ans), (ex)); \ +} while (0) +#define testall(arg1, arg2, ans, ex) do { \ + testboth((arg1), (arg2), (ans), (ex), ); \ + testboth((arg1), (arg2), (ans), (ex), f); \ + testboth((arg1), (arg2), (ans), (ex), l); \ +} while (0) + +static void _testl(const char *, int, long double, long double, int); +static double idd(double); +static float idf(float); + +int +main(int argc, char *argv[]) +{ + static const int ex_under = FE_UNDERFLOW | FE_INEXACT; /* shorthand */ + static const int ex_over = FE_OVERFLOW | FE_INEXACT; + long double ldbl_small, ldbl_eps, ldbl_max; + + printf("1..5\n"); + +#ifdef __i386__ + fpsetprec(FP_PE); +#endif + /* + * We can't use a compile-time constant here because gcc on + * FreeBSD/i386 assumes long doubles are truncated to the + * double format. + */ + ldbl_small = ldexpl(1.0, LDBL_MIN_EXP - LDBL_MANT_DIG); + ldbl_eps = LDBL_EPSILON; + ldbl_max = ldexpl(1.0 - ldbl_eps / 2, LDBL_MAX_EXP); + + /* + * Special cases involving zeroes. + */ +#define ztest(prec) \ + test##prec(copysign##prec(1.0, nextafter##prec(0.0, -0.0)), -1.0, 0); \ + test##prec(copysign##prec(1.0, nextafter##prec(-0.0, 0.0)), 1.0, 0); \ + test##prec(copysign##prec(1.0, nexttoward##prec(0.0, -0.0)), -1.0, 0);\ + test##prec(copysign##prec(1.0, nexttoward##prec(-0.0, 0.0)), 1.0, 0) + + ztest(); + ztest(f); + ztest(l); +#undef ztest + +#define stest(next, eps, prec) \ + test##prec(next(-0.0, 42.0), eps, ex_under); \ + test##prec(next(0.0, -42.0), -eps, ex_under); \ + test##prec(next(0.0, INFINITY), eps, ex_under); \ + test##prec(next(-0.0, -INFINITY), -eps, ex_under) + + stest(nextafter, 0x1p-1074, ); + stest(nextafterf, 0x1p-149f, f); + stest(nextafterl, ldbl_small, l); + stest(nexttoward, 0x1p-1074, ); + stest(nexttowardf, 0x1p-149f, f); + stest(nexttowardl, ldbl_small, l); +#undef stest + + printf("ok 1 - next\n"); + + /* + * `x == y' and NaN tests + */ + testall(42.0, 42.0, 42.0, 0); + testall(-42.0, -42.0, -42.0, 0); + testall(INFINITY, INFINITY, INFINITY, 0); + testall(-INFINITY, -INFINITY, -INFINITY, 0); + testall(NAN, 42.0, NAN, 0); + testall(42.0, NAN, NAN, 0); + testall(NAN, NAN, NAN, 0); + + printf("ok 2 - next\n"); + + /* + * Tests where x is an ordinary normalized number + */ + testboth(1.0, 2.0, 1.0 + DBL_EPSILON, 0, ); + testboth(1.0, -INFINITY, 1.0 - DBL_EPSILON/2, 0, ); + testboth(1.0, 2.0, 1.0 + FLT_EPSILON, 0, f); + testboth(1.0, -INFINITY, 1.0 - FLT_EPSILON/2, 0, f); + testboth(1.0, 2.0, 1.0 + ldbl_eps, 0, l); + testboth(1.0, -INFINITY, 1.0 - ldbl_eps/2, 0, l); + + testboth(-1.0, 2.0, -1.0 + DBL_EPSILON/2, 0, ); + testboth(-1.0, -INFINITY, -1.0 - DBL_EPSILON, 0, ); + testboth(-1.0, 2.0, -1.0 + FLT_EPSILON/2, 0, f); + testboth(-1.0, -INFINITY, -1.0 - FLT_EPSILON, 0, f); + testboth(-1.0, 2.0, -1.0 + ldbl_eps/2, 0, l); + testboth(-1.0, -INFINITY, -1.0 - ldbl_eps, 0, l); + + /* Cases where nextafter(...) != nexttoward(...) */ + test(nexttoward(1.0, 1.0 + ldbl_eps), 1.0 + DBL_EPSILON, 0); + testf(nexttowardf(1.0, 1.0 + ldbl_eps), 1.0 + FLT_EPSILON, 0); + testl(nexttowardl(1.0, 1.0 + ldbl_eps), 1.0 + ldbl_eps, 0); + + printf("ok 3 - next\n"); + + /* + * Tests at word boundaries, normalization boundaries, etc. + */ + testboth(0x1.87654ffffffffp+0, INFINITY, 0x1.87655p+0, 0, ); + testboth(0x1.87655p+0, -INFINITY, 0x1.87654ffffffffp+0, 0, ); + testboth(0x1.fffffffffffffp+0, INFINITY, 0x1p1, 0, ); + testboth(0x1p1, -INFINITY, 0x1.fffffffffffffp+0, 0, ); + testboth(0x0.fffffffffffffp-1022, INFINITY, 0x1p-1022, 0, ); + testboth(0x1p-1022, -INFINITY, 0x0.fffffffffffffp-1022, ex_under, ); + + testboth(0x1.fffffep0f, INFINITY, 0x1p1, 0, f); + testboth(0x1p1, -INFINITY, 0x1.fffffep0f, 0, f); + testboth(0x0.fffffep-126f, INFINITY, 0x1p-126f, 0, f); + testboth(0x1p-126f, -INFINITY, 0x0.fffffep-126f, ex_under, f); + +#if LDBL_MANT_DIG == 53 + testboth(0x1.87654ffffffffp+0L, INFINITY, 0x1.87655p+0L, 0, l); + testboth(0x1.87655p+0L, -INFINITY, 0x1.87654ffffffffp+0L, 0, l); + testboth(0x1.fffffffffffffp+0L, INFINITY, 0x1p1L, 0, l); + testboth(0x1p1L, -INFINITY, 0x1.fffffffffffffp+0L, 0, l); + testboth(0x0.fffffffffffffp-1022L, INFINITY, 0x1p-1022L, 0, l); + testboth(0x1p-1022L, -INFINITY, 0x0.fffffffffffffp-1022L, ex_under, l); +#elif LDBL_MANT_DIG == 64 && !defined(__i386) + testboth(0x1.87654321fffffffep+0L, INFINITY, 0x1.87654322p+0L, 0, l); + testboth(0x1.87654322p+0L, -INFINITY, 0x1.87654321fffffffep+0L, 0, l); + testboth(0x1.fffffffffffffffep0L, INFINITY, 0x1p1L, 0, l); + testboth(0x1p1L, -INFINITY, 0x1.fffffffffffffffep0L, 0, l); + testboth(0x0.fffffffffffffffep-16382L, INFINITY, 0x1p-16382L, 0, l); + testboth(0x1p-16382L, -INFINITY, + 0x0.fffffffffffffffep-16382L, ex_under, l); +#elif LDBL_MANT_DIG == 113 + testboth(0x1.876543210987ffffffffffffffffp+0L, INFINITY, + 0x1.876543210988p+0, 0, l); + testboth(0x1.876543210988p+0L, -INFINITY, + 0x1.876543210987ffffffffffffffffp+0L, 0, l); + testboth(0x1.ffffffffffffffffffffffffffffp0L, INFINITY, 0x1p1L, 0, l); + testboth(0x1p1L, -INFINITY, 0x1.ffffffffffffffffffffffffffffp0L, 0, l); + testboth(0x0.ffffffffffffffffffffffffffffp-16382L, INFINITY, + 0x1p-16382L, 0, l); + testboth(0x1p-16382L, -INFINITY, + 0x0.ffffffffffffffffffffffffffffp-16382L, ex_under, l); +#endif + + printf("ok 4 - next\n"); + + /* + * Overflow tests + */ + test(idd(nextafter(DBL_MAX, INFINITY)), INFINITY, ex_over); + test(idd(nextafter(INFINITY, 0.0)), DBL_MAX, 0); + test(idd(nexttoward(DBL_MAX, DBL_MAX * 2.0L)), INFINITY, ex_over); +#if LDBL_MANT_DIG > 53 + test(idd(nexttoward(INFINITY, DBL_MAX * 2.0L)), DBL_MAX, 0); +#endif + + testf(idf(nextafterf(FLT_MAX, INFINITY)), INFINITY, ex_over); + testf(idf(nextafterf(INFINITY, 0.0)), FLT_MAX, 0); + testf(idf(nexttowardf(FLT_MAX, FLT_MAX * 2.0)), INFINITY, ex_over); + testf(idf(nexttowardf(INFINITY, FLT_MAX * 2.0)), FLT_MAX, 0); + + testboth(ldbl_max, INFINITY, INFINITY, ex_over, l); + testboth(INFINITY, 0.0, ldbl_max, 0, l); + + printf("ok 5 - next\n"); + + return (0); +} + +static void +_testl(const char *exp, int line, long double actual, long double expected, + int except) +{ + int actual_except; + + actual_except = fetestexcept(ALL_STD_EXCEPT); + if (!fpequal(actual, expected)) { + fprintf(stderr, "%d: %s returned %La, expecting %La\n", + line, exp, actual, expected); + abort(); + } + if (actual_except != except) { + fprintf(stderr, "%d: %s raised 0x%x, expecting 0x%x\n", + line, exp, actual_except, except); + abort(); + } +} + +/* + * The idd() and idf() routines ensure that doubles and floats are + * converted to their respective types instead of stored in the FPU + * with extra precision. + */ +static double +idd(double x) +{ + return (x); +} + +static float +idf(float x) +{ + return (x); +} diff --git a/lib/msun/tests/rem_test.c b/lib/msun/tests/rem_test.c new file mode 100644 index 0000000..36e3476 --- /dev/null +++ b/lib/msun/tests/rem_test.c @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2005-2008 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Test for remainder functions: remainder, remainderf, remainderl, + * remquo, remquof, and remquol. + * Missing tests: fmod, fmodf. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <float.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +static void test_invalid(long double, long double); +static void testl(long double, long double, long double, int); +static void testd(double, double, double, int); +static void testf(float, float, float, int); + +#define test(x, y, e_r, e_q) do { \ + testl(x, y, e_r, e_q); \ + testd(x, y, e_r, e_q); \ + testf(x, y, e_r, e_q); \ +} while (0) + +int +main(int argc, char *argv[]) +{ + + printf("1..3\n"); + + test_invalid(0.0, 0.0); + test_invalid(1.0, 0.0); + test_invalid(INFINITY, 0.0); + test_invalid(INFINITY, 1.0); + test_invalid(-INFINITY, 1.0); + test_invalid(NAN, 1.0); + test_invalid(1.0, NAN); + + test(4, 4, 0, 1); + test(0, 3.0, 0, 0); + testd(0x1p-1074, 1, 0x1p-1074, 0); + testf(0x1p-149, 1, 0x1p-149, 0); + test(3.0, 4, -1, 1); + test(3.0, -4, -1, -1); + testd(275 * 1193040, 275, 0, 1193040); + test(4.5 * 7.5, 4.5, -2.25, 8); /* we should get the even one */ + testf(0x1.9044f6p-1, 0x1.ce662ep-1, -0x1.f109cp-4, 1); +#if LDBL_MANT_DIG > 53 + testl(-0x1.23456789abcdefp-2000L, 0x1.fedcba987654321p-2000L, + 0x1.b72ea61d950c862p-2001L, -1); +#endif + + printf("ok 1 - rem\n"); + + /* + * The actual quotient here is 864062210.50000003..., but + * double-precision division gets -8.64062210.5, which rounds + * the wrong way. This test ensures that remquo() is smart + * enough to get the low-order bit right. + */ + testd(-0x1.98260f22fc6dep-302, 0x1.fb3167c430a13p-332, + 0x1.fb3165b82de72p-333, -864062211); + /* Even harder cases with greater exponent separation */ + test(0x1.fp100, 0x1.ep-40, -0x1.cp-41, 143165577); + testd(-0x1.abcdefp120, 0x1.87654321p-120, + -0x1.69c78ec4p-121, -63816414); + + printf("ok 2 - rem\n"); + + test(0x1.66666cp+120, 0x1p+71, 0.0, 1476395008); + testd(-0x1.0000000000003p+0, 0x1.0000000000003p+0, -0.0, -1); + testl(-0x1.0000000000003p+0, 0x1.0000000000003p+0, -0.0, -1); + testd(-0x1.0000000000001p-749, 0x1.4p-1072, 0x1p-1074, -1288490189); + testl(-0x1.0000000000001p-749, 0x1.4p-1072, 0x1p-1074, -1288490189); + + printf("ok 3 - rem\n"); + + return (0); +} + +static void +test_invalid(long double x, long double y) +{ + int q; + + q = 0xdeadbeef; + + assert(isnan(remainder(x, y))); + assert(isnan(remquo(x, y, &q))); +#ifdef STRICT + assert(q == 0xdeadbeef); +#endif + + assert(isnan(remainderf(x, y))); + assert(isnan(remquof(x, y, &q))); +#ifdef STRICT + assert(q == 0xdeadbeef); +#endif + + assert(isnan(remainderl(x, y))); + assert(isnan(remquol(x, y, &q))); +#ifdef STRICT + assert(q == 0xdeadbeef); +#endif +} + +/* 0x012345 ==> 0x01ffff */ +static inline int +mask(int x) +{ + return ((unsigned)~0 >> (32 - fls(x))); +} + +static void +testl(long double x, long double y, long double expected_rem, int expected_quo) +{ + int q; + long double rem; + + q = random(); + rem = remainderl(x, y); + assert(rem == expected_rem); + assert(!signbit(rem) == !signbit(expected_rem)); + rem = remquol(x, y, &q); + assert(rem == expected_rem); + assert(!signbit(rem) == !signbit(expected_rem)); + assert((q ^ expected_quo) >= 0); /* sign(q) == sign(expected_quo) */ + assert((q & 0x7) == (expected_quo & 0x7)); + if (q != 0) { + assert((q > 0) ^ !(expected_quo > 0)); + q = abs(q); + assert(q == (abs(expected_quo) & mask(q))); + } +} + +static void +testd(double x, double y, double expected_rem, int expected_quo) +{ + int q; + double rem; + + q = random(); + rem = remainder(x, y); + assert(rem == expected_rem); + assert(!signbit(rem) == !signbit(expected_rem)); + rem = remquo(x, y, &q); + assert(rem == expected_rem); + assert(!signbit(rem) == !signbit(expected_rem)); + assert((q ^ expected_quo) >= 0); /* sign(q) == sign(expected_quo) */ + assert((q & 0x7) == (expected_quo & 0x7)); + if (q != 0) { + assert((q > 0) ^ !(expected_quo > 0)); + q = abs(q); + assert(q == (abs(expected_quo) & mask(q))); + } +} + +static void +testf(float x, float y, float expected_rem, int expected_quo) +{ + int q; + float rem; + + q = random(); + rem = remainderf(x, y); + assert(rem == expected_rem); + assert(!signbit(rem) == !signbit(expected_rem)); + rem = remquof(x, y, &q); + assert(rem == expected_rem); + assert(!signbit(rem) == !signbit(expected_rem)); + assert((q ^ expected_quo) >= 0); /* sign(q) == sign(expected_quo) */ + assert((q & 0x7) == (expected_quo & 0x7)); + if (q != 0) { + assert((q > 0) ^ !(expected_quo > 0)); + q = abs(q); + assert((q & mask(q)) == (abs(expected_quo) & mask(q))); + } +} diff --git a/lib/msun/tests/trig_test.c b/lib/msun/tests/trig_test.c new file mode 100644 index 0000000..1dcce1f --- /dev/null +++ b/lib/msun/tests/trig_test.c @@ -0,0 +1,280 @@ +/*- + * Copyright (c) 2008 David Schultz <das@FreeBSD.org> + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Tests for corner cases in trigonometric functions. Some accuracy tests + * are included as well, but these are very basic sanity checks, not + * intended to be comprehensive. + * + * The program for generating representable numbers near multiples of pi is + * available at http://www.cs.berkeley.edu/~wkahan/testpi/ . + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#include <assert.h> +#include <fenv.h> +#include <float.h> +#include <math.h> +#include <stdio.h> + +#include "test-utils.h" + +#pragma STDC FENV_ACCESS ON + +/* + * Test that a function returns the correct value and sets the + * exception flags correctly. The exceptmask specifies which + * exceptions we should check. We need to be lenient for several + * reasons, but mainly because on some architectures it's impossible + * to raise FE_OVERFLOW without raising FE_INEXACT. + * + * These are macros instead of functions so that assert provides more + * meaningful error messages. + * + * XXX The volatile here is to avoid gcc's bogus constant folding and work + * around the lack of support for the FENV_ACCESS pragma. + */ +#define test(func, x, result, exceptmask, excepts) do { \ + volatile long double _d = x; \ + assert(feclearexcept(FE_ALL_EXCEPT) == 0); \ + assert(fpequal((func)(_d), (result))); \ + assert(((void)(func), fetestexcept(exceptmask) == (excepts))); \ +} while (0) + +#define testall(prefix, x, result, exceptmask, excepts) do { \ + test(prefix, x, (double)result, exceptmask, excepts); \ + test(prefix##f, x, (float)result, exceptmask, excepts); \ + test(prefix##l, x, result, exceptmask, excepts); \ +} while (0) + +#define testdf(prefix, x, result, exceptmask, excepts) do { \ + test(prefix, x, (double)result, exceptmask, excepts); \ + test(prefix##f, x, (float)result, exceptmask, excepts); \ +} while (0) + +/* + * Test special cases in sin(), cos(), and tan(). + */ +static void +run_special_tests(void) +{ + + /* Values at 0 should be exact. */ + testall(tan, 0.0, 0.0, ALL_STD_EXCEPT, 0); + testall(tan, -0.0, -0.0, ALL_STD_EXCEPT, 0); + testall(cos, 0.0, 1.0, ALL_STD_EXCEPT, 0); + testall(cos, -0.0, 1.0, ALL_STD_EXCEPT, 0); + testall(sin, 0.0, 0.0, ALL_STD_EXCEPT, 0); + testall(sin, -0.0, -0.0, ALL_STD_EXCEPT, 0); + + /* func(+-Inf) == NaN */ + testall(tan, INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID); + testall(sin, INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID); + testall(cos, INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID); + testall(tan, -INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID); + testall(sin, -INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID); + testall(cos, -INFINITY, NAN, ALL_STD_EXCEPT, FE_INVALID); + + /* func(NaN) == NaN */ + testall(tan, NAN, NAN, ALL_STD_EXCEPT, 0); + testall(sin, NAN, NAN, ALL_STD_EXCEPT, 0); + testall(cos, NAN, NAN, ALL_STD_EXCEPT, 0); +} + +/* + * Tests to ensure argument reduction for large arguments is accurate. + */ +static void +run_reduction_tests(void) +{ + /* floats very close to odd multiples of pi */ + static const float f_pi_odd[] = { + 85563208.0f, + 43998769152.0f, + 9.2763667655669323e+25f, + 1.5458357838905804e+29f, + }; + /* doubles very close to odd multiples of pi */ + static const double d_pi_odd[] = { + 3.1415926535897931, + 91.106186954104004, + 642615.9188844458, + 3397346.5699258847, + 6134899525417045.0, + 3.0213551960457761e+43, + 1.2646209897993783e+295, + 6.2083625380677099e+307, + }; + /* long doubles very close to odd multiples of pi */ +#if LDBL_MANT_DIG == 64 + static const long double ld_pi_odd[] = { + 1.1891886960373841596e+101L, + 1.07999475322710967206e+2087L, + 6.522151627890431836e+2147L, + 8.9368974898260328229e+2484L, + 9.2961044110572205863e+2555L, + 4.90208421886578286e+3189L, + 1.5275546401232615884e+3317L, + 1.7227465626338900093e+3565L, + 2.4160090594000745334e+3808L, + 9.8477555741888350649e+4314L, + 1.6061597222105160737e+4326L, + }; +#elif LDBL_MANT_DIG == 113 + static const long double ld_pi_odd[] = { + /* XXX */ + }; +#endif + + int i; + + for (i = 0; i < nitems(f_pi_odd); i++) { + assert(fabs(sinf(f_pi_odd[i])) < FLT_EPSILON); + assert(cosf(f_pi_odd[i]) == -1.0); + assert(fabs(tan(f_pi_odd[i])) < FLT_EPSILON); + + assert(fabs(sinf(-f_pi_odd[i])) < FLT_EPSILON); + assert(cosf(-f_pi_odd[i]) == -1.0); + assert(fabs(tanf(-f_pi_odd[i])) < FLT_EPSILON); + + assert(fabs(sinf(f_pi_odd[i] * 2)) < FLT_EPSILON); + assert(cosf(f_pi_odd[i] * 2) == 1.0); + assert(fabs(tanf(f_pi_odd[i] * 2)) < FLT_EPSILON); + + assert(fabs(sinf(-f_pi_odd[i] * 2)) < FLT_EPSILON); + assert(cosf(-f_pi_odd[i] * 2) == 1.0); + assert(fabs(tanf(-f_pi_odd[i] * 2)) < FLT_EPSILON); + } + + for (i = 0; i < nitems(d_pi_odd); i++) { + assert(fabs(sin(d_pi_odd[i])) < 2 * DBL_EPSILON); + assert(cos(d_pi_odd[i]) == -1.0); + assert(fabs(tan(d_pi_odd[i])) < 2 * DBL_EPSILON); + + assert(fabs(sin(-d_pi_odd[i])) < 2 * DBL_EPSILON); + assert(cos(-d_pi_odd[i]) == -1.0); + assert(fabs(tan(-d_pi_odd[i])) < 2 * DBL_EPSILON); + + assert(fabs(sin(d_pi_odd[i] * 2)) < 2 * DBL_EPSILON); + assert(cos(d_pi_odd[i] * 2) == 1.0); + assert(fabs(tan(d_pi_odd[i] * 2)) < 2 * DBL_EPSILON); + + assert(fabs(sin(-d_pi_odd[i] * 2)) < 2 * DBL_EPSILON); + assert(cos(-d_pi_odd[i] * 2) == 1.0); + assert(fabs(tan(-d_pi_odd[i] * 2)) < 2 * DBL_EPSILON); + } + +#if LDBL_MANT_DIG > 53 + for (i = 0; i < nitems(ld_pi_odd); i++) { + assert(fabsl(sinl(ld_pi_odd[i])) < LDBL_EPSILON); + assert(cosl(ld_pi_odd[i]) == -1.0); + assert(fabsl(tanl(ld_pi_odd[i])) < LDBL_EPSILON); + + assert(fabsl(sinl(-ld_pi_odd[i])) < LDBL_EPSILON); + assert(cosl(-ld_pi_odd[i]) == -1.0); + assert(fabsl(tanl(-ld_pi_odd[i])) < LDBL_EPSILON); + + assert(fabsl(sinl(ld_pi_odd[i] * 2)) < LDBL_EPSILON); + assert(cosl(ld_pi_odd[i] * 2) == 1.0); + assert(fabsl(tanl(ld_pi_odd[i] * 2)) < LDBL_EPSILON); + + assert(fabsl(sinl(-ld_pi_odd[i] * 2)) < LDBL_EPSILON); + assert(cosl(-ld_pi_odd[i] * 2) == 1.0); + assert(fabsl(tanl(-ld_pi_odd[i] * 2)) < LDBL_EPSILON); + } +#endif +} + +/* + * Tests the accuracy of these functions over the primary range. + */ +static void +run_accuracy_tests(void) +{ + + /* For small args, sin(x) = tan(x) = x, and cos(x) = 1. */ + testall(sin, 0xd.50ee515fe4aea16p-114L, 0xd.50ee515fe4aea16p-114L, + ALL_STD_EXCEPT, FE_INEXACT); + testall(tan, 0xd.50ee515fe4aea16p-114L, 0xd.50ee515fe4aea16p-114L, + ALL_STD_EXCEPT, FE_INEXACT); + testall(cos, 0xd.50ee515fe4aea16p-114L, 1.0, + ALL_STD_EXCEPT, FE_INEXACT); + + /* + * These tests should pass for f32, d64, and ld80 as long as + * the error is <= 0.75 ulp (round to nearest) + */ +#if LDBL_MANT_DIG <= 64 +#define testacc testall +#else +#define testacc testdf +#endif + testacc(sin, 0.17255452780841205174L, 0.17169949801444412683L, + ALL_STD_EXCEPT, FE_INEXACT); + testacc(sin, -0.75431944555904520893L, -0.68479288156557286353L, + ALL_STD_EXCEPT, FE_INEXACT); + testacc(cos, 0.70556358769838947292L, 0.76124620693117771850L, + ALL_STD_EXCEPT, FE_INEXACT); + testacc(cos, -0.34061437849088045332L, 0.94254960031831729956L, + ALL_STD_EXCEPT, FE_INEXACT); + testacc(tan, -0.15862817413325692897L, -0.15997221861309522115L, + ALL_STD_EXCEPT, FE_INEXACT); + testacc(tan, 0.38374784931303813530L, 0.40376500259976759951L, + ALL_STD_EXCEPT, FE_INEXACT); + + /* + * XXX missing: + * - tests for ld128 + * - tests for other rounding modes (probably won't pass for now) + * - tests for large numbers that get reduced to hi+lo with lo!=0 + */ +} + +int +main(int argc, char *argv[]) +{ + + printf("1..3\n"); + + run_special_tests(); + printf("ok 1 - trig\n"); + +#ifndef __i386__ + run_reduction_tests(); +#endif + printf("ok 2 - trig\n"); + +#ifndef __i386__ + run_accuracy_tests(); +#endif + printf("ok 3 - trig\n"); + + return (0); +} |