/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. 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.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* .
*/
#include
#include
#include
#include
#include "apr_file_io.h"
#include "apr_file_info.h"
#include "apr_network_io.h"
#include "apr_errno.h"
#include "apr_general.h"
#include "apr_lib.h"
#include "test_apr.h"
struct view_fileinfo
{
apr_int32_t bits;
char *description;
} vfi[] = {
{APR_FINFO_MTIME, "MTIME"},
{APR_FINFO_CTIME, "CTIME"},
{APR_FINFO_ATIME, "ATIME"},
{APR_FINFO_SIZE, "SIZE"},
{APR_FINFO_DEV, "DEV"},
{APR_FINFO_INODE, "INODE"},
{APR_FINFO_NLINK, "NLINK"},
{APR_FINFO_TYPE, "TYPE"},
{APR_FINFO_USER, "USER"},
{APR_FINFO_GROUP, "GROUP"},
{APR_FINFO_UPROT, "UPROT"},
{APR_FINFO_GPROT, "GPROT"},
{APR_FINFO_WPROT, "WPROT"},
{0, NULL}
};
void test_filedel(apr_pool_t *);
void testdirs(apr_pool_t *);
static void test_read(apr_pool_t *);
int main(void)
{
apr_pool_t *pool;
apr_file_t *thefile = NULL;
apr_finfo_t finfo;
apr_socket_t *testsock = NULL;
apr_pollfd_t *sdset = NULL;
apr_status_t status;
apr_int32_t flag = APR_READ | APR_WRITE | APR_CREATE;
apr_size_t nbytes = 0;
apr_off_t zer = 0;
char *buf;
const char *str;
char *filename = "test.fil";
char *teststr;
apr_uid_t uid;
apr_gid_t gid;
#if APR_FILES_AS_SOCKETS
apr_int32_t num;
#endif
printf("APR File Functions Test\n=======================\n\n");
STD_TEST_NEQ("Initializing APR", apr_initialize())
atexit(apr_terminate);
STD_TEST_NEQ("Creating the main pool we'll use",
apr_pool_create(&pool, NULL))
STD_TEST_NEQ("Creating the second pool we'll use",
apr_pool_create(&pool, NULL))
fprintf(stdout, "Testing file functions.\n");
STD_TEST_NEQ(" Opening file",
apr_file_open(&thefile, filename, flag,
APR_UREAD | APR_UWRITE | APR_GREAD, pool))
printf("%-60s", " Checking filename");
if (thefile == NULL){
MSG_AND_EXIT("\nBad file descriptor")
}
apr_file_name_get(&str, thefile);
printf("%s\n", str);
if (strcmp(str, filename) != 0){
MSG_AND_EXIT("Wrong filename\n")
}
nbytes = strlen("this is a test");
STD_TEST_NEQ(" Writing to the file",
apr_file_write(thefile, "this is a test", &nbytes))
TEST_NEQ(" Checking we wrote everything", nbytes,
strlen("this is a test"), "OK", "Failed to write everything")
zer = 0;
STD_TEST_NEQ(" Moving to the start of file",
apr_file_seek(thefile, SEEK_SET, &zer))
#if APR_FILES_AS_SOCKETS
printf(" This platform supports files_like_sockets, testing...\n");
STD_TEST_NEQ(" Making file look like a socket",
apr_socket_from_file(&testsock, thefile))
apr_poll_setup(&sdset, 1, pool);
apr_poll_socket_add(sdset, testsock, APR_POLLIN);
num = 1;
STD_TEST_NEQ(" Checking for incoming data",
apr_poll(sdset, &num, -1))
if (num == 0) {
MSG_AND_EXIT("I should not return until num == 1\n")
}
printf(" End of files as sockets test.\n");
#endif
nbytes = strlen("this is a test");
buf = (char *)apr_palloc(pool, nbytes + 1);
STD_TEST_NEQ(" Reading from the file",
apr_file_read(thefile, buf, &nbytes))
TEST_NEQ(" Checking what we read", nbytes, strlen("this is a test"),
"OK", "We didn't read properly.\n")
STD_TEST_NEQ(" Adding user data to the file",
apr_file_data_set(thefile, "This is a test",
"test", apr_pool_cleanup_null))
STD_TEST_NEQ(" Getting user data from the file",
apr_file_data_get((void **)&teststr, "test", thefile))
TEST_NEQ(" Checking the data we got", strcmp(teststr, "This is a test"), 0,
"OK", "Got the data, but it was wrong")
printf("%-60s", " Getting fileinfo");
status = apr_file_info_get(&finfo, APR_FINFO_NORM, thefile);
if (status == APR_INCOMPLETE) {
int i;
printf("INCOMPLETE\n");
for (i = 0; vfi[i].bits; ++i)
if (vfi[i].bits & ~finfo.valid)
fprintf(stderr, "\t Missing %s\n", vfi[i].description);
}
else if (status != APR_SUCCESS) {
printf("OK\n");
MSG_AND_EXIT("Couldn't get the fileinfo")
}
else {
printf("OK\n");
}
gid = finfo.group;
uid = finfo.user;
STD_TEST_NEQ(" Closing the file", apr_file_close(thefile))
printf("%-60s", " Stat'ing file");
status = apr_stat(&finfo, filename, APR_FINFO_NORM, pool);
if (status == APR_INCOMPLETE) {
int i;
printf("INCOMPLETE\n");
for (i = 0; vfi[i].bits; ++i)
if (vfi[i].bits & ~finfo.valid)
fprintf(stderr, "\t Missing %s\n", vfi[i].description);
}
else if (status != APR_SUCCESS) {
printf("Failed\n");
MSG_AND_EXIT("Couldn't stat the file")
}
else {
printf("OK\n");
}
if (finfo.valid & APR_FINFO_GROUP) {
STD_TEST_NEQ(" Getting groupname",
apr_get_groupname(&buf, finfo.group, pool))
STD_TEST_NEQ(" Comparing group ID's",
apr_compare_groups(finfo.group, gid))
printf(" (gid's for %s match)\n", buf);
}
if (finfo.valid & APR_FINFO_USER) {
STD_TEST_NEQ(" Getting username",
apr_get_username(&buf, finfo.user, pool))
STD_TEST_NEQ(" Comparing users",
apr_compare_users(finfo.user, uid))
printf(" (uid's for %s match)\n", buf);
}
STD_TEST_NEQ(" Deleting the file", apr_file_remove(filename, pool))
TEST_EQ(" Making sure it's gone",
apr_file_open(&thefile, filename, APR_READ,
APR_UREAD | APR_UWRITE | APR_GREAD, pool),
APR_SUCCESS, "OK", "Failed")
testdirs(pool);
test_filedel(pool);
test_read(pool);
apr_pool_destroy(pool);
printf("\nAll tests passed OK\n");
return 1;
}
void test_filedel(apr_pool_t *pool)
{
apr_file_t *thefile = NULL;
apr_int32_t flag = APR_READ | APR_WRITE | APR_CREATE;
STD_TEST_NEQ(" Creating the file",
apr_file_open(&thefile, "testdel", flag,
APR_UREAD | APR_UWRITE | APR_GREAD, pool))
STD_TEST_NEQ(" Closing the file",
apr_file_close(thefile))
STD_TEST_NEQ(" Removing the file", apr_file_remove("testdel", pool))
TEST_EQ(" Checking it's gone",
apr_file_open(&thefile, "testdel", APR_READ,
APR_UREAD | APR_UWRITE | APR_GREAD, pool),
APR_SUCCESS,
"OK", "Failed")
}
void testdirs(apr_pool_t *pool)
{
apr_dir_t *temp;
apr_file_t *file = NULL;
apr_size_t bytes;
apr_finfo_t dirent;
fprintf(stdout, "Testing Directory functions.\n");
STD_TEST_NEQ(" Making directory",
apr_dir_make("testdir",
APR_UREAD | APR_UWRITE | APR_UEXECUTE |
APR_GREAD | APR_GWRITE | APR_GEXECUTE |
APR_WREAD | APR_WWRITE | APR_WEXECUTE, pool))
STD_TEST_NEQ(" Creating a file in the new directory",
apr_file_open(&file, "testdir/testfile",
APR_READ | APR_WRITE | APR_CREATE,
APR_UREAD | APR_UWRITE | APR_UEXECUTE, pool))
bytes = strlen("Another test!!!");
apr_file_write(file, "Another test!!", &bytes);
apr_file_close(file);
STD_TEST_NEQ(" Opening directory", apr_dir_open(&temp, "testdir", pool))
STD_TEST_NEQ(" Reading directory",
apr_dir_read(&dirent, APR_FINFO_DIRENT, temp))
printf(" Getting Information about the file...\n");
do {
/* Because I want the file I created, I am skipping the "." and ".."
* files that are here.
*/
if (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE
| APR_FINFO_SIZE | APR_FINFO_MTIME, temp)
!= APR_SUCCESS) {
fprintf(stderr, "Error reading directory testdir");
exit(-1);
}
} while (dirent.name[0] == '.');
TEST_NEQ(" File name",
strcmp(dirent.name, "testfile"), 0,
"OK", "Got wrong file name");
TEST_NEQ(" File type", dirent.filetype, APR_REG,
"OK", "Got wrong file type")
TEST_NEQ(" File size", dirent.size, bytes,
"OK", "Got wrong file size")
printf(" Done checking file information\n");
STD_TEST_NEQ(" Rewind directory", apr_dir_rewind(temp))
STD_TEST_NEQ(" Closing directory", apr_dir_close(temp))
STD_TEST_NEQ(" Removing file from directory",
apr_file_remove("testdir/testfile", pool))
STD_TEST_NEQ(" Removing directory", apr_dir_remove("testdir", pool))
}
#define TESTREAD_BLKSIZE 1024
#define APR_BUFFERSIZE 4096 /* This should match APR's buffer size. */
static void create_testread(apr_pool_t *p, const char *fname)
{
apr_file_t *f = NULL;
apr_status_t rv;
char buf[TESTREAD_BLKSIZE];
apr_size_t nbytes;
/* Create a test file with known content.
*/
rv = apr_file_open(&f, fname, APR_CREATE | APR_WRITE | APR_TRUNCATE, APR_UREAD | APR_UWRITE, p);
if (rv) {
fprintf(stderr, "apr_file_open()->%d/%s\n",
rv, apr_strerror(rv, buf, sizeof buf));
exit(1);
}
nbytes = 4;
rv = apr_file_write(f, "abc\n", &nbytes);
assert(!rv && nbytes == 4);
memset(buf, 'a', sizeof buf);
nbytes = sizeof buf;
rv = apr_file_write(f, buf, &nbytes);
assert(!rv && nbytes == sizeof buf);
nbytes = 2;
rv = apr_file_write(f, "\n\n", &nbytes);
assert(!rv && nbytes == 2);
rv = apr_file_close(f);
assert(!rv);
}
static char read_one(apr_file_t *f, int expected)
{
char bytes[3];
apr_status_t rv;
static int counter = 0;
apr_size_t nbytes;
counter += 1;
bytes[0] = bytes[2] = 0x01;
if (counter % 2) {
rv = apr_file_getc(bytes + 1, f);
}
else {
nbytes = 1;
rv = apr_file_read(f, bytes + 1, &nbytes);
assert(nbytes == 1);
}
assert(!rv);
assert(bytes[0] == 0x01 && bytes[2] == 0x01);
if (expected != -1) {
assert(bytes[1] == expected);
}
return bytes[1];
}
static int test_read_guts(apr_pool_t *p, const char *fname, apr_int32_t extra_flags)
{
apr_file_t *f = NULL;
apr_status_t rv;
apr_size_t nbytes;
char buf[1024];
int i;
rv = apr_file_open(&f, fname, APR_READ | extra_flags, 0, p);
assert(!rv);
read_one(f, 'a');
read_one(f, 'b');
if (extra_flags & APR_BUFFERED) {
printf("\n skipping apr_file_ungetc() for APR_BUFFERED as it "
"doesn't work yet\n");
return (0);
}
else {
rv = apr_file_ungetc('b', f);
assert(!rv);
/* Note: some implementations move the file ptr back;
* others just save up to one char; it isn't
* portable to unget more than once.
*/
/* Don't do this: rv = apr_file_ungetc('a', f); */
read_one(f, 'b');
}
read_one(f, 'c');
read_one(f, '\n');
for (i = 0; i < TESTREAD_BLKSIZE; i++) {
read_one(f, 'a');
}
read_one(f, '\n');
read_one(f, '\n');
rv = apr_file_getc(buf, f);
assert(rv == APR_EOF);
rv = apr_file_close(f);
assert(!rv);
f = NULL;
rv = apr_file_open(&f, fname, APR_READ | extra_flags, 0, p);
assert(!rv);
rv = apr_file_gets(buf, 10, f);
assert(!rv);
assert(!strcmp(buf, "abc\n"));
/* read first 800 of TESTREAD_BLKSIZE 'a's
*/
rv = apr_file_gets(buf, 801, f);
assert(!rv);
assert(strlen(buf) == 800);
for (i = 0; i < 800; i++) {
assert(buf[i] == 'a');
}
/* read rest of the 'a's and the first newline
*/
rv = apr_file_gets(buf, sizeof buf, f);
assert(!rv);
assert(strlen(buf) == TESTREAD_BLKSIZE - 800 + 1);
for (i = 0; i < TESTREAD_BLKSIZE - 800; i++) {
assert(buf[i] == 'a');
}
assert(buf[TESTREAD_BLKSIZE - 800] == '\n');
/* read the last newline
*/
rv = apr_file_gets(buf, sizeof buf, f);
assert(!rv);
assert(!strcmp(buf, "\n"));
/* get APR_EOF
*/
rv = apr_file_gets(buf, sizeof buf, f);
assert(rv == APR_EOF);
/* get APR_EOF with apr_file_getc
*/
rv = apr_file_getc(buf, f);
assert(rv == APR_EOF);
/* get APR_EOF with apr_file_read
*/
nbytes = sizeof buf;
rv = apr_file_read(f, buf, &nbytes);
assert(rv == APR_EOF);
rv = apr_file_close(f);
assert(!rv);
return (1);
}
static void test_bigread(apr_pool_t *p, const char *fname, apr_int32_t extra_flags)
{
apr_file_t *f = NULL;
apr_status_t rv;
char buf[APR_BUFFERSIZE * 2];
apr_size_t nbytes;
/* Create a test file with known content.
*/
rv = apr_file_open(&f, fname, APR_CREATE | APR_WRITE | APR_TRUNCATE, APR_UREAD | APR_UWRITE, p);
if (rv) {
fprintf(stderr, "apr_file_open()->%d/%s\n",
rv, apr_strerror(rv, buf, sizeof buf));
exit(1);
}
nbytes = APR_BUFFERSIZE;
memset(buf, 0xFE, nbytes);
rv = apr_file_write(f, buf, &nbytes);
assert(!rv && nbytes == APR_BUFFERSIZE);
rv = apr_file_close(f);
assert(!rv);
f = NULL;
rv = apr_file_open(&f, fname, APR_READ | extra_flags, 0, p);
assert(!rv);
nbytes = sizeof buf;
rv = apr_file_read(f, buf, &nbytes);
assert(!rv);
assert(nbytes == APR_BUFFERSIZE);
rv = apr_file_close(f);
assert(!rv);
}
static void test_read(apr_pool_t *p)
{
const char *fname = "testread.dat";
apr_status_t rv;
printf("Testing file read functions.\n");
create_testread(p, fname);
printf("%-60s", " Buffered file tests");
if (test_read_guts(p, fname, APR_BUFFERED))
printf("OK\n");
printf("%-60s", " Unbuffered file tests");
test_read_guts(p, fname, 0);
printf("OK\n");
printf("%-60s", " More buffered file tests");
test_bigread(p, fname, APR_BUFFERED);
printf("OK\n");
printf("%-60s", " More unbuffered file tests");
test_bigread(p, fname, 0);
printf("OK\n");
rv = apr_file_remove(fname, p);
assert(!rv);
printf("%-60s", " All read tests");
printf("OK\n");
}