Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
main.c File Reference
#include "serene/runtime.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "serene/ir/reader.h"
#include "serene/rt/context.h"
#include "serene/rt/engine.h"
#include "serene/rt/errors.h"
#include "serene/rt/fiber.h"
#include "serene/rt/fiber/io.h"
#include "serene/rt/mm/interface.h"
#include "serene/utils.h"
Include dependency graph for main.c:

Go to the source code of this file.

Data Structures

struct  test_case_t
struct  test_fiber_arg_t

Macros

#define _POSIX_C_SOURCE   200809L
#define SEPARATOR   "---"

Typedefs

typedef struct test_case_t test_case_t
typedef struct test_fiber_arg_t test_fiber_arg_t

Functions

static bool split_test_case (const char *path, const char *content, size_t len, test_case_t *out)
 Split content (len bytes) at the line that is exactly "---" into a freshly allocated source and expected string in out.
static srn_fiber_result_t run_test_fiber (srn_context_t *ctx, void *arg_)
 Run one test file end to end, on its own fiber, so a large batch of files runs as that many concurrent fibers rather than one after another: read the file (srn_fiber_read suspends this fiber rather than blocking its worker), split it into source/expected, hand the source to the reader on a nested fiber, then compare and report.
int main (int argc, char **argv)

Macro Definition Documentation

◆ _POSIX_C_SOURCE

#define _POSIX_C_SOURCE   200809L

Definition at line 22 of file main.c.

◆ SEPARATOR

#define SEPARATOR   "---"

Definition at line 43 of file main.c.

Typedef Documentation

◆ test_case_t

typedef struct test_case_t test_case_t

◆ test_fiber_arg_t

typedef struct test_fiber_arg_t test_fiber_arg_t

Function Documentation

◆ main()

int main ( int argc,
char ** argv )

Definition at line 231 of file main.c.

