From a09929c26535abc512883979189d16d25b5a3f7f Mon Sep 17 00:00:00 2001
From: Dominik Loidolt <dominik.loidolt@univie.ac.at>
Date: Tue, 20 Aug 2024 11:32:05 +0200
Subject: [PATCH] Refactor: chunk_round_trip() test

---
 lib/common/cmp_max_used_bits.h      |   1 +
 test/cmp_decmp/test_cmp_decmp.c     | 634 +++++++++++++++-------------
 test/fuzz/fuzz_compression.c        |   7 +-
 test/fuzz/fuzz_data_producer.c      |   3 +-
 test/fuzz/fuzz_helpers.c            |  49 ---
 test/fuzz/fuzz_helpers.h            |  36 --
 test/fuzz/fuzz_round_trip.c         | 102 +----
 test/fuzz/meson.build               |   4 +-
 test/meson.build                    |   5 +-
 test/test_common/chunk_round_trip.c | 133 ++++++
 test/test_common/chunk_round_trip.h |  34 ++
 test/test_common/meson.build        |   1 +
 test/test_common/test_common.c      |  14 +-
 test/test_common/test_common.h      |   2 +-
 14 files changed, 541 insertions(+), 484 deletions(-)
 delete mode 100644 test/fuzz/fuzz_helpers.c
 create mode 100644 test/test_common/chunk_round_trip.c
 create mode 100644 test/test_common/chunk_round_trip.h

diff --git a/lib/common/cmp_max_used_bits.h b/lib/common/cmp_max_used_bits.h
index 21e7dd4..c13a6d6 100644
--- a/lib/common/cmp_max_used_bits.h
+++ b/lib/common/cmp_max_used_bits.h
@@ -29,6 +29,7 @@
 /* predefined maximum used bits registry constants */
 extern const struct cmp_max_used_bits MAX_USED_BITS_SAFE;
 extern const struct cmp_max_used_bits MAX_USED_BITS_V1;
