forked from Imagelibrary/rtems
Check that the state of rand() and lrand48() is thread-specific, that they are properly initialized, and return the expected sequence of pseudo-random numbers for default seed values.
585 lines
13 KiB
C
585 lines
13 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/*
|
|
* Copyright (c) 2014, 2022 embedded brains GmbH. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#include <sys/reent.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/console.h>
|
|
#include <rtems/imfs.h>
|
|
#include <rtems/libcsupport.h>
|
|
#include <rtems/sysinit.h>
|
|
|
|
#include "tmacros.h"
|
|
|
|
const char rtems_test_name[] = "NEWLIB 1";
|
|
|
|
static const char stdio_file_path[] = "/stdio-file";
|
|
|
|
static const char non_stdio_file_path[] = "/non-stdio-file";
|
|
|
|
typedef enum {
|
|
INIT,
|
|
OPEN,
|
|
WRITTEN,
|
|
CLOSED
|
|
} test_state;
|
|
|
|
typedef struct {
|
|
rtems_id main_task_id;
|
|
rtems_id worker_task_id;
|
|
test_state current;
|
|
FILE *non_stdio_file;
|
|
int non_stdio_fd;
|
|
} test_context;
|
|
|
|
static test_context test_instance;
|
|
|
|
static void wake_up_main(const test_context *ctx)
|
|
{
|
|
rtems_status_code sc;
|
|
|
|
sc = rtems_event_transient_send(ctx->main_task_id);
|
|
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
|
}
|
|
|
|
static void wait(void)
|
|
{
|
|
rtems_status_code sc;
|
|
|
|
sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
|
|
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
|
}
|
|
|
|
/*
|
|
* Check that rand() is properly initialized and returns the expected sequence
|
|
* for default seed values. A call to rand() without any previous call to
|
|
* srand() generates the same sequence as when srand() is first called with a
|
|
* seed value of 1.
|
|
*/
|
|
static void test_rand(void)
|
|
{
|
|
int rv;
|
|
|
|
rv = rand();
|
|
rtems_test_assert(rv == 1481765933);
|
|
rv = rand();
|
|
rtems_test_assert(rv == 1085377743);
|
|
rv = rand();
|
|
rtems_test_assert(rv == 1270216262);
|
|
|
|
srand(1);
|
|
rv = rand();
|
|
rtems_test_assert(rv == 1481765933);
|
|
rv = rand();
|
|
rtems_test_assert(rv == 1085377743);
|
|
rv = rand();
|
|
rtems_test_assert(rv == 1270216262);
|
|
}
|
|
|
|
/*
|
|
* Check that lrand48() is properly initialized and returns the expected
|
|
* sequence for default seed values. A call to lrand48() without any previous
|
|
* call to srand48() uses default constant initializer values set in the _seed
|
|
* member of struct _rand48.
|
|
*/
|
|
static void test_lrand48(void)
|
|
{
|
|
long rv;
|
|
|
|
rv = lrand48();
|
|
rtems_test_assert(rv == 851401618);
|
|
rv = lrand48();
|
|
rtems_test_assert(rv == 1804928587);
|
|
rv = lrand48();
|
|
rtems_test_assert(rv == 758783491);
|
|
}
|
|
|
|
static void stdio_file_worker(rtems_task_argument arg)
|
|
{
|
|
test_context *ctx = &test_instance;
|
|
struct _reent *reent = _REENT;
|
|
FILE *output;
|
|
char buf[1] = { 'x' };
|
|
size_t n;
|
|
|
|
test_rand();
|
|
test_lrand48();
|
|
|
|
rtems_test_assert(reent->__cleanup == NULL);
|
|
|
|
output = stdout = fopen(&stdio_file_path[0], "r+");
|
|
rtems_test_assert(stdout != NULL);
|
|
|
|
/*
|
|
* Check newlib's __sinit does not touch our assigned file pointer.
|
|
*/
|
|
rtems_test_assert(reent->__cleanup == NULL);
|
|
rtems_test_assert(fflush(stdout) == 0);
|
|
rtems_test_assert(reent->__cleanup != NULL);
|
|
rtems_test_assert(stdout == output);
|
|
|
|
n = fwrite(&buf[0], sizeof(buf), 1, stdout);
|
|
rtems_test_assert(n == 1);
|
|
|
|
rtems_test_assert(ctx->current == OPEN);
|
|
|
|
wake_up_main(ctx);
|
|
|
|
rtems_test_assert(0);
|
|
}
|
|
|
|
static void non_stdio_file_worker(rtems_task_argument arg)
|
|
{
|
|
test_context *ctx = &test_instance;
|
|
FILE *fp;
|
|
char buf[1] = { 'y' };
|
|
size_t n;
|
|
int fd;
|
|
|
|
fp = ctx->non_stdio_file = fopen(&non_stdio_file_path[0], "w");
|
|
rtems_test_assert(fp != NULL);
|
|
|
|
/* Get file descriptor of new global file stream, store it in text context */
|
|
fd = fileno(fp);
|
|
rtems_test_assert(fd != -1);
|
|
ctx->non_stdio_fd = fd;
|
|
|
|
n = fwrite(&buf[0], sizeof(buf), 1, fp);
|
|
rtems_test_assert(n == 1);
|
|
|
|
wake_up_main(ctx);
|
|
|
|
rtems_test_assert(0);
|
|
}
|
|
|
|
static int handler_open(
|
|
rtems_libio_t *iop,
|
|
const char *path,
|
|
int oflag,
|
|
mode_t mode
|
|
)
|
|
{
|
|
test_context *ctx = &test_instance;
|
|
|
|
rtems_test_assert(ctx->current == INIT);
|
|
ctx->current = OPEN;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_close(
|
|
rtems_libio_t *iop
|
|
)
|
|
{
|
|
test_context *ctx = &test_instance;
|
|
|
|
rtems_test_assert(ctx->current == WRITTEN);
|
|
ctx->current = CLOSED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t handler_read(
|
|
rtems_libio_t *iop,
|
|
void *buffer,
|
|
size_t count
|
|
)
|
|
{
|
|
rtems_test_assert(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t handler_write(
|
|
rtems_libio_t *iop,
|
|
const void *buffer,
|
|
size_t count
|
|
)
|
|
{
|
|
test_context *ctx = &test_instance;
|
|
|
|
rtems_test_assert(ctx->current == OPEN);
|
|
ctx->current = WRITTEN;
|
|
|
|
iop->offset += count;
|
|
|
|
return (ssize_t) count;
|
|
}
|
|
|
|
static int handler_ioctl(
|
|
rtems_libio_t *iop,
|
|
ioctl_command_t request,
|
|
void *buffer
|
|
)
|
|
{
|
|
rtems_test_assert(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static off_t handler_lseek(
|
|
rtems_libio_t *iop,
|
|
off_t length,
|
|
int whence
|
|
)
|
|
{
|
|
rtems_test_assert(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_ftruncate(
|
|
rtems_libio_t *iop,
|
|
off_t length
|
|
)
|
|
{
|
|
rtems_test_assert(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_fsync(
|
|
rtems_libio_t *iop
|
|
)
|
|
{
|
|
rtems_test_assert(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_fdatasync(
|
|
rtems_libio_t *iop
|
|
)
|
|
{
|
|
rtems_test_assert(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handler_fcntl(
|
|
rtems_libio_t *iop,
|
|
int cmd
|
|
)
|
|
{
|
|
rtems_test_assert(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t handler_readv(
|
|
rtems_libio_t *iop,
|
|
const struct iovec *iov,
|
|
int iovcnt,
|
|
ssize_t total
|
|
)
|
|
{
|
|
rtems_test_assert(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t handler_writev(
|
|
rtems_libio_t *iop,
|
|
const struct iovec *iov,
|
|
int iovcnt,
|
|
ssize_t total
|
|
)
|
|
{
|
|
rtems_test_assert(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const rtems_filesystem_file_handlers_r node_handlers = {
|
|
.open_h = handler_open,
|
|
.close_h = handler_close,
|
|
.read_h = handler_read,
|
|
.write_h = handler_write,
|
|
.ioctl_h = handler_ioctl,
|
|
.lseek_h = handler_lseek,
|
|
.fstat_h = rtems_filesystem_default_fstat,
|
|
.ftruncate_h = handler_ftruncate,
|
|
.fsync_h = handler_fsync,
|
|
.fdatasync_h = handler_fdatasync,
|
|
.fcntl_h = handler_fcntl,
|
|
.readv_h = handler_readv,
|
|
.writev_h = handler_writev
|
|
};
|
|
|
|
static const IMFS_node_control node_control = IMFS_GENERIC_INITIALIZER(
|
|
&node_handlers,
|
|
IMFS_node_initialize_default,
|
|
IMFS_node_destroy_default
|
|
);
|
|
|
|
static void create_and_run_worker(test_context *ctx, rtems_task_entry entry)
|
|
{
|
|
rtems_status_code sc;
|
|
|
|
sc = rtems_task_create(
|
|
rtems_build_name('W', 'O', 'R', 'K'),
|
|
2,
|
|
RTEMS_MINIMUM_STACK_SIZE,
|
|
RTEMS_DEFAULT_MODES,
|
|
RTEMS_DEFAULT_ATTRIBUTES,
|
|
&ctx->worker_task_id
|
|
);
|
|
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
|
|
|
sc = rtems_task_start(ctx->worker_task_id, entry, 0);
|
|
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
|
|
|
wait();
|
|
|
|
sc = rtems_task_delete(ctx->worker_task_id);
|
|
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
|
}
|
|
|
|
/*
|
|
* Check that a FILE opened by a task and assigned to a stdio stream is closed
|
|
* during thread termination. Ensure that resources are returned to the system.
|
|
*/
|
|
static void test_stdio_file(test_context *ctx)
|
|
{
|
|
int rv;
|
|
rtems_resource_snapshot snapshot;
|
|
|
|
rtems_resource_snapshot_take(&snapshot);
|
|
|
|
rv = IMFS_make_generic_node(
|
|
&stdio_file_path[0],
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
|
|
&node_control,
|
|
NULL
|
|
);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
create_and_run_worker(ctx, stdio_file_worker);
|
|
|
|
rv = unlink(&stdio_file_path[0]);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
|
|
}
|
|
|
|
/*
|
|
* Open a global FILE object from a task but do not assign it to a stdio
|
|
* stream. The FILE is not closed upon thread termination.
|
|
*/
|
|
static void test_non_stdio_file(test_context *ctx)
|
|
{
|
|
create_and_run_worker(ctx, non_stdio_file_worker);
|
|
}
|
|
|
|
/*
|
|
* This exit handler will be called last among the functions registered with
|
|
* atexit(). Check that stdio file descriptors are closed. The Newlib cleanup
|
|
* handler has not yet run, so the stdio FILE objects themselves are still
|
|
* open.
|
|
*/
|
|
static void check_after_libio_exit(void)
|
|
{
|
|
test_context *ctx = &test_instance;
|
|
struct stat unused;
|
|
int rv;
|
|
|
|
errno = 0;
|
|
rv = fstat(0, &unused);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EBADF);
|
|
|
|
errno = 0;
|
|
rv = fstat(1, &unused);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EBADF);
|
|
|
|
errno = 0;
|
|
rv = fstat(2, &unused);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EBADF);
|
|
|
|
rtems_test_assert(stdin->_flags != 0);
|
|
rtems_test_assert(stdout->_flags != 0);
|
|
rtems_test_assert(stderr->_flags != 0);
|
|
|
|
/*
|
|
* The non-stdio file and its file descriptor should be still open at this
|
|
* point.
|
|
*/
|
|
rv = fstat(ctx->non_stdio_fd, &unused);
|
|
rtems_test_assert(rv == 0);
|
|
rtems_test_assert(ctx->non_stdio_file->_flags != 0);
|
|
}
|
|
|
|
static void register_exit_handler_before_libio_exit(void)
|
|
{
|
|
int rv;
|
|
|
|
rv = atexit(check_after_libio_exit);
|
|
rtems_test_assert(rv == 0);
|
|
}
|
|
|
|
/*
|
|
* Register the exit handler before rtems_libio_exit() so that
|
|
* check_after_libio_exit() is called after rtems_libio_exit(). The exit()
|
|
* handlers are in a LIFO list.
|
|
*/
|
|
RTEMS_SYSINIT_ITEM(register_exit_handler_before_libio_exit,
|
|
RTEMS_SYSINIT_STD_FILE_DESCRIPTORS, RTEMS_SYSINIT_ORDER_FIRST);
|
|
|
|
/*
|
|
* At this point, neither the functions registered with atexit() nor the Newlib
|
|
* cleanup procedures have been called. Therefore, stdio file descriptors
|
|
* should be open and stdio FILE object flags should be non-zero.
|
|
*/
|
|
static void test_exit_handling(test_context *ctx)
|
|
{
|
|
struct stat unused;
|
|
int rv;
|
|
|
|
rv = fstat(0, &unused);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
rv = fstat(1, &unused);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
rv = fstat(2, &unused);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
rtems_test_assert(stdin->_flags != 0);
|
|
rtems_test_assert(stdout->_flags != 0);
|
|
rtems_test_assert(stderr->_flags != 0);
|
|
|
|
/*
|
|
* The file descriptor of the non-stdio file should still be open; the FILE
|
|
* object flags should still be non-zero.
|
|
*/
|
|
rv = fstat(ctx->non_stdio_fd, &unused);
|
|
rtems_test_assert(rv == 0);
|
|
rtems_test_assert(ctx->non_stdio_file->_flags != 0);
|
|
|
|
/* Run exit handlers and Newlib cleanup procedures */
|
|
exit(0);
|
|
}
|
|
|
|
static void Init(rtems_task_argument arg)
|
|
{
|
|
test_context *ctx = &test_instance;
|
|
FILE *file;
|
|
int rv;
|
|
|
|
TEST_BEGIN();
|
|
test_rand();
|
|
test_lrand48();
|
|
|
|
ctx->main_task_id = rtems_task_self();
|
|
|
|
/* Fill dynamic file pool in Newlib */
|
|
file = fopen(CONSOLE_DEVICE_NAME, "r+");
|
|
rtems_test_assert(file != NULL);
|
|
rv = fclose(file);
|
|
rtems_test_assert(rv == 0);
|
|
|
|
test_stdio_file(ctx);
|
|
test_non_stdio_file(ctx);
|
|
test_exit_handling(ctx);
|
|
}
|
|
|
|
static void fatal_extension(
|
|
rtems_fatal_source source,
|
|
bool always_set_to_false,
|
|
rtems_fatal_code error
|
|
)
|
|
{
|
|
if (
|
|
source == RTEMS_FATAL_SOURCE_EXIT
|
|
&& !always_set_to_false
|
|
&& error == 0
|
|
) {
|
|
/*
|
|
* Final conditions check after exit handlers and Newlib cleanup procedures
|
|
* have run. File descriptors and FILE objects themselves are closed.
|
|
*/
|
|
struct stat unused;
|
|
int rv;
|
|
test_context *ctx = &test_instance;
|
|
|
|
errno = 0;
|
|
rv = fstat(0, &unused);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EBADF);
|
|
|
|
errno = 0;
|
|
rv = fstat(1, &unused);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EBADF);
|
|
|
|
errno = 0;
|
|
rv = fstat(2, &unused);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EBADF);
|
|
|
|
rtems_test_assert(stdin->_flags == 0);
|
|
rtems_test_assert(stdout->_flags == 0);
|
|
rtems_test_assert(stderr->_flags == 0);
|
|
|
|
/* The non-stdio file and its file descriptor should be closed */
|
|
errno = 0;
|
|
rv = fstat(ctx->non_stdio_fd, &unused);
|
|
rtems_test_assert(rv == -1);
|
|
rtems_test_assert(errno == EBADF);
|
|
rtems_test_assert(ctx->non_stdio_file->_flags == 0);
|
|
|
|
TEST_END();
|
|
}
|
|
}
|
|
|
|
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
|
|
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
|
|
|
|
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 5
|
|
|
|
#define CONFIGURE_MAXIMUM_TASKS 2
|
|
|
|
#define CONFIGURE_INITIAL_EXTENSIONS \
|
|
{ .fatal = fatal_extension }, \
|
|
RTEMS_TEST_INITIAL_EXTENSION
|
|
|
|
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
|
|
|
|
#define CONFIGURE_INIT
|
|
|
|
#include <rtems/confdefs.h>
|