aboutsummaryrefslogtreecommitdiff
path: root/unit_tests/misc_tests.cpp
diff options
context:
space:
mode:
authorBenoit Germain <bnt.germain@gmail.com>2025-09-20 14:30:57 +0200
committerBenoit Germain <bnt.germain@gmail.com>2025-09-20 14:30:57 +0200
commitc52571736d852d2636bd285d19c613be5c706cff (patch)
tree50a1fc9b867197faa695c728008c8d0c7498b756 /unit_tests/misc_tests.cpp
parentdc3de6ef8d4bb1a8ce7b2932515a0f6287ab1e76 (diff)
downloadlanes-c52571736d852d2636bd285d19c613be5c706cff.tar.gz
lanes-c52571736d852d2636bd285d19c613be5c706cff.tar.bz2
lanes-c52571736d852d2636bd285d19c613be5c706cff.zip
Improve table and userdata conversions
* add convert_fallback and convert_max_attempts to global settings * if no __lanesconvert is available, use convert_fallback (can be useful for externally provided full userdata with fixed metatables) * only try conversion on non-deep and non-clonable userdata * conversion can be applied recursively, up to convert_max_attempts times * plus all the relevant unit tests of course
Diffstat (limited to 'unit_tests/misc_tests.cpp')
-rw-r--r--unit_tests/misc_tests.cpp193
1 files changed, 193 insertions, 0 deletions
diff --git a/unit_tests/misc_tests.cpp b/unit_tests/misc_tests.cpp
new file mode 100644
index 0000000..a2199aa
--- /dev/null
+++ b/unit_tests/misc_tests.cpp
@@ -0,0 +1,193 @@
1#include "_pch.hpp"
2
3#include "shared.h"
4
5// #################################################################################################
6// #################################################################################################
7
8TEST_CASE("misc.__lanesconvert.for_tables")
9{
10 LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
11 S.requireSuccess("lanes = require 'lanes'.configure()");
12
13 S.requireSuccess(
14 " l = lanes.linda()"
15 " t = setmetatable({}, {__lanesconvert = lanes.null})" // table with a nil-converter
16 " l:send('k', t)" // send the table
17 " key, out = l:receive('k')" // read it back
18 " assert(key == 'k' and type(out) == 'nil', 'got ' .. key .. ' ' .. tostring(out))" // should have become nil
19 );
20
21 S.requireSuccess(
22 " l = lanes.linda()"
23 " t = setmetatable({}, {__lanesconvert = 'decay'})" // table with a decay-converter
24 " l:send('k', t)" // send the table
25 " key, out = l:receive('k')" // read it back
26 " assert(key == 'k' and type(out) == 'userdata', 'got ' .. key .. ' ' .. tostring(out))" // should have become a light userdata
27 );
28
29 S.requireSuccess(
30 " l = lanes.linda()"
31 " t = setmetatable({}, {__lanesconvert = function(t, hint) return 'keeper' end})" // table with a string-converter
32 " l:send('k', t)" // send the table
33 " key, out = l:receive('k')" // read it back
34 " assert(key == 'k' and out == 'keeper')" // should become 'keeper', the hint that the function received
35 );
36
37 // make sure that a function that returns the original object causes an error (we don't want infinite loops during conversion)
38 S.requireFailure(
39 " l = lanes.linda()"
40 " t = setmetatable({}, {__lanesconvert = function(t, hint) return t end})" // table with a string-converter
41 " l:send('k', t)" // send the table, it should raise an error because the converter triggers an infinite loop
42 );
43}
44
45// #################################################################################################
46// #################################################################################################
47
48TEST_CASE("misc.__lanesconvert.for_userdata")
49{
50 LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
51 S.requireSuccess("lanes = require 'lanes'.configure()");
52 S.requireSuccess("fixture = require 'fixture'");
53
54 S.requireSuccess("u_tonil = fixture.newuserdata{__lanesconvert = lanes.null}; assert(type(u_tonil) == 'userdata')");
55 S.requireSuccess(
56 " l = lanes.linda()"
57 " l:send('k', u_tonil)" // send a full userdata with a nil-converter
58 " key, out = l:receive('k')" // read it back
59 " assert(key == 'k' and type(out) == 'nil')" // should become nil
60 );
61
62 S.requireSuccess("u_tolud = fixture.newuserdata{__lanesconvert = 'decay'}; assert(type(u_tolud) == 'userdata')");
63 S.requireSuccess(
64 " l = lanes.linda()"
65 " l:send('k', u_tolud)" // send a full userdata with a decay-converter
66 " key, out = l:receive('k')" // read it back
67 " assert(key == 'k' and type(out) == 'userdata' and getmetatable(out) == nil)" // should become a light userdata
68 );
69
70 S.requireSuccess("u_tostr = fixture.newuserdata{__lanesconvert = function() return 'yo' end}; assert(type(u_tostr) == 'userdata')");
71 S.requireSuccess(
72 " l = lanes.linda()"
73 " l:send('k', u_tostr)" // send a full userdata with a string-converter
74 " key, out = l:receive('k')" // read it back
75 " assert(key == 'k' and out == 'yo')" // should become 'yo'
76 );
77
78 // make sure that a function that returns the original object causes an error (we don't want infinite loops during conversion)
79 S.requireSuccess("u_toself = fixture.newuserdata{__lanesconvert = function(u) return u end}; assert(type(u_toself) == 'userdata')");
80 S.requireFailure(
81 " l = lanes.linda()"
82 " l:send('k', u_toself)" // send the userdata, it should raise an error because the converter triggers an infinite loop
83 );
84
85 // TODO: make sure that a deep userdata with a __lanesconvert isn't converted (because deep copy takes precedence)
86}
87
88// #################################################################################################
89// #################################################################################################
90
91TEST_CASE("misc.convert_fallback.unset")
92{
93 LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
94 S.requireSuccess("lanes = require 'lanes'.configure()");
95
96 S.requireSuccess(
97 " l = lanes.linda()"
98 " l:send('k', {})" // send a table without a metatable
99 " key, out = l:receive('k')" // read it back
100 " assert(key == 'k' and type(out) == 'table')" // should not change
101 );
102
103 S.requireSuccess("fixture = require 'fixture'; u = fixture.newuserdata(); assert(type(u) == 'userdata')");
104 S.requireFailure(
105 " l = lanes.linda()"
106 " l:send('k', u)" // send a full userdata without a metatable, should fail
107 );
108}
109
110// #################################################################################################
111// #################################################################################################
112
113TEST_CASE("misc.convert_fallback.decay")
114{
115 LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
116 S.requireSuccess("lanes = require 'lanes'.configure{convert_fallback = 'decay'}");
117 S.requireSuccess("fixture = require 'fixture'");
118
119 S.requireSuccess(
120 " l = lanes.linda()"
121 " l:send('k', {})" // send a table without a metatable
122 " key, out = l:receive('k')" // read it back
123 " assert(key == 'k' and type(out) == 'userdata' and getmetatable(out) == nil)" // should have become a light userdata
124 );
125
126 S.requireSuccess("u = fixture.newuserdata(); assert(type(u) == 'userdata')");
127 S.requireSuccess(
128 " l = lanes.linda()"
129 " l:send('k', u)" // send a non-copyable non-deep full userdata
130 " key, out = l:receive('k')" // read it back
131 " assert(key == 'k' and type(out) == 'userdata' and getmetatable(out) == nil)" // should have become a light userdata
132 );
133}
134
135// #################################################################################################
136// #################################################################################################
137
138TEST_CASE("misc.convert_fallback.convert_no_nil")
139{
140 LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
141 S.requireSuccess("lanes = require 'lanes'; lanes.configure{convert_fallback = lanes.null}");
142
143 S.requireSuccess(
144 " l = lanes.linda()"
145 " l:send('k', {})" // send a table without a metatable
146 " key, out = l:receive('k')" // read it back
147 " assert(key == 'k' and type(out) == 'nil')" // should have become nil
148 );
149
150 S.requireSuccess(
151 " l = lanes.linda()"
152 " t = setmetatable({}, {__lanesconvert = 'decay'})" // override global converter with our own
153 " l:send('k', t)" // send the table
154 " key, out = l:receive('k')" // read it back
155 " assert(key == 'k' and type(out) == 'userdata', 'got ' .. key .. ' ' .. tostring(out))" // should have become a light userdata
156 );
157}
158
159// #################################################################################################
160// #################################################################################################
161
162TEST_CASE("misc.convert_max_attempts.is_respected")
163{
164 LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
165 S.requireSuccess("lanes = require 'lanes'; lanes.configure{convert_max_attempts = 3}");
166 S.requireSuccess("l = lanes.linda()");
167
168 S.requireSuccess(
169 " t = setmetatable({n=1}, {__lanesconvert = function(t, hint) t.n = t.n - 1 return t.n > 0 and t or 'done' end})" // table with a string-converter
170 " l:send('k', t)" // send the table
171 " key, out = l:receive('k')" // read it back
172 " assert(key == 'k' and out == 'done', 'got ' .. key .. ' ' .. tostring(out))" // should have stayed a table
173 );
174
175 S.requireSuccess(
176 " t = setmetatable({n=2}, {__lanesconvert = function(t, hint) t.n = t.n - 1 return t.n > 0 and t or 'done' end})" // table with a string-converter
177 " l:send('k', t)" // send the table
178 " key, out = l:receive('k')" // read it back
179 " assert(key == 'k' and out == 'done', 'got ' .. key .. ' ' .. tostring(out))" // should have stayed a table
180 );
181
182 S.requireSuccess(
183 " t = setmetatable({n=3}, {__lanesconvert = function(t, hint) t.n = t.n - 1 return t.n > 0 and t or 'done' end})" // table with a string-converter
184 " l:send('k', t)" // send the table
185 " key, out = l:receive('k')" // read it back
186 " assert(key == 'k' and out == 'done', 'got ' .. key .. ' ' .. tostring(out))" // should have stayed a table
187 );
188
189 S.requireFailure(
190 " t = setmetatable({n=4}, {__lanesconvert = function(t, hint) t.n = t.n - 1 return t.n > 0 and t or 'done' end})" // table with a string-converter
191 " l:send('k', t)" // send the table, it should raise an error because the converter retries too many times
192 );
193}