diff options
Diffstat (limited to 'extract/src/buffer-test.c')
-rw-r--r-- | extract/src/buffer-test.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/extract/src/buffer-test.c b/extract/src/buffer-test.c new file mode 100644 index 00000000..6701fbab --- /dev/null +++ b/extract/src/buffer-test.c @@ -0,0 +1,306 @@ +#include "../include/extract_buffer.h" +#include "../include/extract_alloc.h" + +#include "mem.h" +#include "memento.h" +#include "outf.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> + + +static int rand_int(int max) +/* Returns random int from 0..max-1. */ +{ + return (int) (rand() / (RAND_MAX+1.0) * max); +} + + +/* Support for an extract_buffer_t that reads from / writes to a fixed block of +memory, with a fn_cache() that returns a randomly-sized cache each time it is +called, and read/write functions that do random short reads and writes. */ + +typedef struct +{ + extract_alloc_t* alloc; + char* data; + size_t bytes; /* Size of data[]. */ + size_t pos; /* Current position in data[]. */ + char cache[137]; + int num_calls_cache; + int num_calls_read; + int num_calls_write; +} mem_t; + +static int s_read(void* handle, void* destination, size_t bytes, size_t* o_actual) +/* Does a randomised short read. */ +{ + mem_t* r = handle; + size_t n = 91; + assert(bytes > 0); + r->num_calls_read += 1; + assert(r->pos <= r->bytes); + if (n > bytes) n = bytes; + if (n > r->bytes - r->pos) n = r->bytes - r->pos; + if (n) n = rand_int((int) n-1) + 1; + memcpy(destination, r->data + r->pos, n); + r->pos += n; + *o_actual = n; + return 0; +} + +static int s_read_cache(void* handle, void** o_cache, size_t* o_numbytes) +/* Returns a cache with randomised size. */ +{ + mem_t* r = handle; + int n; + r->num_calls_cache += 1; + *o_cache = r->cache; + n = (int) (r->bytes - r->pos); + if (n > (int) sizeof(r->cache)) n = sizeof(r->cache); + if (n) n = rand_int( n - 1) + 1; + memcpy(r->cache, r->data + r->pos, n); + r->pos += n; + *o_cache = r->cache; + *o_numbytes = n; + return 0; +} + +static void s_read_buffer_close(void* handle) +{ + mem_t* r = handle; + extract_free(r->alloc, &r->data); +} + +static void s_create_read_buffer(extract_alloc_t* alloc, int bytes, mem_t* r, extract_buffer_t** o_buffer) +/* Creates extract_buffer_t that reads from randomised data using randomised +short reads and cache with randomised sizes. */ +{ + int i; + int e; + if (extract_malloc(alloc, &r->data, bytes)) abort(); + for (i=0; i<bytes; ++i) { + r->data[i] = (char) rand(); + } + r->alloc = alloc; + r->bytes = bytes; + r->pos = 0; + r->num_calls_cache = 0; + r->num_calls_read = 0; + r->num_calls_write = 0; + e = extract_buffer_open(alloc, r, s_read, NULL /*write*/, s_read_cache, s_read_buffer_close, o_buffer); + assert(!e); +} + +static void test_read(void) +{ + /* Create read buffer with randomised content. */ + int len = 12345; + mem_t r; + char* out_buffer; + int out_pos; + int its; + int e; + extract_buffer_t* buffer; + s_create_read_buffer(NULL /*alloc*/, len, &r, &buffer); + + /* Repeatedly read from read-buffer until we get EOF, and check we read the + original content. */ + if (extract_malloc(r.alloc, &out_buffer, len)) abort(); + out_pos = 0; + for (its=0;; ++its) { + size_t actual; + int n = rand_int(120)+1; + int e = extract_buffer_read(buffer, out_buffer + out_pos, n, &actual); + out_pos += (int) actual; + assert(out_pos == (int) extract_buffer_pos(buffer)); + if (e == 1) break; + assert(!e); + assert(!memcmp(out_buffer, r.data, out_pos)); + } + assert(out_pos == len); + assert(!memcmp(out_buffer, r.data, len)); + outf("its=%i num_calls_read=%i num_calls_write=%i num_calls_cache=%i", + its, r.num_calls_read, r.num_calls_write, r.num_calls_cache); + extract_free(r.alloc, &out_buffer); + out_buffer = NULL; + e = extract_buffer_close(&buffer); + assert(!e); + + outf("Read test passed.\n"); +} + + +static int s_write(void* handle, const void* source, size_t bytes, size_t* o_actual) +/* Does a randomised short write. */ +{ + mem_t* r = handle; + int n = 61; + r->num_calls_write += 1; + if (n > (int) bytes) n = (int) bytes; + if (n > (int) (r->bytes - r->pos)) n = (int) (r->bytes - r->pos); + assert(n); + n = rand_int((int) n-1) + 1; + memcpy(r->data + r->pos, source, n); + r->data[r->bytes] = 0; + r->pos += n; + *o_actual = n; + return 0; +} + +static int s_write_cache(void* handle, void** o_cache, size_t* o_numbytes) +/* Returns a cache with randomised size. */ +{ + mem_t* r = handle; + int n; + r->num_calls_cache += 1; + assert(r->bytes >= r->pos); + *o_cache = r->cache; + n = (int) (r->bytes - r->pos); + if (n > (int) sizeof(r->cache)) n = sizeof(r->cache); + if (n) n = rand_int( n - 1) + 1; + *o_cache = r->cache; + *o_numbytes = n; + /* We will return a zero-length cache at EOF. */ + return 0; +} + +static void s_write_buffer_close(void* handle) +{ + mem_t* mem = handle; + outf("*** freeing mem->data=%p", mem->data); + extract_free(mem->alloc, &mem->data); +} + +static void s_create_write_buffer(extract_alloc_t* alloc, size_t bytes, mem_t* r, extract_buffer_t** o_buffer) +/* Creates extract_buffer_t that reads from randomised data using randomised +short reads and cache with randomised sizes. */ +{ + int e; + if (extract_malloc(alloc, &r->data, bytes+1)) abort(); + extract_bzero(r->data, bytes); + r->alloc = alloc; + r->bytes = bytes; + r->pos = 0; + r->num_calls_cache = 0; + r->num_calls_read = 0; + r->num_calls_write = 0; + e = extract_buffer_open(r->alloc, r, NULL /*read*/, s_write, s_write_cache, s_write_buffer_close, o_buffer); + assert(!e); +} + + +static void test_write(void) +{ + /* Create write buffer. */ + size_t len = 12345; + mem_t r; + extract_buffer_t* buffer; + char* out_buffer; + unsigned i; + size_t out_pos = 0; + int its; + int e; + + s_create_write_buffer(NULL /*alloc*/, len, &r, &buffer); + + /* Write to read-buffer, and check it contains the original content. */ + if (extract_malloc(r.alloc, &out_buffer, len)) abort(); + for (i=0; i<len; ++i) { + out_buffer[i] = (char) ('a' + rand_int(26)); + } + for (its=0;; ++its) { + size_t actual; + size_t n = rand_int(12)+1; + int e = extract_buffer_write(buffer, out_buffer+out_pos, n, &actual); + out_pos += actual; + assert(out_pos == extract_buffer_pos(buffer)); + if (e == 1) break; + assert(!e); + } + assert(out_pos == len); + assert(!memcmp(out_buffer, r.data, len)); + extract_free(r.alloc, &out_buffer); + outf("its=%i num_calls_read=%i num_calls_write=%i num_calls_cache=%i", + its, r.num_calls_read, r.num_calls_write, r.num_calls_cache); + e = extract_buffer_close(&buffer); + assert(!e); + outf("Write test passed.\n"); +} + +static void test_file(void) +{ + /* Check we can write 3 bytes to file. */ + extract_buffer_t* file_buffer; + if (extract_buffer_open_file(NULL /*alloc*/, "test/generated/buffer-file", 1 /*writable*/, &file_buffer)) abort(); + + { + size_t n; + int e; + errno = 0; + e = extract_buffer_write(file_buffer, "foo", 3, &n); + if (e == 0 && n == 3) {} + else { + outf("extract_buffer_write() returned e=%i errno=%i n=%zi", e, errno, n); + abort(); + } + } + if (extract_buffer_close(&file_buffer)) abort(); + + /* Check we get back expected short reads and EOF when reading from 3-byte + file created above. */ + if (extract_buffer_open_file(NULL /*alloc*/, "test/generated/buffer-file", 0 /*writable*/, &file_buffer)) abort(); + + { + size_t n; + char buffer[10]; + int e; + errno = 0; + e = extract_buffer_read(file_buffer, buffer, 2, &n); + if (e == 0 && n == 2) {} + else { + outf("extract_buffer_read() returned e=%i errno=%i n=%zi", e, errno, n); + abort(); + } + e = extract_buffer_read(file_buffer, buffer, 3, &n); + if (e == 1 && n == 1) {} + else { + outf("extract_buffer_read() returned e=%i errno=%i n=%zi", e, errno, n); + abort(); + } + e = extract_buffer_read(file_buffer, buffer, 3, &n); + if (e == 1 && n == 0) {} + else { + outf("extract_buffer_read() returned e=%i errno=%i n=%zi", e, errno, n); + abort(); + } + } + if (extract_buffer_close(&file_buffer)) abort(); + + /* Check writing to read-only file buffer fails. */ + { + int e; + char text[] = "hello world"; + size_t actual; + if (extract_buffer_open_file(NULL /*alloc*/, "test/generated/buffer-file", 0 /*writable*/, &file_buffer)) { + abort(); + } + + e = extract_buffer_write(file_buffer, text, sizeof(text)-1, &actual); + outf("extract_buffer_write() on read buffer returned e=%i actual=%zi", e, actual); + if (e != -1 || errno != EINVAL) abort(); + if (extract_buffer_close(&file_buffer)) abort(); + } + + outf("file buffer tests passed.\n"); +} + +int main(void) +{ + outf_verbose_set(1); + test_read(); + test_write(); + test_file(); + return 0; +} |