Skip to content

Commit d2cb24d

Browse files
committed
luzer: introduce oneof() in FuzzedDataProvider
The patch introduces a new FDP method - `oneof()`, it returns a random element in the specified Lua array: ``` > fdp:oneof({'clickhouse', 'ydb', 'tarantool'}) --- - tarantool ... > ``` `oneof()` with empty table returns a `nil`.
1 parent 9aabda7 commit d2cb24d

File tree

5 files changed

+59
-1
lines changed

5 files changed

+59
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Examples with tests.
1616
- Documentation with usecases, API etc.
1717
- Support command-line options.
18+
- Method `oneof()` in FuzzedDataProvider.
1819

1920
### Changed
2021

docs/api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ The `FuzzedDataProvider` then supports the following functions:
6868
If there's no input data left, always returns 0.
6969
- `remaining_bytes()` - returns the number of unconsumed bytes in the fuzzer
7070
input.
71+
- `oneof()` - returns a random element in the specified Lua array. With empty
72+
table `oneof()` returns a `nil` value.
7173

7274
Examples:
7375

luzer/fuzzed_data_provider.cc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ extern "C" {
1111
#include "lauxlib.h"
1212
#include "lualib.h"
1313
#include <float.h>
14+
15+
int table_nkeys(lua_State *L, int idx);
16+
1417
#ifdef __cplusplus
1518
} /* extern "C" */
1619
#endif
@@ -208,6 +211,31 @@ luaL_remaining_bytes(lua_State *L)
208211
return 1;
209212
}
210213

214+
/* Returns a random element of the specified array. */
215+
NO_SANITIZE static int
216+
luaL_oneof(lua_State *L)
217+
{
218+
lua_userdata_t *lfdp;
219+
lfdp = (lua_userdata_t *)luaL_checkudata(L, 1, FDP_LUA_UDATA_NAME);
220+
luaL_checktype(L, 2, LUA_TTABLE);
221+
222+
int len = 0;
223+
#if LUA_VERSION_NUM == 501
224+
len = table_nkeys(L, 2);
225+
#else
226+
len = lua_rawlen(L, 2);
227+
#endif
228+
if (len == 0) {
229+
lua_pushnil(L);
230+
return 1;
231+
}
232+
int idx = lfdp->fdp->ConsumeIntegralInRange(1, len);
233+
lua_pushinteger(L, idx);
234+
lua_gettable(L, -2);
235+
236+
return 1;
237+
}
238+
211239
NO_SANITIZE static int close(lua_State *L) {
212240
lua_userdata_t *lfdp;
213241
lfdp = (lua_userdata_t *)luaL_checkudata(L, 1, FDP_LUA_UDATA_NAME);
@@ -233,6 +261,7 @@ const luaL_Reg methods[] =
233261
{ "consume_integers", luaL_consume_integers },
234262
{ "consume_probability", luaL_consume_probability },
235263
{ "remaining_bytes", luaL_remaining_bytes },
264+
{ "oneof", luaL_oneof },
236265
{ "__gc", close },
237266
{ "__tostring", tostring },
238267
{ NULL, NULL }

luzer/luzer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ load_custom_mutator_lib(void) {
315315
}
316316

317317
/* Find amount of fields in the table on the top of the stack. */
318-
NO_SANITIZE static int
318+
NO_SANITIZE int
319319
table_nkeys(lua_State *L, int idx)
320320
{
321321
int len = 0;

luzer/tests/test_unit.lua

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,32 @@ assert(p1 >= 0 and p2 >= 0)
174174
assert(p1 <= 1 and p2 <= 1)
175175
assert(p1 ~= p2)
176176

177+
-- luzer.FuzzedDataProvider.oneof()
178+
fdp = luzer.FuzzedDataProvider("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
179+
assert(type(fdp.oneof) == "function")
180+
181+
-- Call `oneof()` with no values should raise an error.
182+
ok, err = pcall(fdp.oneof, fdp)
183+
assert(ok == false)
184+
assert(err:match("table expected, got no value"), err)
185+
186+
-- Call `oneof()` with empty table returns a `nil`.
187+
local n = fdp:oneof({})
188+
assert(n == nil)
189+
190+
-- Call `oneof()` with numbers.
191+
local a = 3
192+
local b = 4
193+
n = fdp:oneof({a, b})
194+
assert(type(n) == "number" and (n == a or n == b))
195+
196+
-- Call `oneof()` with strings.
197+
local str1 = "Python"
198+
local str2 = "Lua"
199+
local str = fdp:oneof({str1, str2})
200+
print(("'%s'"):format(str))
201+
assert(type(str) == "string" and (str == str1 or str == str2))
202+
177203
local function custom_mutator(data, size, max_size, seed)
178204
assert(type(data) == "string")
179205
assert(type(size) == "number")

0 commit comments

Comments
 (0)