231 {
232 if (argc < 2) {
233 (void)fprintf(stderr, "usage: %s <test-file>...\n", argv[0]);
234 return 1;
235 }
236
237 SERENE_RUNTIME_INIT_WITH_SCHED(engine, sched, nullptr);
238 srn_context_t *ctx = srn_context_make(engine);
239 size_t ntests = (size_t)(argc - 1);
240
242
243 for (size_t i = 0; i < ntests; i++) {
244 args[i].path = argv[i + 1];
246 }
247
248 srn_sched_run(sched, 0);
249
250 int failures = 0;
251 for (size_t i = 0; i < ntests; i++) {
252 if (!args[i].passed) {
253 failures++;
254 }
255 }
256
259
260 printf("\n%d/%d passed\n", (int)ntests - failures, (int)ntests);
261
262 return failures == 0 ? 0 : 1;
263}
va_list args
Definition acutest.h:876
srn_context_t * srn_context_make(srn_engine_t *engine)
Make an empty context, by allocating a new memory block.
Definition context.c:39
int srn_context_release(srn_context_t *ctx)
Definition context.c:64
#define ALLOCN_OR_PANIC(ctx, T, N)
Definition context.h:99
srn_fiber_t * srn_fiber_spawn(srn_context_t *ctx, srn_fiber_entry_t entry, void *arg)
Make and schedule a fiber with every default, the engine's scheduler, the configured stack size,...
Definition fiber.c:250
static srn_fiber_result_t run_test_fiber(srn_context_t *ctx, void *arg_)
Run one test file end to end, on its own fiber, so a large batch of files runs as that many concurren...
Definition main.c:124
#define SERENE_RUNTIME_SHUTDOWN(engine)
Tear down what SERENE_RUNTIME_INIT brought up, in reverse order, the engine (reactor,...
Definition runtime.h:58
#define SERENE_RUNTIME_INIT_WITH_SCHED(engine, sched, config)
SERENE_RUNTIME_INIT plus a sched variable bound to the engine's scheduler, which every run and fiber ...
Definition runtime.h:46
void srn_sched_run(srn_scheduler_t *sched, size_t nworkers)
Run the scheduler with nworkers os threads draining it, returning once the pool goes quiescent (every...
Definition scheduler.c:875
Here is the call graph for this function:

◆ run_test_fiber()

srn_fiber_result_t run_test_fiber ( srn_context_t * ctx,
void * arg_ )
static

Run one test file end to end, on its own fiber, so a large batch of files runs as that many concurrent fibers rather than one after another: read the file (srn_fiber_read suspends this fiber rather than blocking its worker), split it into source/expected, hand the source to the reader on a nested fiber, then compare and report.

Errors along the way (I/O, a malformed fixture, a reader failure) are reported to stderr and leave arg->passed false; nothing here escapes to the fiber's result, there is no driver left to inspect it.

Definition at line 124 of file main.c.

124 {
125 test_fiber_arg_t *arg = arg_;
126 arg->passed = false;
127
128 int fd = open(arg->path, O_RDONLY);
129 if (fd < 0) {
131 srn_error_t *err = ERR(ctx, tag, "Failed to open the test file.");
132 (void)fprintf(stderr, "%s: %s\n", arg->path, srn_error_to_string(ctx, err));
133 return nullptr;
134 }
135
136 struct stat st;
137 if (fstat(fd, &st) < 0) {
139 close(fd);
140 srn_error_t *err = ERR(ctx, tag, "Failed to stat the test file.");
141 (void)fprintf(stderr, "%s: %s\n", arg->path, srn_error_to_string(ctx, err));
142 return nullptr;
143 }
144
145 size_t len = (size_t)st.st_size;
146 char *content = malloc(len + 1);
147 if (content == nullptr) {
148 close(fd);
149 (void)fprintf(stderr, "%s: out of memory reading the test file\n", arg->path);
150 return nullptr;
151 }
152
153 size_t total_read = 0;
154 while (total_read < len) {
156 SRN_WITH_RETRY(r.maybe_error, {
157 r = srn_fiber_read(fd, content + total_read, len - total_read, -1);
158 });
159
160 if (HAS_ERROR(r)) {
161 close(fd);
162 free(content);
163 (void)fprintf(stderr, "%s: %s\n", arg->path, srn_error_to_string(ctx, r.maybe_error));
164 return nullptr;
165 }
166
167 if (r.count == 0) {
168 // Short read against fstat's size, treat what we got as the whole file.
169 break;
170 }
171
172 total_read += r.count;
173 }
174
175 close(fd);
176 content[total_read] = '\0';
177
178 test_case_t test;
179 if (!split_test_case(arg->path, content, total_read, &test)) {
180 free(content);
181 return nullptr;
182 }
183 free(content);
184
185 srn_sexp_parser_t parser = {
186 .ctx = ctx,
187 .reading_context = nullptr,
188 .buffer_id = 0,
189 .buffer = (const unsigned char *)test.source,
190 .len = strlen(test.source),
191 .pos = 0,
192 };
193
194 reader_fiber_arg_t reader_arg = {
195 .parser = &parser,
196 .tree = nullptr,
197 };
198
199 srn_fiber_t *reader_fiber = srn_fiber_spawn(ctx, read_from_buffer, &reader_arg);
200 srn_fiber_result_t reader_result = srn_fiber_wait_for(reader_fiber);
201
202 if (reader_result != nullptr) {
203 srn_error_t *err = (srn_error_t *)reader_result;
204 (void)fprintf(stderr, "%s: %s\n", arg->path, srn_error_to_string(ctx, err));
205 free(test.source);
206 free(test.expected);
207 return nullptr;
208 }
209
210 const char *actual = srn_sexp_to_debug_string(ctx, reader_arg.tree);
211 arg->passed = ((actual != nullptr && strcmp(actual, test.expected) == 0) != 0);
212
213 if (arg->passed) {
214 printf("PASS %s\n", arg->path);
215 } else {
216 printf("FAIL %s\n", arg->path);
217 printf("==> expected:\n%s\n\n", test.expected);
218 printf("==> actual:\n%s\n\n", actual != nullptr ? actual : "(reader error, see above)");
219 }
220
221 if (actual != nullptr) {
222 FREE(ctx, (void *)actual);
223 }
224
225 free(test.source);
226 free(test.expected);
227
228 return nullptr;
229}
char * srn_error_to_string(const srn_context_t *ctx, const srn_error_t *err)
Format err as a newly allocated string of the form [TAG]: msg, allocated in ctx.
Definition errors.c:133
#define HAS_ERROR(x)
True when x – a value carrying an ERROR_HEADER – has an error attached.
Definition errors.h:70
srn_error_tag_t
The kind of a failure.
Definition errors.h:92
@ UNKNOWN_IO_ERROR
Definition errors.h:147
srn_error_tag_t srn_errno_to_tag(int errno_value, srn_error_tag_t default_tag)
Map a POSIX errno to error tag.
#define ERR(ctx, err, msg)
Definition errors.h:223
void * srn_fiber_result_t
What a fiber's entry produces, type-erased.
Definition fiber.h:171
#define FREE(ctx, ptr)
Definition interface.h:247
#define SRN_WITH_RETRY(err,...)
Run block and retry it (yielding between attempts through srn_fiber_io_retry) while err holds the tra...
Definition io.h:136
static bool split_test_case(const char *path, const char *content, size_t len, test_case_t *out)
Split content (len bytes) at the line that is exactly "---" into a freshly allocated source and expec...
Definition main.c:62
const char * srn_sexp_to_debug_string(srn_context_t *ctx, srn_sexp_tree_t *tree)
Convert the given sexp to a string for debugging purposes.
Definition reader.c:860
srn_fiber_result_t read_from_buffer(srn_context_t *ctx, void *arg)
Definition reader.c:584
srn_fiber_result_t srn_fiber_wait_for(srn_fiber_t *target)
Block the calling fiber until target finishes, then return its result.
Definition scheduler.c:1130
const size_t len
void * arg
close(fd)
srn_sexp_tree_t * tree
Definition reader.h:184
A runtime error, a tag classifying the failure and a human-readable message.
Definition errors.h:173
The result of a byte transfer op (read/write/sendto/recvmsg/sendmsg).
Definition io.h:80
size_t count
Definition io.h:82
char * source
Definition main.c:46
char * expected
Definition main.c:47
Here is the call graph for this function:
Here is the caller graph for this function:

◆ split_test_case()

bool split_test_case ( const char * path,
const char * content,
size_t len,
test_case_t * out )
static

Split content (len bytes) at the line that is exactly "---" into a freshly allocated source and expected string in out.

A single trailing newline is stripped from each side, if present, everything else is kept byte for byte. Returns false, after printing a message to stderr, when no such line exists.

Definition at line 62 of file main.c.

62 {
63 size_t line_start = 0;
64 size_t sep_start = SIZE_MAX;
65 size_t sep_end = SIZE_MAX;
66
67 for (size_t i = 0; i <= len; i++) {
68 if (i == len || content[i] == '\n') {
69 size_t line_len = i - line_start;
70 if (line_len == strlen(SEPARATOR) && memcmp(content + line_start, SEPARATOR, line_len) == 0) {
71 sep_start = line_start;
72 sep_end = (i < len) ? i + 1 : i;
73 break;
74 }
75 line_start = i + 1;
76 }
77 }
78
79 if (sep_start == SIZE_MAX) {
80 (void)fprintf(stderr, "%s: missing '---' separator line\n", path);
81 return false;
82 }
83
84 size_t src_len = sep_start;
85 if (src_len > 0 && content[src_len - 1] == '\n') {
86 src_len--;
87 }
88
89 size_t exp_start = sep_end;
90 size_t exp_len = len - sep_end;
91 if (exp_len > 0 && content[exp_start + exp_len - 1] == '\n') {
92 exp_len--;
93 }
94
95 out->source = malloc(src_len + 1);
96 out->expected = malloc(exp_len + 1);
97
98 if (out->source == nullptr || out->expected == nullptr) {
99 (void)fprintf(stderr, "%s: out of memory\n", path);
100 free(out->source);
101 free(out->expected);
102 return false;
103 }
104
105 memcpy(out->source, content, src_len);
106 out->source[src_len] = '\0';
107
108 memcpy(out->expected, content + exp_start, exp_len);
109 out->expected[exp_len] = '\0';
110
111 return true;
112}
#define SEPARATOR
Definition main.c:43
char path[PATH_MAX]
Here is the caller graph for this function: