Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
seq_tests.h
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <[email protected]>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <stdio.h>
22
23#include <serene/rt/context.h>
24#include <serene/rt/impl/seq.h>
25
26#include "base.h"
27
28#define SEQ_TESTS(X) \
29 X("seq::empty", test_seq_empty), X("seq::push_less_than_32", test_seq_push_less_than_32), \
30 X("seq::push_more_than_32", test_seq_push_more_than_32), \
31 X("seq::branching_push", test_seq_branching_push), \
32 X("seq::branching_promotion", test_seq_branching_promotion)
33
34#if defined(SEQ_DEBUG)
35# define SEQ_TEST_LOG(...) DBG("SEQ_TEST", __VA_ARGS__)
36#else
37# define SEQ_TEST_LOG(...)
38#endif
39
40typedef struct seq_dummy {
41 int foo;
42 int bar;
44
45static void test_seq_empty() {
46 MAKE_ENGINE(mm, engine);
47 MAKE_CONTEXT(engine, ctx);
48 auto s = seq_empty(ctx);
49
50 TEST_CHECK(s.len == 0);
51 TEST_CHECK(s.tail_len == 0);
52 TEST_CHECK(s.root == nullptr);
53 TEST_CHECK(s.maybe_error == nullptr);
54
55 RELEASE_CONTEXT(ctx);
56 SHUTDOWN_ENGINE(mm, engine);
57}
58
60 MAKE_ENGINE(mm, engine);
61 MAKE_CONTEXT(engine, ctx);
62
63 seq_dummy d = {.foo = 10, .bar = 20}; // NOLINT
64 auto s = seq_empty(ctx);
65 auto s1 = seq_push(&s, (seq_elem_t)&d);
66 TEST_CHECK(s.len == 0);
67 TEST_CHECK(s.tail_len == 0);
68 TEST_CHECK(s.root == nullptr);
69 TEST_CHECK(s.maybe_error == nullptr);
70
71 TEST_CHECK(s1.len == 1);
72 TEST_CHECK(s1.tail_len == 1);
73 TEST_CHECK(s1.root == nullptr);
74 TEST_CHECK(s1.maybe_error == nullptr);
75 seq_dummy *d1 = (seq_dummy *)s1.tail[0];
76 TEST_CHECK(d1->foo == 10);
77 TEST_CHECK(d1->bar == 20);
78
79 RELEASE_CONTEXT(ctx);
80 SHUTDOWN_ENGINE(mm, engine);
81}
82
84 MAKE_ENGINE(mm, engine);
85 MAKE_CONTEXT(engine, ctx);
86
87 seq_t s = seq_empty(ctx);
88
89 for (int i = 0; i < 2000; i++) {
90
91 seq_dummy *d = ALLOC(ctx, seq_dummy);
92 d->foo = i;
93 d->bar = 3000 - i;
94 s = seq_push(&s, (seq_elem_t)d);
95 }
96
97 TEST_CHECK(s.len == 2000);
98 TEST_CHECK(s.tail_len == 16);
99 TEST_CHECK(s.root != nullptr);
100 TEST_CHECK(s.maybe_error == nullptr);
101
102 seq_dummy *d1 = (seq_dummy *)s.tail[0];
103
104 ASSERT_NOT_NULL(d1);
105 TEST_CHECK(d1->foo == 1984);
106 TEST_CHECK(d1->bar == 3000 - d1->foo);
107
108 seq_dummy *d2 = (seq_dummy *)s.tail[s.tail_len - 1];
109 ASSERT_NOT_NULL(d2);
110 TEST_CHECK(d2->foo == 1999);
111 TEST_CHECK(d2->bar == 3000 - d2->foo);
112
113 for (int i = 0; i < 2000; i++) {
114 seq_lookup_result_t res = seq_get(&s, (size_t)i);
115 TEST_CHECK(res.maybe_error == nullptr);
117 seq_dummy *d = (seq_dummy *)res.data;
118
119 SEQ_TEST_LOG("%d %d %d", i, d->foo, d->bar);
120 TEST_CHECK(d->foo == i);
121 TEST_CHECK(d->bar == 3000 - i);
122 };
123
124 RELEASE_CONTEXT(ctx);
125 SHUTDOWN_ENGINE(mm, engine);
126}
127
128/// Two pushes branched off one head must not see each other's element. The
129/// tail buffer is shared between versions, so without copy on write the
130/// second push clobbers the first branch's slot.
132 MAKE_ENGINE(mm, engine);
133 MAKE_CONTEXT(engine, ctx);
134
135 seq_t s = seq_empty(ctx);
136 for (int i = 0; i < 5; i++) {
137 seq_dummy *d = ALLOC(ctx, seq_dummy);
138 d->foo = i;
139 d->bar = i;
140 s = seq_push(&s, (seq_elem_t)d);
141 }
142
143 seq_dummy da = {.foo = 100, .bar = 1};
144 seq_dummy db = {.foo = 200, .bar = 2};
145
146 seq_t b1 = seq_push(&s, (seq_elem_t)&da);
147 seq_t b2 = seq_push(&s, (seq_elem_t)&db);
148
149 TEST_CHECK(s.len == 5);
150 TEST_CHECK(b1.len == 6);
151 TEST_CHECK(b2.len == 6);
152
153 seq_lookup_result_t r1 = seq_get(&b1, 5);
154 TEST_CHECK(r1.maybe_error == nullptr);
155 TEST_CHECK(r1.data == (seq_elem_t)&da);
156
157 seq_lookup_result_t r2 = seq_get(&b2, 5);
158 TEST_CHECK(r2.maybe_error == nullptr);
159 TEST_CHECK(r2.data == (seq_elem_t)&db);
160
161 RELEASE_CONTEXT(ctx);
162 SHUTDOWN_ENGINE(mm, engine);
163}
164
165/// Two branches that each promote their own diverging tail into the same
166/// root slot must not overwrite each other. 96 shared elements put the base
167/// at a 64 element tree plus a full tail; 33 pushes per branch force two
168/// promotions each, the second landing in the same fringe slot of a root
169/// that used to be shared.
171 MAKE_ENGINE(mm, engine);
172 MAKE_CONTEXT(engine, ctx);
173
174 int *vals = ALLOCN(ctx, int, 96);
175 seq_t s = seq_empty(ctx);
176 for (int i = 0; i < 96; i++) {
177 vals[i] = i;
178 s = seq_push(&s, (seq_elem_t)&vals[i]);
179 }
180
181 int *b1_vals = ALLOCN(ctx, int, 33);
182 int *b2_vals = ALLOCN(ctx, int, 33);
183 seq_t b1 = s;
184 seq_t b2 = s;
185 for (int i = 0; i < 33; i++) {
186 b1_vals[i] = 1000 + i;
187 b2_vals[i] = 2000 + i;
188 b1 = seq_push(&b1, (seq_elem_t)&b1_vals[i]);
189 b2 = seq_push(&b2, (seq_elem_t)&b2_vals[i]);
190 }
191
192 TEST_CHECK(s.len == 96);
193 TEST_CHECK(b1.len == 129);
194 TEST_CHECK(b2.len == 129);
195
196 // The shared prefix must read the same through every head.
197 for (int i = 0; i < 96; i++) {
198 TEST_CHECK(seq_get(&s, (size_t)i).data == (seq_elem_t)&vals[i]);
199 TEST_CHECK(seq_get(&b1, (size_t)i).data == (seq_elem_t)&vals[i]);
200 TEST_CHECK(seq_get(&b2, (size_t)i).data == (seq_elem_t)&vals[i]);
201 }
202
203 // Each branch must read only its own suffix.
204 for (int i = 0; i < 33; i++) {
205 TEST_CHECK(seq_get(&b1, (size_t)(96 + i)).data == (seq_elem_t)&b1_vals[i]);
206 TEST_CHECK(seq_get(&b2, (size_t)(96 + i)).data == (seq_elem_t)&b2_vals[i]);
207 }
208
209 RELEASE_CONTEXT(ctx);
210 SHUTDOWN_ENGINE(mm, engine);
211}
#define TEST_CHECK(cond)
Definition acutest.h:95
#define RELEASE_CONTEXT(x)
Definition base.h:46
#define ASSERT_NOT_NULL(x)
Definition base.h:30
#define SHUTDOWN_ENGINE(mm, engine)
Definition base.h:38
#define MAKE_ENGINE(mm, engine)
Definition base.h:32
#define MAKE_CONTEXT(engine, x)
Definition base.h:42
#define ALLOCN(ctx, T, N)
Definition context.h:85
#define ALLOC(ctx, T)
Definition context.h:84
seq_t seq_empty(const srn_context_t *ctx)
Create an empty seq pinned to ctx.
Definition seq.c:100
seq_lookup_result_t seq_get(const seq_t *seq, size_t n)
Negative index is not supported.
Definition seq.c:205
seq_t seq_push(const seq_t *seq, seq_elem_t x)
Definition seq.c:113
This is an implementation of bit - partitioned, persistent, immutable sequence For more information,...
void * seq_elem_t
We use generic pointers to refer to internal nodes, leaf nodes and even elements.
Definition seq.h:131
static void test_seq_branching_promotion()
Two branches that each promote their own diverging tail into the same root slot must not overwrite ea...
Definition seq_tests.h:170
void test_seq_push_more_than_32()
Definition seq_tests.h:83
static void test_seq_branching_push()
Two pushes branched off one head must not see each other's element.
Definition seq_tests.h:131
void test_seq_push_less_than_32()
Definition seq_tests.h:59
static void test_seq_empty()
Definition seq_tests.h:45
#define SEQ_TEST_LOG(...)
Definition seq_tests.h:37
seq_elem_t data
Definition seq.h:135
Definition seq.h:155
size_t len
logical length.
Definition seq.h:161
seq_node_t * root
NULL means “all data is in tail”
Definition seq.h:167
seq_elem_t * tail
small tail array for fast push/pop.
Definition seq.h:170
uint16_t tail_len
0..SEQ_BR
Definition seq.h:163