Skip to content

Commit

Permalink
Merge pull request #118 from mrc0mmand/unit-tests
Browse files Browse the repository at this point in the history
Introduce unit tests for df_rand_* stuff
  • Loading branch information
evverx authored Jul 4, 2022
2 parents a47afe4 + ea21737 commit 5b0ed15
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 58 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/run-tests.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#!/bin/bash

set -ex
set -o pipefail

ninja -C ./build test

dfuzzer=("dfuzzer")
if [[ "$TYPE" == valgrind ]]; then
Expand Down
1 change: 1 addition & 0 deletions .packit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ actions:
# Drop the "sources" file so rebase-helper doesn't think we're a dist-git
- "rm -fv .packit_rpm/sources"
- sed -i '1 i%define _unpackaged_files_terminate_build 0' .packit_rpm/dfuzzer.spec
- sed -i '/^%meson$/a%meson_test' .packit_rpm/dfuzzer.spec
- sed -i 's/^%meson$/%meson --werror -Ddfuzzer-test-server=true/' .packit_rpm/dfuzzer.spec

jobs:
Expand Down
26 changes: 26 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ project('dfuzzer', 'c',
],
)

tests = []

libgio = dependency('gio-2.0', required : true)
xsltproc = find_program('xsltproc', required: false)

Expand All @@ -21,6 +23,7 @@ config_h = configure_file(
add_project_arguments('-include', 'config.h', language : 'c')

subdir('src')
subdir('test')

executable(
'dfuzzer',
Expand Down Expand Up @@ -66,3 +69,26 @@ if get_option('dfuzzer-test-server')
endif

install_data('src/dfuzzer.conf', install_dir : get_option('sysconfdir'))

foreach tuple : tests
sources = tuple[0]
name = '@0@'.format(sources[0]).split('/')[-1].split('.')[0]

exe = executable(
name,
dfuzzer_util_sources + sources,
include_directories : include_directories('src/'),
dependencies : [libgio],
)

# See: https://docs.gtk.org/glib/testing.html#using-meson
test(name, exe,
timeout : 60,
env : [
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
]
)
endforeach

# vi: sw=8 ts=8 et:
2 changes: 1 addition & 1 deletion src/dfuzzer.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ static int df_fuzz(GDBusConnection *dcon, const char *name, const char *object,
int rv = DF_BUS_OK;

// initialization of random module
df_rand_init();
df_rand_init(time(NULL));

// Sanity check fuzzing target
if (isempty(name) || isempty(object) || isempty(interface)) {
Expand Down
7 changes: 5 additions & 2 deletions src/meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
dfuzzer_sources = files(
dfuzzer_util_sources = files(
'bus.c',
'bus.h',
'dfuzzer.c',
'fuzz.c',
'fuzz.h',
'introspection.c',
Expand All @@ -16,6 +15,10 @@ dfuzzer_sources = files(
'util.h',
)

dfuzzer_sources = dfuzzer_util_sources + files(
'dfuzzer.c',
)

dfuzzer_test_server_sources = files(
'dfuzzer-test-server.c',
)
13 changes: 8 additions & 5 deletions src/rand.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ static struct external_dictionary df_external_dictionary;
* numbers generators.
* @param buf_size Maximum buffer size for generated strings (in Bytes)
*/
void df_rand_init()
void df_rand_init(unsigned int seed)
{
srand(time(NULL)); // for int rand()
srandom(time(NULL)); // for long int random()
srand(seed);
srandom(seed);
}

int df_rand_load_external_dictionary(const char *filename)
Expand Down Expand Up @@ -445,7 +445,7 @@ gdouble df_rand_gdouble(guint64 iteration)
}
}

static gunichar df_rand_unichar(guint16 *width)
gunichar df_rand_unichar(guint16 *width)
{
gunichar uc = 0;

Expand Down Expand Up @@ -624,7 +624,10 @@ int df_rand_dbus_objpath_string(gchar **buf, guint64 iteration)
* (each element needs at least two characters, hence size/2) and
* we need at least one element (hence +-1). With that, generate
* a pseudo-random number of elements in interval <1, size/2> */
nelem = (rand() % (size / 2 - 1)) + 1;
g_assert(size >= 2);
/* Set the number of elements to 1 if size is < 4 to avoid dividing
* by zero */
nelem = size < 4 ? 1 : (rand() % (size / 2 - 1)) + 1;

ret = g_try_new(gchar, size + 1);
if (!ret)
Expand Down
53 changes: 3 additions & 50 deletions src/rand.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@ struct external_dictionary {
char **strings;
};

/**
* @function Initializes global flag variables and seeds pseudo-random
* numbers generators.
*/
void df_rand_init();
void df_rand_init(unsigned int seed);
int df_rand_load_external_dictionary(const char *filename);

GVariant *df_generate_random_basic(const GVariantType *type, guint64 iteration);
Expand Down Expand Up @@ -87,54 +83,11 @@ guint64 df_rand_guint64(guint64 iteration);
*/
gdouble df_rand_gdouble(guint64 iteration);

/**
* @function Allocates memory for pseudo-random string of size counted
* by adding generated pseudo-random number from interval <0, CHAR_MAX>
* to df_str_len (this mechanism is responsible for generating bigger strings
* by every call of df_rand_string()). Then pseudo-random string is generated
* and stored in buf. At the beginning strings from global array df_str_def
* are used. Warning: buf should be freed outside this module by callee
* of this function.
* @param buf Address of pointer on buffer where generated string
* will be stored
* @return 0 on success, -1 on error
*/
int df_rand_string(gchar **buf, guint64 iteration);
gunichar df_rand_unichar(guint16 *width);

/**
* @function Allocates memory for pseudo-random object path string of size
* counted by adding 1 to size variable on every call of function to maximum
* size of MAX_OBJECT_PATH_LENGTH. On every call pseudo-random object path string is generated
* into buf buffer.
* Warning: buf should be freed outside this module by callee of this
* function.
* @param buf Address of pointer on buffer where generated object path string
* will be stored
* @return 0 on success, -1 on error
*/
int df_rand_string(gchar **buf, guint64 iteration);
int df_rand_dbus_objpath_string(gchar **buf, guint64 iteration);

/**
* @function Allocates memory for pseudo-random signature string of size
* counted by adding 1 to size variable on every call of function to maximum
* size of MAX_SIGNATURE_LENGTH. On every call pseudo-random signature string is generated
* by random access into global variable df_sig_def which contains all D-Bus
* signatures and copying signature into buf buffer.
* Warning: buf should be freed outside this module by callee of this
* function.
* @param buf Address of pointer on buffer where generated signature string
* will be stored
* @return 0 on success, -1 on error
*/
int df_rand_dbus_signature_string(gchar **buf, guint64 iteration);

/**
* @function Creates Gvariant containing pseudo-random string. At the beginning
* strings from global array df_str_def are used.
* @param var Address of pointer on GVariant where new Gvariant value
* will be stored
* @return 0 on success, -1 on error
*/
int df_rand_GVariant(GVariant **var, guint64 iteration);

/**
Expand Down
5 changes: 5 additions & 0 deletions test/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
tests += [
[files('test-rand.c')],
]

# vi: sw=8 ts=8 et:
130 changes: 130 additions & 0 deletions test/test-rand.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#include <gio/gio.h>
#include <glib.h>
#include <stdio.h>

#include "rand.h"
#include "util.h"

#define RAND_TEST_ITERATIONS 5000

static void test_df_rand_unichar(void)
{
guint16 width;
gunichar uc;

/* Test if the function asserts on invalid width */
if (g_test_subprocess()) {
/* This section runs in a subprocess */
width = 5;

(void) df_rand_unichar(&width);
}

/* Respawn the current test in a subprocess */
g_test_trap_subprocess (NULL, 0, 0);
/* The forked process above should have failed */
g_test_trap_assert_failed();

/* Test explicit (and valid) 1 - 4 B wide characters */
for (guint16 i = 1; i < 5; i++) {
uc = 0;
width = i;

uc = df_rand_unichar(&width);

/* The returned unichar should be in an UTF-8 range */
g_assert_true(uc <= 0x10FFFF);
/* And the width should remain unchanged */
g_assert_true(i == width);
}

/* Test width == 0, which should give us a pseudo-random 1 - 4 B wide unichar */
for (guint32 i = 0; i < RAND_TEST_ITERATIONS; i++) {
width = uc = 0;

uc = df_rand_unichar(&width);

/* The returned unichar should be in an UTF-8 range */
g_assert_true(uc <= 0x10FFFF);
/* And the width should be in a valid range */
g_assert_true(width > 0 && width < 5);
}
}

static void test_df_rand_string(void)
{
for (guint32 i = 0; i < RAND_TEST_ITERATIONS; i++) {
g_autoptr(gchar) str = NULL;
/* Test the "upper" guint64 interval in the second half of the iterations */
guint64 iteration = i < RAND_TEST_ITERATIONS / 2 ? i : (guint64) g_test_rand_int_range(0, G_MAXINT32) + G_MAXINT32;

g_assert_true(df_rand_string(&str, iteration) == 0);
g_assert_nonnull(str);
}
}

static void test_df_rand_dbus_objpath_string(void)
{
for (guint32 i = 0; i < RAND_TEST_ITERATIONS; i++) {
g_autoptr(gchar) str = NULL;
/* Test the "upper" guint64 interval in the second half of the iterations */
guint64 iteration = i < RAND_TEST_ITERATIONS / 2 ? i : (guint64) g_test_rand_int_range(0, G_MAXINT32) + G_MAXINT32;

g_assert_true(df_rand_dbus_objpath_string(&str, iteration) == 0);
g_assert_nonnull(str);
}

/* Test certain specific/problematic cases */
g_autoptr(gchar) str = NULL;

g_assert_true(df_rand_dbus_objpath_string(&str, df_fuzz_get_buffer_length() - 2) == 0);
g_assert_nonnull(str);
}

static void test_df_rand_dbus_signature_string(void)
{
for (guint32 i = 0; i < RAND_TEST_ITERATIONS; i++) {
g_autoptr(gchar) str = NULL;
/* Test the "upper" guint64 interval in the second half of the iterations */
guint64 iteration = i < RAND_TEST_ITERATIONS / 2 ? i : (guint64) g_test_rand_int_range(0, G_MAXINT32) + G_MAXINT32;

g_assert_true(df_rand_dbus_signature_string(&str, iteration) == 0);
g_assert_true(g_variant_is_signature(str));
g_assert_nonnull(str);
}
}

static void test_df_rand_GVariant(void)
{
for (guint32 i = 0; i < RAND_TEST_ITERATIONS; i++) {
g_autoptr(GVariant) variant = NULL;
/* Test the "upper" guint64 interval in the second half of the iterations */
guint64 iteration = i < RAND_TEST_ITERATIONS / 2 ? i : (guint64) g_test_rand_int_range(0, G_MAXINT32) + G_MAXINT32;

g_assert_true(df_rand_GVariant(&variant, iteration) == 0);
g_assert_nonnull(variant);
}
}

int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
/* Init our internal pseudo-random number generators
*
* Since we can't access the g_test_*() seed directly, let's use one
* of the g_test_rand*() functions that generate reproducible numbers
* (using the internal seed), so a possible test crash can be later
* reproduced using the `--seed xxx` option
*
* See: https://docs.gtk.org/glib/func.test_rand_int.html
* */
df_rand_init(g_test_rand_int());

g_test_add_func("/df_rand/df_rand_unichar", test_df_rand_unichar);
g_test_add_func("/df_rand/df_rand_string", test_df_rand_string);
g_test_add_func("/df_rand/df_rand_dbus_objpath_string", test_df_rand_dbus_objpath_string);
g_test_add_func("/df_rand/df_rand_dbus_signature_string", test_df_rand_dbus_signature_string);
g_test_add_func("/df_rand/df_rand_GVariant", test_df_rand_GVariant);

return g_test_run();
}

0 comments on commit 5b0ed15

Please sign in to comment.