#include "runtime.h" #include #include #include #define EXPECT(condition) do { \ if (!(condition)) { \ fprintf(stderr, "resource ABI assertion failed at line %d: %s\n", __LINE__, #condition); \ return 1; \ } \ } while (0) typedef struct { uint64_t kind; void *value; } ReadEvent; static unsigned event_retains; static unsigned event_drops; static void event_clone(const void *source, void *destination) { const ReadEvent *event = source; ReadEvent *copy = destination; copy->kind = event->kind; copy->value = event->kind == 1 ? dever_rt_v1_bytes_retain(event->value) : dever_rt_v1_text_retain(event->value); ++event_retains; } static void event_drop(void *value) { ReadEvent *event = value; if (event->kind == 1) { dever_rt_v1_bytes_release(event->value); } else { dever_rt_v1_text_release(event->value); } ++event_drops; } static uint8_t event_equal(const void *left, const void *right) { const ReadEvent *first = left; const ReadEvent *second = right; if (first->kind != second->kind) return 0; return first->kind == 1 ? dever_rt_v1_bytes_equal(first->value, second->value) : dever_rt_v1_text_equal(first->value, second->value); } static void event_chunk(const void *bytes, void *out) { *(ReadEvent *)out = (ReadEvent){1, dever_rt_v1_bytes_retain(bytes)}; ++event_retains; } static void event_failed(const void *text, void *out) { *(ReadEvent *)out = (ReadEvent){2, dever_rt_v1_text_retain(text)}; ++event_retains; } static const DeverRtType EVENT_TYPE = { sizeof(ReadEvent), _Alignof(ReadEvent), event_clone, event_drop, event_equal, NULL }; static const DeverRtReadEvent READ_EVENT = { &EVENT_TYPE, event_chunk, event_failed }; static int expect_buffer(DeverRtBuffer *buffer, const char *expected) { size_t length = strlen(expected); int equal = buffer->len == length && (length == 0 || (buffer->ptr && memcmp(buffer->ptr, expected, length) == 0)); dever_rt_v1_buffer_free(buffer->ptr, buffer->len); *buffer = (DeverRtBuffer){NULL, 0}; return equal; } static int expect_nonempty(DeverRtBuffer *buffer) { int nonempty = buffer->ptr != NULL && buffer->len != 0; dever_rt_v1_buffer_free(buffer->ptr, buffer->len); *buffer = (DeverRtBuffer){NULL, 0}; return nonempty; } static int expect_event_bytes(ReadEvent *event, const char *expected) { DeverRtBuffer bytes = {NULL, 0}; if (event->kind != 1 || dever_rt_v1_bytes_export(event->value, &bytes) != DEVER_RT_OK) { return 0; } return expect_buffer(&bytes, expected); } int main(int argc, char **argv) { EXPECT(argc == 2); DeverRtBuffer error = {NULL, 0}; void *path = NULL; void *file = NULL; void *bytes = NULL; void *stream = NULL; uint8_t done = 0; uint8_t present = 0; EXPECT(dever_rt_v1_file_retain(NULL) == NULL); dever_rt_v1_file_release(NULL); EXPECT(dever_rt_v1_stream_retain(NULL) == NULL); dever_rt_v1_stream_release(NULL); EXPECT(dever_rt_v1_text_new((const uint8_t *)argv[1], strlen(argv[1]), &path, &error) == DEVER_RT_OK); char missing_path[4096]; int missing_length = snprintf(missing_path, sizeof(missing_path), "%s.missing", argv[1]); EXPECT(missing_length > 0 && (size_t)missing_length < sizeof(missing_path)); void *missing = NULL; EXPECT(dever_rt_v1_text_new((const uint8_t *)missing_path, (uint64_t)missing_length, &missing, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_file_open(missing, &file, &error) == DEVER_RT_RUNTIME_ERROR); EXPECT(expect_nonempty(&error)); dever_rt_v1_text_release(missing); /* Invalid output must not create the file. */ EXPECT(dever_rt_v1_file_create(path, NULL, &error) == DEVER_RT_INVALID_INPUT); EXPECT(expect_buffer(&error, "invalid ABI output pointer")); EXPECT(dever_rt_v1_file_create(path, &file, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_file_create(path, &bytes, &error) == DEVER_RT_RUNTIME_ERROR); EXPECT(expect_nonempty(&error)); EXPECT(dever_rt_v1_bytes_new((const uint8_t *)"abcd", 4, &bytes, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_file_write(file, bytes, &done, &error) == DEVER_RT_OK && done == 1); void *alias = dever_rt_v1_file_retain(file); EXPECT(dever_rt_v1_file_close(file, &done, &error) == DEVER_RT_OK && done == 1); EXPECT(dever_rt_v1_file_write(alias, bytes, &done, &error) == DEVER_RT_RUNTIME_ERROR); EXPECT(expect_buffer(&error, "resource is closed")); EXPECT(dever_rt_v1_file_close(alias, &done, &error) == DEVER_RT_RUNTIME_ERROR); EXPECT(expect_buffer(&error, "resource is closed")); dever_rt_v1_file_release(alias); dever_rt_v1_file_release(file); dever_rt_v1_bytes_release(bytes); EXPECT(dever_rt_v1_file_open(path, &file, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_file_read(file, 0, &bytes, &present, &error) == DEVER_RT_RUNTIME_ERROR); EXPECT(expect_buffer(&error, "read size must be a positive Int")); EXPECT(dever_rt_v1_file_read(file, 4, NULL, &present, &error) == DEVER_RT_INVALID_INPUT); EXPECT(expect_buffer(&error, "invalid ABI output pointer")); EXPECT(dever_rt_v1_file_read(file, 4, &bytes, &present, &error) == DEVER_RT_OK && present); EXPECT(dever_rt_v1_bytes_export(bytes, &error) == DEVER_RT_OK); EXPECT(expect_buffer(&error, "abcd")); dever_rt_v1_bytes_release(bytes); bytes = NULL; EXPECT(dever_rt_v1_file_read(file, 4, &bytes, &present, &error) == DEVER_RT_OK && !present); EXPECT(bytes == NULL); EXPECT(dever_rt_v1_file_close(file, &done, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_file_chunks(file, 2, &READ_EVENT, &stream, &error) == DEVER_RT_RUNTIME_ERROR); EXPECT(expect_buffer(&error, "resource is closed")); dever_rt_v1_file_release(file); EXPECT(dever_rt_v1_file_open(path, &file, &error) == DEVER_RT_OK); DeverRtReadEvent invalid_event = {&EVENT_TYPE, event_chunk, NULL}; EXPECT(dever_rt_v1_file_chunks(file, 2, &invalid_event, &stream, &error) == DEVER_RT_INVALID_INPUT); EXPECT(expect_buffer(&error, "incomplete ABI read event descriptor")); EXPECT(dever_rt_v1_file_chunks(file, 0, &READ_EVENT, &stream, &error) == DEVER_RT_RUNTIME_ERROR); EXPECT(expect_buffer(&error, "read size must be a positive Int")); EXPECT(dever_rt_v1_file_chunks(file, 2, &READ_EVENT, &stream, &error) == DEVER_RT_OK); alias = dever_rt_v1_stream_retain(stream); ReadEvent event; EXPECT(dever_rt_v1_stream_pull(stream, NULL, &present, &error) == DEVER_RT_INVALID_INPUT); EXPECT(expect_buffer(&error, "invalid ABI output pointer")); EXPECT(dever_rt_v1_stream_pull(alias, &event, &present, &error) == DEVER_RT_OK && present); EXPECT(expect_event_bytes(&event, "ab")); event_drop(&event); EXPECT(dever_rt_v1_stream_pull(stream, &event, &present, &error) == DEVER_RT_OK && present); EXPECT(expect_event_bytes(&event, "cd")); event_drop(&event); EXPECT(dever_rt_v1_stream_pull(alias, &event, &present, &error) == DEVER_RT_OK && !present); EXPECT(dever_rt_v1_stream_close(stream, &done, &error) == DEVER_RT_OK && done == 1); EXPECT(dever_rt_v1_stream_close(alias, &done, &error) == DEVER_RT_OK && done == 1); dever_rt_v1_stream_release(alias); dever_rt_v1_stream_release(stream); EXPECT(dever_rt_v1_file_close(file, &done, &error) == DEVER_RT_OK); dever_rt_v1_file_release(file); EXPECT(dever_rt_v1_file_open(path, &file, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_file_chunks(file, 1, &READ_EVENT, &stream, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_stream_pull(stream, &event, &present, &error) == DEVER_RT_OK && present); EXPECT(expect_event_bytes(&event, "a")); event_drop(&event); EXPECT(dever_rt_v1_stream_close(stream, &done, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_stream_pull(stream, &event, &present, &error) == DEVER_RT_OK && !present); dever_rt_v1_stream_release(stream); EXPECT(dever_rt_v1_file_close(file, &done, &error) == DEVER_RT_OK); dever_rt_v1_file_release(file); EXPECT(dever_rt_v1_file_open(path, &file, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_file_chunks(file, 2, &READ_EVENT, &stream, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_file_close(file, &done, &error) == DEVER_RT_OK); EXPECT(dever_rt_v1_stream_pull(stream, &event, &present, &error) == DEVER_RT_OK && present); EXPECT(event.kind == 2); EXPECT(dever_rt_v1_text_export(event.value, &error) == DEVER_RT_OK); EXPECT(expect_buffer(&error, "resource is closed")); event_drop(&event); EXPECT(dever_rt_v1_stream_pull(stream, &event, &present, &error) == DEVER_RT_OK && !present); dever_rt_v1_stream_release(stream); dever_rt_v1_file_release(file); EXPECT(dever_rt_v1_file_chunks(NULL, 2, &READ_EVENT, &stream, &error) == DEVER_RT_INVALID_INPUT); EXPECT(expect_buffer(&error, "invalid ABI handle")); EXPECT(dever_rt_v1_file_open(NULL, &file, &error) == DEVER_RT_INVALID_INPUT); EXPECT(expect_buffer(&error, "invalid ABI handle")); dever_rt_v1_text_release(path); EXPECT(event_retains == event_drops); return 0; }