+#define MAX_USED_BITS MAX_USED_BITS_SAFE
 
 
 /**
diff --git a/test/cmp_decmp/test_cmp_decmp.c b/test/cmp_decmp/test_cmp_decmp.c
index be5da6e..915a8cf 100644
--- a/test/cmp_decmp/test_cmp_decmp.c
+++ b/test/cmp_decmp/test_cmp_decmp.c
@@ -26,6 +26,7 @@
 
 #include <unity.h>
 #include "../test_common/test_common.h"
+#include "../test_common/chunk_round_trip.h"
 
 #include <cmp_icu.h>
 #include <cmp_chunk.h>
@@ -58,7 +59,6 @@ void setUp(void)
 
 #ifdef HAS_TIME_H
 	seed = (uint64_t)(time(NULL) ^ getpid()  ^ (intptr_t)&setUp);
-	seed = 0;
 #else
 	seed = 1;
 #endif
@@ -73,8 +73,71 @@ void setUp(void)
 }
 
 
+/**
+ * @brief generate a geometric distribution (bernoulli trial with probability p)
+ *
+ * prob(k) =  p (1 - p)^k for k = 0, 1, 2, 3, ...
+ *
+ * @param p	probability of geometric distribution (0 < p <= 1)
+ *
+ * @returns random number following a geometric distribution
+ */
+
+static uint32_t cmp_rand_geometric(double p)
+{
+	double u = ldexp(cmp_rand32(), -32); /*see: https://www.pcg-random.org/using-pcg-c-basic.html */
+
+	if (p >= 1.0)
+		return 0;
+
+	return (uint32_t)(log(u) / log(1 - p));
+}
+
+
+/**
+ * @brief generate geometric distribution data with a specified number of bits
+ *
+ * @param n_bits	number of bits for the output (1 <= n_bits <= 32)
+ * @param extra		pointer to a double containing the probability of
+ *			geometric distribution (0 < p <= 1)
+ *
+ * @returns random number following a geometric distribution, masked with n_bits
+ */
+
+static uint32_t gen_geometric_data(uint32_t n_bits, void *extra)
+{
+	double *p = (double *)extra;
+	uint32_t mask;
+
+	TEST_ASSERT(n_bits > 0);
+	TEST_ASSERT(n_bits <= 32);
+	TEST_ASSERT_NOT_NULL(p);
+	TEST_ASSERT(*p > 0);
+	TEST_ASSERT(*p <= 1.0);
+
+	mask = ~0U >> (32 - n_bits);
+	return cmp_rand_geometric(*p) & mask;
+}
+
+
+/**
+ * @brief Generate uniform distribution data with a specified number of bits
+ *
+ * @param n_bits	number of bits for the output (1 <= n_bits <= 32)
+ * @param unused	unused parameter
+ *
+ * @returns random number following a uniform distribution, masked with n_bits
+ */
+
+static uint32_t gen_uniform_data(uint32_t n_bits, void* unused UNUSED)
+{
+	return cmp_rand_nbits(n_bits);
+}
+
+
 static size_t gen_ima_data(uint16_t *data, enum cmp_data_type data_type,
-			   uint32_t samples, const struct cmp_max_used_bits *max_used_bits)
+			   uint32_t samples, uint32_t (*gen_data_f)(uint32_t, void*),
+			   void *extra)
 {
 	uint32_t i;
 
@@ -84,35 +147,35 @@ static size_t gen_ima_data(uint16_t *data, enum cmp_data_type data_type,
 		switch (data_type) {
 		case DATA_TYPE_IMAGETTE:
 		case DATA_TYPE_IMAGETTE_ADAPTIVE:
-			max_data_bits = max_used_bits->nc_imagette;
+			max_data_bits = MAX_USED_BITS.nc_imagette;
 			break;
 		case DATA_TYPE_SAT_IMAGETTE:
 		case DATA_TYPE_SAT_IMAGETTE_ADAPTIVE:
-			max_data_bits = max_used_bits->saturated_imagette;
+			max_data_bits = MAX_USED_BITS.saturated_imagette;
 			break;
 		case DATA_TYPE_F_CAM_IMAGETTE:
 		case DATA_TYPE_F_CAM_IMAGETTE_ADAPTIVE:
-			max_data_bits = max_used_bits->fc_imagette;
+			max_data_bits = MAX_USED_BITS.fc_imagette;
 			break;
 		default:
 			TEST_FAIL();
 		}
 		for (i = 0; i < samples; i++)
-			data[i] = (uint16_t)cmp_rand_nbits(max_data_bits);
+			data[i] = (uint16_t)gen_data_f(max_data_bits, extra);
 	}
 	return sizeof(*data) * samples;
 }
 
 
 static size_t gen_nc_offset_data(struct offset *data, uint32_t samples,
-				 const struct cmp_max_used_bits *max_used_bits)
+				 uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].mean = cmp_rand_nbits(max_used_bits->nc_offset_mean);
-			data[i].variance = cmp_rand_nbits(max_used_bits->nc_offset_variance);
+			data[i].mean = gen_data_f(MAX_USED_BITS.nc_offset_mean, extra);
+			data[i].variance = gen_data_f(MAX_USED_BITS.nc_offset_variance, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -120,14 +183,14 @@ static size_t gen_nc_offset_data(struct offset *data, uint32_t samples,
 
 
 static size_t gen_fc_offset_data(struct offset *data, uint32_t samples,
-				 const struct cmp_max_used_bits *max_used_bits)
+				 uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].mean = cmp_rand_nbits(max_used_bits->fc_offset_mean);
-			data[i].variance = cmp_rand_nbits(max_used_bits->fc_offset_variance);
+			data[i].mean = gen_data_f(MAX_USED_BITS.fc_offset_mean, extra);
+			data[i].variance = gen_data_f(MAX_USED_BITS.fc_offset_variance, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -135,15 +198,16 @@ static size_t gen_fc_offset_data(struct offset *data, uint32_t samples,
 
 
 static size_t gen_nc_background_data(struct background *data, uint32_t samples,
-				     const struct cmp_max_used_bits *max_used_bits)
+				     uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].mean = cmp_rand_nbits(max_used_bits->nc_background_mean);
-			data[i].variance = cmp_rand_nbits(max_used_bits->nc_background_variance);
-			data[i].outlier_pixels = (__typeof__(data[i].outlier_pixels))cmp_rand_nbits(max_used_bits->nc_background_outlier_pixels);
+			data[i].mean = gen_data_f(MAX_USED_BITS.nc_background_mean, extra);
+			data[i].variance = gen_data_f(MAX_USED_BITS.nc_background_variance, extra);
+			data[i].outlier_pixels =
+				(__typeof__(data[i].outlier_pixels))gen_data_f(MAX_USED_BITS.nc_background_outlier_pixels, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -151,15 +215,16 @@ static size_t gen_nc_background_data(struct background *data, uint32_t samples,
 
 
 static size_t gen_fc_background_data(struct background *data, uint32_t samples,
-				     const struct cmp_max_used_bits *max_used_bits)
+				     uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].mean = cmp_rand_nbits(max_used_bits->fc_background_mean);
-			data[i].variance = cmp_rand_nbits(max_used_bits->fc_background_variance);
-			data[i].outlier_pixels = (__typeof__(data[i].outlier_pixels))cmp_rand_nbits(max_used_bits->fc_background_outlier_pixels);
+			data[i].mean = gen_data_f(MAX_USED_BITS.fc_background_mean, extra);
+			data[i].variance = gen_data_f(MAX_USED_BITS.fc_background_variance, extra);
+			data[i].outlier_pixels =
+				(__typeof__(data[i].outlier_pixels))gen_data_f(MAX_USED_BITS.fc_background_outlier_pixels, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -167,15 +232,17 @@ static size_t gen_fc_background_data(struct background *data, uint32_t samples,
 
 
 static size_t gen_smearing_data(struct smearing *data, uint32_t samples,
-				const struct cmp_max_used_bits *max_used_bits)
+				uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].mean = cmp_rand_nbits(max_used_bits->smearing_mean);
-			data[i].variance_mean = (__typeof__(data[i].variance_mean))cmp_rand_nbits(max_used_bits->smearing_variance_mean);
-			data[i].outlier_pixels = (__typeof__(data[i].outlier_pixels))cmp_rand_nbits(max_used_bits->smearing_outlier_pixels);
+			data[i].mean = gen_data_f(MAX_USED_BITS.smearing_mean, extra);
+			data[i].variance_mean =
+				(__typeof__(data[i].variance_mean))gen_data_f(MAX_USED_BITS.smearing_variance_mean, extra);
+			data[i].outlier_pixels =
+				(__typeof__(data[i].outlier_pixels))gen_data_f(MAX_USED_BITS.smearing_outlier_pixels, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -183,14 +250,15 @@ static size_t gen_smearing_data(struct smearing *data, uint32_t samples,
 
 
 static size_t gen_s_fx_data(struct s_fx *data, uint32_t samples,
-			    const struct cmp_max_used_bits *max_used_bits)
+			    uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].exp_flags = (__typeof__(data[i].exp_flags))cmp_rand_nbits(max_used_bits->s_exp_flags);
-			data[i].fx = cmp_rand_nbits(max_used_bits->s_fx);
+			data[i].exp_flags =
+				(__typeof__(data[i].exp_flags))gen_data_f(MAX_USED_BITS.s_exp_flags, extra);
+			data[i].fx = gen_data_f(MAX_USED_BITS.s_fx, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -198,15 +266,16 @@ static size_t gen_s_fx_data(struct s_fx *data, uint32_t samples,
 
 
 static size_t gen_s_fx_efx_data(struct s_fx_efx *data, uint32_t samples,
-				const struct cmp_max_used_bits *max_used_bits)
+				uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].exp_flags = (__typeof__(data[i].exp_flags))cmp_rand_nbits(max_used_bits->s_exp_flags);
-			data[i].fx = cmp_rand_nbits(max_used_bits->s_fx);
-			data[i].efx = cmp_rand_nbits(max_used_bits->s_efx);
+			data[i].exp_flags =
+				(__typeof__(data[i].exp_flags))gen_data_f(MAX_USED_BITS.s_exp_flags, extra);
+			data[i].fx = gen_data_f(MAX_USED_BITS.s_fx, extra);
+			data[i].efx = gen_data_f(MAX_USED_BITS.s_efx, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -214,16 +283,17 @@ static size_t gen_s_fx_efx_data(struct s_fx_efx *data, uint32_t samples,
 
 
 static size_t gen_s_fx_ncob_data(struct s_fx_ncob *data, uint32_t samples,
-				 const struct cmp_max_used_bits *max_used_bits)
+				 uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].exp_flags = (__typeof__(data[i].exp_flags))cmp_rand_nbits(max_used_bits->s_exp_flags);
-			data[i].fx = cmp_rand_nbits(max_used_bits->s_fx);
-			data[i].ncob_x = cmp_rand_nbits(max_used_bits->s_ncob);
-			data[i].ncob_y = cmp_rand_nbits(max_used_bits->s_ncob);
+			data[i].exp_flags =
+				(__typeof__(data[i].exp_flags))gen_data_f(MAX_USED_BITS.s_exp_flags, extra);
+			data[i].fx = gen_data_f(MAX_USED_BITS.s_fx, extra);
+			data[i].ncob_x = gen_data_f(MAX_USED_BITS.s_ncob, extra);
+			data[i].ncob_y = gen_data_f(MAX_USED_BITS.s_ncob, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -231,19 +301,20 @@ static size_t gen_s_fx_ncob_data(struct s_fx_ncob *data, uint32_t samples,
 
 
 static size_t gen_s_fx_efx_ncob_ecob_data(struct s_fx_efx_ncob_ecob *data, uint32_t samples,
-					  const struct cmp_max_used_bits *max_used_bits)
+					  uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].exp_flags = (__typeof__(data[i].exp_flags))cmp_rand_nbits(max_used_bits->s_exp_flags);
-			data[i].fx = cmp_rand_nbits(max_used_bits->s_fx);
-			data[i].ncob_x = cmp_rand_nbits(max_used_bits->s_ncob);
-			data[i].ncob_y = cmp_rand_nbits(max_used_bits->s_ncob);
-			data[i].efx = cmp_rand_nbits(max_used_bits->s_efx);
-			data[i].ecob_x = cmp_rand_nbits(max_used_bits->s_ecob);
-			data[i].ecob_y = cmp_rand_nbits(max_used_bits->s_ecob);
+			data[i].exp_flags =
+				(__typeof__(data[i].exp_flags))gen_data_f(MAX_USED_BITS.s_exp_flags, extra);
+			data[i].fx = gen_data_f(MAX_USED_BITS.s_fx, extra);
+			data[i].ncob_x = gen_data_f(MAX_USED_BITS.s_ncob, extra);
+			data[i].ncob_y = gen_data_f(MAX_USED_BITS.s_ncob, extra);
+			data[i].efx = gen_data_f(MAX_USED_BITS.s_efx, extra);
+			data[i].ecob_x = gen_data_f(MAX_USED_BITS.s_ecob, extra);
+			data[i].ecob_y = gen_data_f(MAX_USED_BITS.s_ecob, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -251,26 +322,26 @@ static size_t gen_s_fx_efx_ncob_ecob_data(struct s_fx_efx_ncob_ecob *data, uint3
 
 
 static size_t gen_f_fx_data(struct f_fx *data, uint32_t samples,
-			    const struct cmp_max_used_bits *max_used_bits)
+			    uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data)
 		for (i = 0; i < samples; i++)
-			data[i].fx = cmp_rand_nbits(max_used_bits->f_fx);
+			data[i].fx = gen_data_f(MAX_USED_BITS.f_fx, extra);
 	return sizeof(*data) * samples;
 }
 
 
 static size_t gen_f_fx_efx_data(struct f_fx_efx *data, uint32_t samples,
-				const struct cmp_max_used_bits *max_used_bits)
+				uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].fx = cmp_rand_nbits(max_used_bits->f_fx);
-			data[i].efx = cmp_rand_nbits(max_used_bits->f_efx);
+			data[i].fx = gen_data_f(MAX_USED_BITS.f_fx, extra);
+			data[i].efx = gen_data_f(MAX_USED_BITS.f_efx, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -278,15 +349,15 @@ static size_t gen_f_fx_efx_data(struct f_fx_efx *data, uint32_t samples,
 
 
 static size_t gen_f_fx_ncob_data(struct f_fx_ncob *data, uint32_t samples,
-				 const struct cmp_max_used_bits *max_used_bits)
+				 uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].fx = cmp_rand_nbits(max_used_bits->f_fx);
-			data[i].ncob_x = cmp_rand_nbits(max_used_bits->f_ncob);
-			data[i].ncob_y = cmp_rand_nbits(max_used_bits->f_ncob);
+			data[i].fx = gen_data_f(MAX_USED_BITS.f_fx, extra);
+			data[i].ncob_x = gen_data_f(MAX_USED_BITS.f_ncob, extra);
+			data[i].ncob_y = gen_data_f(MAX_USED_BITS.f_ncob, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -294,18 +365,18 @@ static size_t gen_f_fx_ncob_data(struct f_fx_ncob *data, uint32_t samples,
 
 
 static size_t gen_f_fx_efx_ncob_ecob_data(struct f_fx_efx_ncob_ecob *data, uint32_t samples,
-					  const struct cmp_max_used_bits *max_used_bits)
+					  uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].fx = cmp_rand_nbits(max_used_bits->f_fx);
-			data[i].ncob_x = cmp_rand_nbits(max_used_bits->f_ncob);
-			data[i].ncob_y = cmp_rand_nbits(max_used_bits->f_ncob);
-			data[i].efx = cmp_rand_nbits(max_used_bits->f_efx);
-			data[i].ecob_x = cmp_rand_nbits(max_used_bits->f_ecob);
-			data[i].ecob_y = cmp_rand_nbits(max_used_bits->f_ecob);
+			data[i].fx = gen_data_f(MAX_USED_BITS.f_fx, extra);
+			data[i].ncob_x = gen_data_f(MAX_USED_BITS.f_ncob, extra);
+			data[i].ncob_y = gen_data_f(MAX_USED_BITS.f_ncob, extra);
+			data[i].efx = gen_data_f(MAX_USED_BITS.f_efx, extra);
+			data[i].ecob_x = gen_data_f(MAX_USED_BITS.f_ecob, extra);
+			data[i].ecob_y = gen_data_f(MAX_USED_BITS.f_ecob, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -313,15 +384,15 @@ static size_t gen_f_fx_efx_ncob_ecob_data(struct f_fx_efx_ncob_ecob *data, uint3
 
 
 static size_t gen_l_fx_data(struct l_fx *data, uint32_t samples,
-			    const struct cmp_max_used_bits *max_used_bits)
+			    uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].exp_flags = cmp_rand_nbits(max_used_bits->l_exp_flags);
-			data[i].fx = cmp_rand_nbits(max_used_bits->l_fx);
-			data[i].fx_variance = cmp_rand_nbits(max_used_bits->l_fx_variance);
+			data[i].exp_flags = gen_data_f(MAX_USED_BITS.l_exp_flags, extra);
+			data[i].fx = gen_data_f(MAX_USED_BITS.l_fx, extra);
+			data[i].fx_variance = gen_data_f(MAX_USED_BITS.l_fx_variance, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -329,16 +400,16 @@ static size_t gen_l_fx_data(struct l_fx *data, uint32_t samples,
 
 
 static size_t gen_l_fx_efx_data(struct l_fx_efx *data, uint32_t samples,
-				const struct cmp_max_used_bits *max_used_bits)
+				uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	uint32_t i;
 
 	if (data) {
 		for (i = 0; i < samples; i++) {
-			data[i].exp_flags = cmp_rand_nbits(max_used_bits->l_exp_flags);
-			data[i].fx = cmp_rand_nbits(max_used_bits->l_fx);
-			data[i].efx = cmp_rand_nbits(max_used_bits->l_efx);
-			data[i].fx_variance = cmp_rand_nbits(max_used_bits->l_fx_variance);
+			data[i].exp_flags = gen_data_f(MAX_USED_BITS.l_exp_flags, extra);
+			data[i].fx = gen_data_f(MAX_USED_BITS.l_fx, extra);
+			data[i].efx = gen_data_f(MAX_USED_BITS.l_efx, extra);
+			data[i].fx_variance = gen_data_f(MAX_USED_BITS.l_fx_variance, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -346,19 +417,19 @@ static size_t gen_l_fx_efx_data(struct l_fx_efx *data, uint32_t samples,
 
 
 static size_t gen_l_fx_ncob_data(struct l_fx_ncob *data, uint32_t samples,
-				 const struct cmp_max_used_bits *max_used_bits)
+				 uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	if (data) {
 		uint32_t i;
 
 		for (i = 0; i < samples; i++) {
-			data[i].exp_flags = cmp_rand_nbits(max_used_bits->l_exp_flags);
-			data[i].fx = cmp_rand_nbits(max_used_bits->l_fx);
-			data[i].ncob_x = cmp_rand_nbits(max_used_bits->l_ncob);
-			data[i].ncob_y = cmp_rand_nbits(max_used_bits->l_ncob);
-			data[i].fx_variance = cmp_rand_nbits(max_used_bits->l_fx_variance);
-			data[i].cob_x_variance = cmp_rand_nbits(max_used_bits->l_cob_variance);
-			data[i].cob_y_variance = cmp_rand_nbits(max_used_bits->l_cob_variance);
+			data[i].exp_flags = gen_data_f(MAX_USED_BITS.l_exp_flags, extra);
+			data[i].fx = gen_data_f(MAX_USED_BITS.l_fx, extra);
+			data[i].ncob_x = gen_data_f(MAX_USED_BITS.l_ncob, extra);
+			data[i].ncob_y = gen_data_f(MAX_USED_BITS.l_ncob, extra);
+			data[i].fx_variance = gen_data_f(MAX_USED_BITS.l_fx_variance, extra);
+			data[i].cob_x_variance = gen_data_f(MAX_USED_BITS.l_cob_variance, extra);
+			data[i].cob_y_variance = gen_data_f(MAX_USED_BITS.l_cob_variance, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -366,22 +437,22 @@ static size_t gen_l_fx_ncob_data(struct l_fx_ncob *data, uint32_t samples,
 
 
 static size_t gen_l_fx_efx_ncob_ecob_data(struct l_fx_efx_ncob_ecob *data, uint32_t samples,
-					  const struct cmp_max_used_bits *max_used_bits)
+					  uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	if (data) {
 		uint32_t i;
 
 		for (i = 0; i < samples; i++) {
-			data[i].exp_flags = cmp_rand_nbits(max_used_bits->l_exp_flags);
-			data[i].fx = cmp_rand_nbits(max_used_bits->l_fx);
-			data[i].ncob_x = cmp_rand_nbits(max_used_bits->l_ncob);
-			data[i].ncob_y = cmp_rand_nbits(max_used_bits->l_ncob);
-			data[i].efx = cmp_rand_nbits(max_used_bits->l_efx);
-			data[i].ecob_x = cmp_rand_nbits(max_used_bits->l_ecob);
-			data[i].ecob_y = cmp_rand_nbits(max_used_bits->l_ecob);
-			data[i].fx_variance = cmp_rand_nbits(max_used_bits->l_fx_variance);
-			data[i].cob_x_variance = cmp_rand_nbits(max_used_bits->l_cob_variance);
-			data[i].cob_y_variance = cmp_rand_nbits(max_used_bits->l_cob_variance);
+			data[i].exp_flags = gen_data_f(MAX_USED_BITS.l_exp_flags, extra);
+			data[i].fx = gen_data_f(MAX_USED_BITS.l_fx, extra);
+			data[i].ncob_x = gen_data_f(MAX_USED_BITS.l_ncob, extra);
+			data[i].ncob_y = gen_data_f(MAX_USED_BITS.l_ncob, extra);
+			data[i].efx = gen_data_f(MAX_USED_BITS.l_efx, extra);
+			data[i].ecob_x = gen_data_f(MAX_USED_BITS.l_ecob, extra);
+			data[i].ecob_y = gen_data_f(MAX_USED_BITS.l_ecob, extra);
+			data[i].fx_variance = gen_data_f(MAX_USED_BITS.l_fx_variance, extra);
+			data[i].cob_x_variance = gen_data_f(MAX_USED_BITS.l_cob_variance, extra);
+			data[i].cob_y_variance = gen_data_f(MAX_USED_BITS.l_cob_variance, extra);
 		}
 	}
 	return sizeof(*data) * samples;
@@ -423,14 +494,16 @@ uint32_t generate_random_collection_hdr(struct collection_hdr *col, enum cmp_dat
  *			random collection
  * @param data_type	specifies the compression data type of the test data
  * @param samples	the number of random test data samples to generate
- * @param max_used_bits	pointer to a structure that tracks the maximum number of
- *			bits used
+ * @param gen_data_f	function pointer to a data generation function
+ * @param extra		pointer to additional data required by the data
+ *			generation function
  *
  * @return the size of the generated random collection in bytes
  */
 
 size_t generate_random_collection(struct collection_hdr *col, enum cmp_data_type data_type,
-				  uint32_t samples, const struct cmp_max_used_bits *max_used_bits)
+				  uint32_t samples, uint32_t (*gen_data_f)(uint32_t, void *),
+				  void *extra)
 {
 	size_t size;
 	void *science_data = NULL;
@@ -439,16 +512,6 @@ size_t generate_random_collection(struct collection_hdr *col, enum cmp_data_type
 		science_data = col->entry;
 
 	size = generate_random_collection_hdr(col, data_type, samples);
-#if 0
-	{	int i;
-
-		for (i = 0; i < size_of_a_sample(data_type)*samples; i++) {
-			if (col)
-				col->entry[i] = i;
-		}
-		return size+i;
-	}
-#endif
 
 	switch (data_type) {
 	case DATA_TYPE_IMAGETTE:
@@ -457,58 +520,64 @@ size_t generate_random_collection(struct collection_hdr *col, enum cmp_data_type
 	case DATA_TYPE_SAT_IMAGETTE_ADAPTIVE:
 	case DATA_TYPE_F_CAM_IMAGETTE:
 	case DATA_TYPE_F_CAM_IMAGETTE_ADAPTIVE:
-		size += gen_ima_data(science_data, data_type, samples, max_used_bits);
+		size += gen_ima_data(science_data, data_type, samples,
+				     gen_data_f, extra);
 		break;
 	case DATA_TYPE_OFFSET:
-		size += gen_nc_offset_data(science_data, samples, max_used_bits);
+		size += gen_nc_offset_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_BACKGROUND:
-		size += gen_nc_background_data(science_data, samples, max_used_bits);
+		size += gen_nc_background_data(science_data, samples,
+					       gen_data_f, extra);
 		break;
 	case DATA_TYPE_SMEARING:
-		size += gen_smearing_data(science_data, samples, max_used_bits);
+		size += gen_smearing_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_S_FX:
-		size += gen_s_fx_data(science_data, samples, max_used_bits);
+		size += gen_s_fx_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_S_FX_EFX:
-		size += gen_s_fx_efx_data(science_data, samples, max_used_bits);
+		size += gen_s_fx_efx_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_S_FX_NCOB:
-		size += gen_s_fx_ncob_data(science_data, samples, max_used_bits);
+		size += gen_s_fx_ncob_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_S_FX_EFX_NCOB_ECOB:
-		size += gen_s_fx_efx_ncob_ecob_data(science_data, samples, max_used_bits);
+		size += gen_s_fx_efx_ncob_ecob_data(science_data, samples,
+						    gen_data_f, extra);
 		break;
 	case DATA_TYPE_L_FX:
-		size += gen_l_fx_data(science_data, samples, max_used_bits);
+		size += gen_l_fx_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_L_FX_EFX:
-		size += gen_l_fx_efx_data(science_data, samples, max_used_bits);
+		size += gen_l_fx_efx_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_L_FX_NCOB:
-		size += gen_l_fx_ncob_data(science_data, samples, max_used_bits);
+		size += gen_l_fx_ncob_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_L_FX_EFX_NCOB_ECOB:
-		size += gen_l_fx_efx_ncob_ecob_data(science_data, samples, max_used_bits);
+		size += gen_l_fx_efx_ncob_ecob_data(science_data, samples,
+						    gen_data_f, extra);
 		break;
 	case DATA_TYPE_F_FX:
-		size += gen_f_fx_data(science_data, samples, max_used_bits);
+		size += gen_f_fx_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_F_FX_EFX:
-		size += gen_f_fx_efx_data(science_data, samples, max_used_bits);
+		size += gen_f_fx_efx_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_F_FX_NCOB:
-		size += gen_f_fx_ncob_data(science_data, samples, max_used_bits);
+		size += gen_f_fx_ncob_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_F_FX_EFX_NCOB_ECOB:
-		size += gen_f_fx_efx_ncob_ecob_data(science_data, samples, max_used_bits);
+		size += gen_f_fx_efx_ncob_ecob_data(science_data, samples,
+						    gen_data_f, extra);
 		break;
 	case DATA_TYPE_F_CAM_OFFSET:
-		size += gen_fc_offset_data(science_data, samples, max_used_bits);
+		size += gen_fc_offset_data(science_data, samples, gen_data_f, extra);
 		break;
 	case DATA_TYPE_F_CAM_BACKGROUND:
-		size += gen_fc_background_data(science_data, samples, max_used_bits);
+		size += gen_fc_background_data(science_data, samples,
+					       gen_data_f, extra);
 		break;
 	default:
 		TEST_FAIL();
@@ -534,14 +603,16 @@ struct chunk_def {
  *			random chunk
  * @param col_array	specifies which collections are contained in the chunk
  * @param array_elements	number of elements in the col_array
- * @param max_used_bits	pointer to a structure that tracks the maximum number of
- *			bits used
+ * @param gen_data_f	function pointer to a data generation function
+ * @param extra		pointer to additional data required by the data
+ *			generation function
  *
  * @return the size of the generated random chunk in bytes
  */
 
-static uint32_t generate_random_chunk(void *chunk, struct chunk_def col_array[], size_t array_elements,
-				    const struct cmp_max_used_bits *max_used_bits)
+static uint32_t generate_random_chunk(void *chunk, struct chunk_def col_array[],
+				      size_t array_elements,
+				      uint32_t (*gen_data_f)(uint32_t, void*), void *extra)
 {
 	size_t i;
 	uint32_t chunk_size = 0;
@@ -552,7 +623,8 @@ static uint32_t generate_random_chunk(void *chunk, struct chunk_def col_array[],
 			col = (struct collection_hdr *)((uint8_t *)chunk + chunk_size);
 
 		chunk_size += generate_random_collection(col, col_array[i].data_type,
-							 col_array[i].samples, max_used_bits);
+							 col_array[i].samples, gen_data_f,
+							 extra);
 	}
 	return chunk_size;
 }
@@ -583,45 +655,47 @@ void generate_random_rdcu_cfg(struct rdcu_cfg *rcfg)
 
 void generate_random_cmp_par(struct cmp_par *par)
 {
-	if (par) {
-		par->cmp_mode = cmp_rand_between(0, MAX_RDCU_CMP_MODE);
-		par->model_value = cmp_rand_between(0, MAX_MODEL_VALUE);
-		par->lossy_par = cmp_rand_between(0, MAX_ICU_ROUND);
-
-		par->nc_imagette = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-
-		par->s_exp_flags = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->s_fx = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->s_ncob = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->s_efx = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->s_ecob = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-
-		par->l_exp_flags = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->l_fx = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->l_ncob = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->l_efx = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->l_ecob = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->l_fx_cob_variance = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-
-		par->saturated_imagette = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-
-		par->nc_offset_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->nc_offset_variance = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->nc_background_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->nc_background_variance = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->nc_background_outlier_pixels = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-
-		par->smearing_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->smearing_variance_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->smearing_outlier_pixels = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-
-		par->fc_imagette = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->fc_offset_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->fc_offset_variance = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->fc_background_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->fc_background_variance = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-		par->fc_background_outlier_pixels = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
-	}
+	if (!par)
+		return;
+
+	par->cmp_mode = cmp_rand_between(0, MAX_RDCU_CMP_MODE);
+	par->model_value = cmp_rand_between(0, MAX_MODEL_VALUE);
+	par->lossy_par = cmp_rand_between(0, MAX_ICU_ROUND);
+
+	par->nc_imagette = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+
+	par->s_exp_flags = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->s_fx = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->s_ncob = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->s_efx = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->s_ecob = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+
+	par->l_exp_flags = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->l_fx = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->l_ncob = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->l_efx = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->l_ecob = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->l_fx_cob_variance = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+
+	par->saturated_imagette = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+
+	par->nc_offset_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->nc_offset_variance = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->nc_background_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->nc_background_variance = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->nc_background_outlier_pixels = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+
+	par->smearing_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->smearing_variance_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->smearing_outlier_pixels = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+
+	par->fc_imagette = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->fc_offset_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->fc_offset_variance = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->fc_background_mean = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->fc_background_variance = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+	par->fc_background_outlier_pixels = cmp_rand_between(MIN_NON_IMA_GOLOMB_PAR, MAX_NON_IMA_GOLOMB_PAR);
+
 }
 
 
@@ -726,7 +800,7 @@ void compression_decompression_like_rdcu(struct rdcu_cfg *rcfg)
 
 void test_random_round_trip_like_rdcu_compression(void)
 {
-	enum cmp_data_type data_type;
+	enum cmp_data_type data_type = DATA_TYPE_IMAGETTE;
 	enum cmp_mode cmp_mode;
 	struct rdcu_cfg rcfg;
 	enum {
@@ -736,22 +810,39 @@ void test_random_round_trip_like_rdcu_compression(void)
 	void *data_to_compress1 = malloc(MAX_DATA_TO_COMPRESS_SIZE);
 	void *data_to_compress2 = malloc(MAX_DATA_TO_COMPRESS_SIZE);
 	void *updated_model = calloc(1, MAX_DATA_TO_COMPRESS_SIZE);
+	int run;
 
-	for (data_type = 1; data_type <= DATA_TYPE_F_CAM_BACKGROUND; data_type++) {
-		/* printf("%s\n", data_type2string(data_type)); */
-		/* generate random data*/
+	for (run = 0; run < 2; run++) {
+		uint32_t (*gen_data_f)(uint32_t, void*);
+		void *extra;
+		double p = 0.01;
 		size_t size;
-		uint32_t samples = cmp_rand_between(1, UINT16_MAX/size_of_a_sample(data_type));
-		uint32_t model_value = cmp_rand_between(0, MAX_MODEL_VALUE);
+		uint32_t samples, model_value;
+
+		/* generate random data*/
+		switch (run) {
+		case 0:
+			gen_data_f = gen_uniform_data;
+			extra = NULL;
+			break;
+		case 1:
+			gen_data_f = gen_geometric_data;
+			extra = &p;
+			break;
+		default:
+			TEST_FAIL();
+		}
+		samples = cmp_rand_between(1, UINT16_MAX/size_of_a_sample(data_type));
+		model_value = cmp_rand_between(0, MAX_MODEL_VALUE);
 
 		if (!rdcu_supported_data_type_is_used(data_type))
 			continue;
 
-		size = gen_ima_data(NULL, data_type, samples, &MAX_USED_BITS_V1);
+		size = gen_ima_data(NULL, data_type, samples, gen_data_f, extra);
 		TEST_ASSERT(size <= MAX_DATA_TO_COMPRESS_SIZE);
-		size = gen_ima_data(data_to_compress1, data_type, samples, &MAX_USED_BITS_V1);
+		size = gen_ima_data(data_to_compress1, data_type, samples, gen_data_f, extra);
 		TEST_ASSERT(size <= MAX_DATA_TO_COMPRESS_SIZE);
-		size = gen_ima_data(data_to_compress2, data_type, samples, &MAX_USED_BITS_V1);
+		size = gen_ima_data(data_to_compress2, data_type, samples, gen_data_f, extra);
 		TEST_ASSERT(size <= MAX_DATA_TO_COMPRESS_SIZE);
 		/* for (cmp_mode = CMP_MODE_RAW; cmp_mode <= CMP_MODE_STUFF; cmp_mode++) { */
 		for (cmp_mode = CMP_MODE_RAW; cmp_mode <= CMP_MODE_DIFF_MULTI; cmp_mode++) {
@@ -832,91 +923,6 @@ void test_random_compression_decompress_rdcu_data(void)
 }
 
 
-static uint32_t chunk_round_trip(void *data, uint32_t data_size,
-				 void *model, void *up_model,
-				 uint32_t *cmp_data, uint32_t cmp_data_capacity,
-				 struct cmp_par *cmp_par, int use_decmp_buf, int use_decmp_up_model)
-{
-	uint32_t cmp_size;
-	void *model_cpy = NULL;
-
-	/* if in-place model update is used (up_model == model), the model
-	 * needed for decompression is destroyed; therefore we make a copy
-	 */
-	if (model) {
-		if (up_model == model) {
-			model_cpy = TEST_malloc(data_size);
-			memcpy(model_cpy, model, data_size);
-		} else {
-			model_cpy = model;
-		}
-	}
-
-	cmp_size = compress_chunk(data, data_size, model, up_model,
-				  cmp_data, cmp_data_capacity, cmp_par);
-
-#if 0
-	{ /* Compress a second time and check for determinism */
-		int32_t cSize2;
-		void *compressed2 = NULL;
-		void *up_model2 = NULL;
-
-		if (compressed)
-			compressed2 = FUZZ_malloc(compressedCapacity);
-
-		if (up_model)
-			up_model2 = FUZZ_malloc(srcSize);
-		cSize2 = compress_chunk((void *)src, srcSize, (void *)model, up_model2,
-					   compressed2, compressedCapacity, cmp_par);
-		FUZZ_ASSERT(cSize == cSize2);
-		FUZZ_ASSERT_MSG(!FUZZ_memcmp(compressed, compressed2, cSize), "Not deterministic!");
-		FUZZ_ASSERT_MSG(!FUZZ_memcmp(up_model, compressed2, cSize), "NO deterministic!");
-		free(compressed2);
-		free(up_model2);
-	}
-#endif
-	if (!cmp_is_error(cmp_size) && cmp_data) {
-		void *decmp_data = NULL;
-		void *up_model_decmp = NULL;
-		int decmp_size;
-
-		decmp_size = decompress_cmp_entiy((struct cmp_entity *)cmp_data, model_cpy, NULL, NULL);
-		TEST_ASSERT(decmp_size >= 0);
-		TEST_ASSERT_EQUAL((uint32_t)decmp_size, data_size);
-
-		if (use_decmp_buf)
-			decmp_data = TEST_malloc(data_size);
-		if (use_decmp_up_model)
-			up_model_decmp = TEST_malloc(data_size);
-
-		decmp_size = decompress_cmp_entiy((struct cmp_entity *)cmp_data, model_cpy,
-						  up_model_decmp, decmp_data);
-		TEST_ASSERT(decmp_size >= 0);
-		TEST_ASSERT((uint32_t)decmp_size == data_size);
-
-		if (use_decmp_buf) {
-			TEST_ASSERT_EQUAL_HEX8_ARRAY(data, decmp_data, data_size);
-			TEST_ASSERT(!memcmp(data, decmp_data, data_size));
-
-			/*
-			 * the model is only updated when the decompressed_data
-			 * buffer is set
-			 */
-			if (up_model && up_model_decmp)
-				TEST_ASSERT(!memcmp(up_model, up_model_decmp, data_size));
-		}
-
-		free(decmp_data);
-		free(up_model_decmp);
-	}
-
-	if (up_model == model)
-		free(model_cpy);
-
-	return cmp_size;
-}
-
-
 /**
  * @brief random compression decompression round trip test
  *
@@ -934,6 +940,7 @@ void test_random_collection_round_trip(void)
 	enum cmp_mode cmp_mode;
 	enum { MAX_DATA_TO_COMPRESS_SIZE = UINT16_MAX};
 	uint32_t cmp_data_capacity = COMPRESS_CHUNK_BOUND(MAX_DATA_TO_COMPRESS_SIZE, 1);
+	int run;
 #ifdef __sparc__
 	void *data          = (void *)0x63000000;
 	void *model         = (void *)0x64000000;
@@ -951,36 +958,85 @@ void test_random_collection_round_trip(void)
 	TEST_ASSERT_NOT_NULL(updated_model);
 	TEST_ASSERT_NOT_NULL(cmp_data);
 
-	for (data_type = 1; data_type <= DATA_TYPE_F_CAM_BACKGROUND; data_type++) {
-		/* printf("%s\n", data_type2string(data_type)); */
-		/* generate random data*/
-		size_t size;
-		uint32_t samples = cmp_rand_between(1, UINT16_MAX/size_of_a_sample(data_type));
+	for (run = 0; run < 2; run++) {
+		uint32_t (*gen_data_f)(uint32_t, void*);
+		void *extra;
+		double p = 0.01;
 
-		size = generate_random_collection(NULL, data_type, samples, &MAX_USED_BITS_SAFE);
-		TEST_ASSERT(size <= MAX_DATA_TO_COMPRESS_SIZE);
-		size = generate_random_collection(data, data_type, samples, &MAX_USED_BITS_SAFE);
-		TEST_ASSERT(size <= MAX_DATA_TO_COMPRESS_SIZE);
-		size = generate_random_collection(model, data_type, samples, &MAX_USED_BITS_SAFE);
-		TEST_ASSERT(size <= MAX_DATA_TO_COMPRESS_SIZE);
+		switch (run) {
+		case 0:
+			gen_data_f = gen_uniform_data;
+			extra = NULL;
+			break;
+		case 1:
+			gen_data_f = gen_geometric_data;
+			extra = &p;
+			break;
+		default:
+			TEST_FAIL();
+		}
 
-		for (cmp_mode = CMP_MODE_RAW; cmp_mode <= CMP_MODE_DIFF_MULTI; cmp_mode++) {
-			struct cmp_par par;
-			uint32_t cmp_size;
-
-			generate_random_cmp_par(&par);
-			par.cmp_mode = cmp_mode;
-			par.lossy_par = CMP_LOSSLESS;
-
-			cmp_size = chunk_round_trip(data, (uint32_t)size, model, updated_model,
-						   cmp_data, cmp_data_capacity,
-						   &par, 1, model_mode_is_used(par.cmp_mode));
-			/* No chunk is defined for fast cadence subservices */
-			if (data_type == DATA_TYPE_F_FX || data_type == DATA_TYPE_F_FX_EFX ||
-			    data_type == DATA_TYPE_F_FX_NCOB || data_type == DATA_TYPE_F_FX_EFX_NCOB_ECOB)
-				TEST_ASSERT_EQUAL_INT(CMP_ERROR_COL_SUBSERVICE_UNSUPPORTED, cmp_get_error_code(cmp_size));
-			else
-				TEST_ASSERT_FALSE(cmp_is_error(cmp_size));
+		for (data_type = 1; data_type <= DATA_TYPE_F_CAM_BACKGROUND; data_type++) {
+			/* printf("%s\n", data_type2string(data_type)); */
+			/* generate random data*/
+			size_t size;
+			uint32_t samples = cmp_rand_between(1, UINT16_MAX/size_of_a_sample(data_type) - COLLECTION_HDR_SIZE);
+
+			size = generate_random_collection(NULL, data_type, samples, gen_data_f, extra);
+			TEST_ASSERT(size <= MAX_DATA_TO_COMPRESS_SIZE);
+			size = generate_random_collection(data, data_type, samples, gen_data_f, extra);
+			TEST_ASSERT(size <= MAX_DATA_TO_COMPRESS_SIZE);
+			size = generate_random_collection(model, data_type, samples, gen_data_f, extra);
+			TEST_ASSERT(size <= MAX_DATA_TO_COMPRESS_SIZE);
+
+			for (cmp_mode = CMP_MODE_RAW; cmp_mode <= CMP_MODE_DIFF_MULTI; cmp_mode++) {
+				struct cmp_par par;
+				uint32_t cmp_size, cmp_size2;
+
+				cmp_data_capacity = COMPRESS_CHUNK_BOUND(MAX_DATA_TO_COMPRESS_SIZE, 1);
+
+				generate_random_cmp_par(&par);
+				par.cmp_mode = cmp_mode;
+				par.lossy_par = CMP_LOSSLESS;
+
+				cmp_size = chunk_round_trip(data, (uint32_t)size, model, updated_model,
+							   cmp_data, cmp_data_capacity,
+							   &par, 1, model_mode_is_used(par.cmp_mode));
+				/* No chunk is defined for fast cadence subservices */
+				if (data_type == DATA_TYPE_F_FX || data_type == DATA_TYPE_F_FX_EFX ||
+				    data_type == DATA_TYPE_F_FX_NCOB || data_type == DATA_TYPE_F_FX_EFX_NCOB_ECOB) {
+					TEST_ASSERT_EQUAL_INT(CMP_ERROR_COL_SUBSERVICE_UNSUPPORTED, cmp_get_error_code(cmp_size));
+					continue;
+				} else {
+					TEST_ASSERT_EQUAL_INT(CMP_ERROR_NO_ERROR, cmp_get_error_code(cmp_size));
+				}
+
+
+				/* test with minimum compressed data capacity */
+				cmp_data_capacity = ROUND_UP_TO_MULTIPLE_OF_4(cmp_size);
+				cmp_size2 = chunk_round_trip(data, (uint32_t)size, model, updated_model,
+							     cmp_data, cmp_data_capacity,
+							     &par, 1, model_mode_is_used(par.cmp_mode));
+
+				TEST_ASSERT_EQUAL_UINT(cmp_size, cmp_size2);
+				TEST_ASSERT_FALSE(cmp_is_error(cmp_size2));
+
+				/* error: the capacity for the compressed data is to small */
+				for (cmp_data_capacity = cmp_size2 - 1;
+				     cmp_data_capacity <= cmp_size - 32  && cmp_data_capacity > 1;
+				     cmp_data_capacity--) {
+					cmp_size = chunk_round_trip(data, (uint32_t)size, model, updated_model,
+								    cmp_data, cmp_data_capacity,
+								    &par, 1, model_mode_is_used(par.cmp_mode));
+					TEST_ASSERT_EQUAL_INT(CMP_ERROR_SMALL_BUF_, cmp_get_error_code(cmp_size));
+				}
+
+				cmp_data_capacity = cmp_size2 - cmp_rand_between(1, cmp_size2);
+				cmp_size = chunk_round_trip(data, (uint32_t)size, model, updated_model,
+							    cmp_data, cmp_data_capacity,
+							    &par, 1, model_mode_is_used(par.cmp_mode));
+				TEST_ASSERT_EQUAL_INT(CMP_ERROR_SMALL_BUF_, cmp_get_error_code(cmp_size));
+			}
 		}
 	}
 #ifndef __sparc__
@@ -1335,11 +1391,11 @@ void test_cmp_decmp_chunk_raw(void)
 	uint32_t dst_capacity = 0;
 
 	/* generate test data */
-	chunk_size = generate_random_chunk(chunk, chunk_def, ARRAY_SIZE(chunk_def), &MAX_USED_BITS_SAFE);
+	chunk_size = generate_random_chunk(chunk, chunk_def, ARRAY_SIZE(chunk_def), gen_uniform_data, NULL);
 	TEST_ASSERT_EQUAL_size_t(chunk_size_exp, chunk_size);
 	chunk = calloc(1, chunk_size);
 	TEST_ASSERT_NOT_NULL(chunk);
-	chunk_size = generate_random_chunk(chunk, chunk_def, ARRAY_SIZE(chunk_def), &MAX_USED_BITS_SAFE);
+	chunk_size = generate_random_chunk(chunk, chunk_def, ARRAY_SIZE(chunk_def), gen_uniform_data, NULL);
 	TEST_ASSERT_EQUAL_size_t(chunk_size_exp, chunk_size);
 
 	/* "compress" data */
@@ -1548,11 +1604,11 @@ void test_cmp_decmp_diff(void)
 		struct collection_hdr *col;
 		size_t chunk_size_exp = 2*sizeof(struct s_fx) + 3*sizeof(struct s_fx_efx_ncob_ecob) + 2*COLLECTION_HDR_SIZE;
 
-		chunk_size = generate_random_chunk(chunk, chunk_def, ARRAY_SIZE(chunk_def), &MAX_USED_BITS_SAFE);
+		chunk_size = generate_random_chunk(chunk, chunk_def, ARRAY_SIZE(chunk_def), gen_uniform_data, NULL);
 		TEST_ASSERT_EQUAL_size_t(chunk_size_exp, chunk_size);
 		chunk = calloc(1, chunk_size);
 		TEST_ASSERT_NOT_NULL(chunk);
-		chunk_size = generate_random_chunk(chunk, chunk_def, ARRAY_SIZE(chunk_def), &MAX_USED_BITS_SAFE);
+		chunk_size = generate_random_chunk(chunk, chunk_def, ARRAY_SIZE(chunk_def), gen_uniform_data, NULL);
 		TEST_ASSERT_EQUAL_size_t(chunk_size_exp, chunk_size);
 
 		col = (struct collection_hdr *)chunk;
diff --git a/test/fuzz/fuzz_compression.c b/test/fuzz/fuzz_compression.c
index 9962dee..daff743 100644
--- a/test/fuzz/fuzz_compression.c
+++ b/test/fuzz/fuzz_compression.c
@@ -26,6 +26,7 @@
 
 #include "fuzz_helpers.h"
 #include "fuzz_data_producer.h"
+#include "../test_common/test_common.h"
 
 #include "../../lib/cmp_chunk.h"
 
@@ -61,7 +62,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
 	if (cmp_is_error(cmp_size_bound))
 		cmp_size_bound = 0;
 	cmp_data_capacity = FUZZ_dataProducer_uint32Range(producer, 0, cmp_size_bound+(uint32_t)size);
-	cmp_data = (uint32_t *)FUZZ_malloc(cmp_data_capacity);
+	cmp_data = (uint32_t *)TEST_malloc(cmp_data_capacity);
 
 	FUZZ_dataProducer_cmp_par(producer, &cmp_par);
 	cmp_par.lossy_par = 0; /*TODO: implement lossy  */
@@ -74,10 +75,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
 		up_model = NULL;
 		break;
 	case 1:
-		up_model = FUZZ_malloc(size);
+		up_model = TEST_malloc(size);
 		break;
 	case 2:
-		up_model = FUZZ_malloc(size);
+		up_model = TEST_malloc(size);
 		if (model && up_model) {
 			memcpy(up_model, model, size);
 			model = up_model; /* in-place update */
diff --git a/test/fuzz/fuzz_data_producer.c b/test/fuzz/fuzz_data_producer.c
index 1ee5994..9f03a94 100644
--- a/test/fuzz/fuzz_data_producer.c
+++ b/test/fuzz/fuzz_data_producer.c
@@ -23,6 +23,7 @@
 
 #include "fuzz_helpers.h"
 #include "fuzz_data_producer.h"
+#include "../test_common/test_common.h"
 #include <cmp_chunk.h>
 
 struct FUZZ_dataProducer_s{
@@ -31,7 +32,7 @@ struct FUZZ_dataProducer_s{
 };
 
 FUZZ_dataProducer_t *FUZZ_dataProducer_create(const uint8_t *data, size_t size) {
-    FUZZ_dataProducer_t *producer = FUZZ_malloc(sizeof(FUZZ_dataProducer_t));
+    FUZZ_dataProducer_t *producer = TEST_malloc(sizeof(FUZZ_dataProducer_t));
 
     producer->data = data;
     producer->size = size;
diff --git a/test/fuzz/fuzz_helpers.c b/test/fuzz/fuzz_helpers.c
deleted file mode 100644
index 27f1076..0000000
--- a/test/fuzz/fuzz_helpers.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- * All rights reserved.
- *
- * This source code is licensed under both the BSD-style license (found in the
- * LICENSE.BSD-3.Zstandard file in the 3rdparty_licenses directory) and the GPLv2
- * (found in the LICENSE.GPL-2 file in the 3rdparty_licenses directory).
- * You may select, at your option, one of the above-listed licenses.
- */
-
-#include "fuzz_helpers.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
-void* FUZZ_malloc(size_t size)
-{
-    if (size > 0) {
-        void* const mem = malloc(size);
-        FUZZ_ASSERT(mem);
-        return mem;
-    }
-    return NULL;
-}
-
-void* FUZZ_malloc_rand(size_t size, FUZZ_dataProducer_t *producer)
-{
-    if (size > 0) {
-        void* const mem = malloc(size);
-        FUZZ_ASSERT(mem);
-        return mem;
-    } else {
-        uintptr_t ptr = 0;
-        /* Add +- 1M 50% of the time */
-        if (FUZZ_dataProducer_uint32Range(producer, 0, 1))
-            FUZZ_dataProducer_int32Range(producer, -1000000, 1000000);
-        return (void*)ptr;
-    }
-
-}
-
-int FUZZ_memcmp(void const* lhs, void const* rhs, int32_t size)
-{
-    if (size <= 0) {
-        return 0;
-    }
-    return memcmp(lhs, rhs, (size_t)size);
-}
diff --git a/test/fuzz/fuzz_helpers.h b/test/fuzz/fuzz_helpers.h
index 70fb3fb..0f95b74 100644
--- a/test/fuzz/fuzz_helpers.h
+++ b/test/fuzz/fuzz_helpers.h
@@ -15,18 +15,12 @@
 #ifndef FUZZ_HELPERS_H
 #define FUZZ_HELPERS_H
 
-#include "fuzz.h"
-#include "fuzz_data_producer.h"
-#include <stdint.h>
 #include <stdio.h>
-#include <stdlib.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
 
 #define FUZZ_QUOTE_IMPL(str) #str
 #define FUZZ_QUOTE(str) FUZZ_QUOTE_IMPL(str)
@@ -40,36 +34,6 @@ extern "C" {
                      __LINE__, FUZZ_QUOTE(cond), (msg)),                       \
              abort()))
 #define FUZZ_ASSERT(cond) FUZZ_ASSERT_MSG((cond), "");
-#define FUZZ_ZASSERT(code)                                                     \
-  FUZZ_ASSERT_MSG(!ZSTD_isError(code), ZSTD_getErrorName(code))
-
-#if defined(__GNUC__)
-#define FUZZ_STATIC static __inline __attribute__((unused))
-#elif defined(__cplusplus) ||                                                  \
-    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-#define FUZZ_STATIC static inline
-#elif defined(_MSC_VER)
-#define FUZZ_STATIC static __inline
-#else
-#define FUZZ_STATIC static
-#endif
-
-/**
- * malloc except return NULL for zero sized data and FUZZ_ASSERT
- * that malloc doesn't fail.
- */
-void* FUZZ_malloc(size_t size);
-
-/**
- * malloc except returns random pointer for zero sized data and FUZZ_ASSERT
- * that malloc doesn't fail.
- */
-void* FUZZ_malloc_rand(size_t size,  FUZZ_dataProducer_t *producer);
-
-/**
- * memcmp but accepts NULL. Ignore negative sizes
- */
-int FUZZ_memcmp(void const* lhs, void const* rhs, int32_t size);
 
 #ifdef __cplusplus
 }
diff --git a/test/fuzz/fuzz_round_trip.c b/test/fuzz/fuzz_round_trip.c
index 1aec745..cef6591 100644
--- a/test/fuzz/fuzz_round_trip.c
+++ b/test/fuzz/fuzz_round_trip.c
@@ -22,104 +22,12 @@
  */
 
 #include <string.h>
+#include <stdlib.h>
 
 #include "fuzz_helpers.h"
 #include "fuzz_data_producer.h"
-
-#include "../../lib/cmp_chunk.h"
-#include "../../lib/decmp.h"
-
-
-#define TEST_malloc(size) FUZZ_malloc(size)
-#define TEST_ASSERT(cond) FUZZ_ASSERT(cond)
-
-
-static uint32_t chunk_round_trip(void *data, uint32_t data_size,
-				 void *model, void *up_model,
-				 uint32_t *cmp_data, uint32_t cmp_data_capacity,
-				 struct cmp_par *cmp_par, int use_decmp_buf,
-				 int use_decmp_up_model)
-{
-	uint32_t cmp_size;
-	void *model_cpy = NULL;
-
-	/* if in-place model update is used (up_model == model), the model
-	 * needed for decompression is destroyed; therefore we make a copy
-	 */
-	if (model) {
-		if (up_model == model) {
-			model_cpy = TEST_malloc(data_size);
-			memcpy(model_cpy, model, data_size);
-		} else {
-			model_cpy = model;
-		}
-	}
-
-	cmp_size = compress_chunk(data, data_size, model, up_model,
-				  cmp_data, cmp_data_capacity, cmp_par);
-
-#if 0
-	{ /* Compress a second time and check for determinism */
-		int32_t cSize2;
-		void *compressed2 = NULL;
-		void *up_model2 = NULL;
-
-		if (compressed)
-			compressed2 = FUZZ_malloc(compressedCapacity);
-
-		if (up_model)
-			up_model2 = FUZZ_malloc(srcSize);
-		cSize2 = compress_chunk((void *)src, srcSize, (void *)model, up_model2,
-					   compressed2, compressedCapacity, cmp_par);
-		FUZZ_ASSERT(cSize == cSize2);
-		FUZZ_ASSERT_MSG(!FUZZ_memcmp(compressed, compressed2, cSize), "Not deterministic!");
-		FUZZ_ASSERT_MSG(!FUZZ_memcmp(up_model, compressed2, cSize), "NO deterministic!");
-		free(compressed2);
-		free(up_model2);
-	}
-#endif
-	if (!cmp_is_error(cmp_size) && cmp_data) {
-		void *decmp_data = NULL;
-		void *up_model_decmp = NULL;
-		int decmp_size;
-
-		decmp_size = decompress_cmp_entiy((struct cmp_entity *)cmp_data, model_cpy, NULL, NULL);
-		TEST_ASSERT(decmp_size >= 0);
-		TEST_ASSERT((uint32_t)decmp_size == data_size);
-
-		if (use_decmp_buf)
-			decmp_data = TEST_malloc(data_size);
-		if (use_decmp_up_model) {
-			up_model_decmp = TEST_malloc(data_size);
-			if (!model_mode_is_used(cmp_par->cmp_mode))
-				memset(up_model_decmp, 0, data_size); /* up_model is not used */
-		}
-
-		decmp_size = decompress_cmp_entiy((struct cmp_entity *)cmp_data, model_cpy,
-						  up_model_decmp, decmp_data);
-		TEST_ASSERT(decmp_size >= 0);
-		TEST_ASSERT((uint32_t)decmp_size == data_size);
-
-		if (use_decmp_buf) {
-			TEST_ASSERT(!memcmp(data, decmp_data, data_size));
-
-			/*
-			 * the model is only updated when the decompressed_data
-			 * buffer is set
-			 */
-			if (up_model && up_model_decmp)
-				TEST_ASSERT(!memcmp(up_model, up_model_decmp, data_size));
-		}
-
-		free(decmp_data);
-		free(up_model_decmp);
-	}
-
-	if (up_model == model)
-		free(model_cpy);
-
-	return cmp_size;
-}
+#include "../test_common/chunk_round_trip.h"
+#include "../test_common/test_common.h"
 
 
 int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
@@ -155,7 +63,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
 
 	/* 1/2 of the cases we use a updated model buffer */
 	if (FUZZ_dataProducer_uint32Range(producer, 0, 1)) {
-		up_model = FUZZ_malloc(size);
+		up_model = TEST_malloc(size);
 		if (!model_mode_is_used(cmp_par.cmp_mode))
 			memset(up_model, 0, size); /* up_model is not used */
 	}
@@ -164,7 +72,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
 	if (cmp_is_error(cmp_size_bound))
 		cmp_size_bound = 0;
 	cmp_data_capacity = FUZZ_dataProducer_uint32Range(producer, 0, cmp_size_bound+(uint32_t)size);
-	cmp_data = (uint32_t *)FUZZ_malloc(cmp_data_capacity);
+	cmp_data = (uint32_t *)TEST_malloc(cmp_data_capacity);
 
 	use_decmp_buf = FUZZ_dataProducer_int32Range(producer, 0, 1);
 	use_decmp_up_model = FUZZ_dataProducer_int32Range(producer, 0, 1);
diff --git a/test/fuzz/meson.build b/test/fuzz/meson.build
index f7da779..3815fcf 100644
--- a/test/fuzz/meson.build
+++ b/test/fuzz/meson.build
@@ -2,7 +2,7 @@ if get_option('fuzzer').disabled()
   subdir_done()
 endif
 
-fuzz_common = files('fuzz_helpers.c', 'fuzz_data_producer.c')
+fuzz_common = files('fuzz_data_producer.c')
 fuzz_targets = ['fuzz_compression.c', 'fuzz_round_trip.c']
 
 add_languages('cpp', native: false) # libFuzzingEngine needs c++
@@ -14,7 +14,7 @@ foreach target : fuzz_targets
   fuzz_exe = executable(target_name,
     fuzz_common, file_name,
     include_directories : incdir,
-    link_with : cmp_lib,
+    link_with : [cmp_lib, test_common_lib],
     link_args : get_option('fuzzer_ldflags'),
     link_language : 'cpp' # libFuzzingEngine needs c++
   )
diff --git a/test/meson.build b/test/meson.build
index 0fdcfc7..7878b91 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -53,7 +53,7 @@ if cc.has_argument('-fsanitize=leak') and not (host_machine.system() == 'darwin'
 endif
 
 test_env.set('UBSAN_OPTIONS',
-  'abort_on_error=1',
+  'halt_on_error=1',
   'print_stacktrace=1',
   'print_summary=1',
   'symbolize=1',
@@ -71,6 +71,7 @@ subdir('tools')
 subdir('cmp_tool')
 
 unity_dep = dependency('unity', fallback : ['unity', 'unity_dep'])
+m_dep = cc.find_library('m', required : false)
 subdir('test_common')
 subdir('fuzz')
 
@@ -104,7 +105,7 @@ if ruby.found()
       test_src, test_runner,
       include_directories : incdir,
       link_with : test_libs,
-      dependencies : unity_dep,
+      dependencies : [unity_dep, m_dep],
       c_args : test_args,
       build_by_default : false
     )
diff --git a/test/test_common/chunk_round_trip.c b/test/test_common/chunk_round_trip.c
new file mode 100644
index 0000000..4819ffd
--- /dev/null
+++ b/test/test_common/chunk_round_trip.c
@@ -0,0 +1,133 @@
+/**
+ * @file chunk_round_trip.c
+ * @date 2024
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief chunk compression/decompression test
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "../test_common/test_common.h"
+#include "../../lib/cmp_chunk.h"
+#include "../../lib/decmp.h"
+#include "chunk_round_trip.h"
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+#  include "../fuzz/fuzz_helpers.h"
+#  define TEST_ASSERT(cond) FUZZ_ASSERT(cond)
+#else
+#  include <unity.h>
+#endif
+
+
+/**
+ * @brief performs chunk compression and checks if a decompression is possible
+ *
+ * @param chunk			pointer to the chunk to be compressed
+ * @param chunk_size		byte size of the chunk
+ * @param chunk_model		pointer to a model of a chunk; has the same size
+ *				as the chunk (can be NULL if no model compression
+ *				mode is used)
+ * @param updated_chunk_model	pointer to store the updated model for the next
+ *				model mode compression; has the same size as the
+ *				chunk (can be the same as the model_of_data
+ *				buffer for in-place update or NULL if updated
+ *				model is not needed)
+ * @param dst			destination pointer to the compressed data
+ *				buffer; has to be 4-byte aligned; can be NULL to
+ *				only get the compressed data size
+ * @param dst_capacity		capacity of the dst buffer; it's recommended to
+ *				provide a dst_capacity >=
+ *				compress_chunk_cmp_size_bound(chunk, chunk_size)
+ *				as it eliminates one potential failure scenario:
+ *				not enough space in the dst buffer to write the
+ *				compressed data; size is internally rounded down
+ *				to a multiple of 4
+ * @param use_decmp_buf		if non-zero, a buffer is allocated for the
+ *				decompressed data
+ * @param use_decmp_up_model	if non-zero, a buffer for the updated model is
+ *				allocated during the decompression
+ *
+ * @returns the return value of the compress_chunk() function
+ */
+
+uint32_t chunk_round_trip(void *chunk, uint32_t chunk_size,
+			  void *chunk_model, void *updated_chunk_model,
+			  uint32_t *dst, uint32_t dst_capacity,
+			  struct cmp_par *cmp_par,
+			  int use_decmp_buf, int use_decmp_up_model)
+{
+	uint32_t cmp_size;
+	void *model_cpy = NULL;
+
+	/* if in-place model update is used (up_model == model), the model
+	 * needed for decompression is destroyed; therefore we make a copy
+	 */
+	if (chunk_model) {
+		if (updated_chunk_model == chunk_model) {
+			model_cpy = TEST_malloc(chunk_size);
+			memcpy(model_cpy, chunk_model, chunk_size);
+		} else {
+			model_cpy = chunk_model;
+		}
+	}
+
+	cmp_size = compress_chunk(chunk, chunk_size, chunk_model, updated_chunk_model,
+				  dst, dst_capacity, cmp_par);
+
+
+	/* decompress the data if the compression was successful */
+	if (!cmp_is_error(cmp_size) && dst) {
+		void *decmp_data = NULL;
+		void *up_model_decmp = NULL;
+		int decmp_size;
+
+		decmp_size = decompress_cmp_entiy((struct cmp_entity *)dst, model_cpy, NULL, NULL);
+		TEST_ASSERT(decmp_size >= 0);
+		TEST_ASSERT((uint32_t)decmp_size == chunk_size);
+
+		if (use_decmp_buf)
+			decmp_data = TEST_malloc(chunk_size);
+		if (use_decmp_up_model) {
+			up_model_decmp = TEST_malloc(chunk_size);
+			if (!model_mode_is_used(cmp_par->cmp_mode))
+				memset(up_model_decmp, 0, chunk_size); /* up_model is not used */
+		}
+
+		decmp_size = decompress_cmp_entiy((struct cmp_entity *)dst, model_cpy,
+						  up_model_decmp, decmp_data);
+		TEST_ASSERT(decmp_size >= 0);
+		TEST_ASSERT((uint32_t)decmp_size == chunk_size);
+
+		if (use_decmp_buf) {
+			TEST_ASSERT(!memcmp(chunk, decmp_data, chunk_size));
+
+			/*
+			 * the model is only updated when the decompressed_data
+			 * buffer is set
+			 */
+			if (use_decmp_up_model && updated_chunk_model)
+				TEST_ASSERT(!memcmp(updated_chunk_model, up_model_decmp, chunk_size));
+		}
+
+		free(decmp_data);
+		free(up_model_decmp);
+	}
+
+	if (updated_chunk_model == chunk_model)
+		free(model_cpy);
+
+	return cmp_size;
+}
diff --git a/test/test_common/chunk_round_trip.h b/test/test_common/chunk_round_trip.h
new file mode 100644
index 0000000..96e9df8
--- /dev/null
+++ b/test/test_common/chunk_round_trip.h
@@ -0,0 +1,34 @@
+/**
+ * @file chunk_round_trip.h
+ * @date 2024
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief chunk compression/decompression
+ */
+
+
+#ifndef CHUNK_ROUND_TRIP_H
+#define CHUNK_ROUND_TRIP_H
+
+#include <stdint.h>
+#include "../../lib/cmp_chunk.h"
+
+
+uint32_t chunk_round_trip(void *data, uint32_t data_size,
+			  void *model, void *up_model,
+			  uint32_t *cmp_data, uint32_t cmp_data_capacity,
+			  struct cmp_par *cmp_par, int use_decmp_buf, int
+			  use_decmp_up_model);
+
+
+#endif /* CHUNK_ROUND_TRIP_H */
+
diff --git a/test/test_common/meson.build b/test/test_common/meson.build
index 082ac4c..ab030dc 100644
--- a/test/test_common/meson.build
+++ b/test/test_common/meson.build
@@ -4,5 +4,6 @@ pcb_dep = pcg_proj.get_variable('libpcg_basic_dep')
 test_common_lib = static_library(
    'test_common',
    'test_common.c',
+   'chunk_round_trip.c',
    dependencies: [pcb_dep, unity_dep]
 )
diff --git a/test/test_common/test_common.c b/test/test_common/test_common.c
index 7a1b25f..0cdd826 100644
--- a/test/test_common/test_common.c
+++ b/test/test_common/test_common.c
@@ -6,7 +6,12 @@
 #include "pcg_basic.h"
 #include "test_common.h"
 
-#include <unity.h>
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+#  include "../fuzz/fuzz_helpers.h"
+#  define TEST_ASSERT(cond) FUZZ_ASSERT(cond)
+#else
+#  include <unity.h>
+#endif
 
 void cmp_rand_seed(uint64_t seed)
 {
@@ -38,11 +43,12 @@ uint32_t cmp_rand_between(uint32_t min, uint32_t max)
 }
 
 
-uint32_t cmp_rand_nbits(unsigned int nbits)
+uint32_t cmp_rand_nbits(unsigned int n_bits)
 {
-	TEST_ASSERT(nbits > 0);
+	TEST_ASSERT(n_bits > 0);
+	TEST_ASSERT(n_bits <= 32);
 
-	return cmp_rand32() >> (32 - nbits);
+	return cmp_rand32() >> (32 - n_bits);
 }
 
 
diff --git a/test/test_common/test_common.h b/test/test_common/test_common.h
index e27d68a..a5e72f9 100644
--- a/test/test_common/test_common.h
+++ b/test/test_common/test_common.h
@@ -10,7 +10,7 @@ uint32_t cmp_rand32(void);
 
 uint32_t cmp_rand_between(uint32_t min, uint32_t max);
 
-uint32_t cmp_rand_nbits(unsigned int nbits);
+uint32_t cmp_rand_nbits(unsigned int n_bits);
 
 void* TEST_malloc(size_t size);
 
-- 
GitLab