Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
H
Heatshrink
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Grant Kim
Heatshrink
Commits
555f7cf0
Commit
555f7cf0
authored
10 years ago
by
Scott Vokes
Browse files
Options
Downloads
Patches
Plain Diff
Add optional property-based tests, using theft (if available).
parent
c565a44c
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
Makefile
+11
-3
11 additions, 3 deletions
Makefile
test_heatshrink_dynamic.c
+7
-0
7 additions, 0 deletions
test_heatshrink_dynamic.c
test_heatshrink_dynamic_theft.c
+521
-0
521 additions, 0 deletions
test_heatshrink_dynamic_theft.c
with
539 additions
and
3 deletions
Makefile
+
11
−
3
View file @
555f7cf0
...
...
@@ -8,6 +8,11 @@ CFLAGS += -Wmissing-prototypes
CFLAGS
+=
-Wstrict-prototypes
CFLAGS
+=
-Wmissing-declarations
# If libtheft is available, build additional property-based tests.
# Uncomment these to use it in test_heatshrink_dynamic.
#CFLAGS += -DHEATSHRINK_HAS_THEFT
#LDFLAGS += -ltheft
all
:
@
echo
"For tests, make test_heatshrink_dynamic (default) or change the"
@
echo
"config.h to disable static memory and build test_heatshrink_static."
...
...
@@ -15,9 +20,12 @@ all:
${PROJECT}
:
heatshrink.c
heatshrink
:
heatshrink_encoder.o heatshrink_decoder.o
test_heatshrink_dynamic
:
heatshrink_encoder.o heatshrink_decoder.o
test_heatshrink_static
:
heatshrink_encoder.o heatshrink_decoder.o
OBJS
=
heatshrink_encoder.o
\
heatshrink_decoder.o
\
heatshrink
:
${OBJS}
test_heatshrink_dynamic
:
${OBJS} test_heatshrink_dynamic_theft.o
test_heatshrink_static
:
${OBJS}
*.o
:
Makefile heatshrink_config.h
...
...
This diff is collapsed.
Click to expand it.
test_heatshrink_dynamic.c
+
7
−
0
View file @
555f7cf0
...
...
@@ -14,6 +14,10 @@ SUITE(encoding);
SUITE
(
decoding
);
SUITE
(
integration
);
#ifdef HEATSHRINK_HAS_THEFT
SUITE
(
properties
);
#endif
static
void
dump_buf
(
char
*
name
,
uint8_t
*
buf
,
uint16_t
count
)
{
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
uint8_t
c
=
(
uint8_t
)
buf
[
i
];
...
...
@@ -988,5 +992,8 @@ int main(int argc, char **argv) {
RUN_SUITE
(
encoding
);
RUN_SUITE
(
decoding
);
RUN_SUITE
(
integration
);
#ifdef HEATSHRINK_HAS_THEFT
RUN_SUITE
(
properties
);
#endif
GREATEST_MAIN_END
();
/* display results */
}
This diff is collapsed.
Click to expand it.
test_heatshrink_dynamic_theft.c
0 → 100644
+
521
−
0
View file @
555f7cf0
#include
"heatshrink_config.h"
#ifdef HEATSHRINK_HAS_THEFT
#include
<stdint.h>
#include
<ctype.h>
#include
<assert.h>
#include
<string.h>
#include
<sys/time.h>
#include
"heatshrink_encoder.h"
#include
"heatshrink_decoder.h"
#include
"greatest.h"
#include
"theft.h"
#include
"greatest_theft.h"
#if !HEATSHRINK_DYNAMIC_ALLOC
#error Must set HEATSHRINK_DYNAMIC_ALLOC to 1 for this test suite.
#endif
SUITE
(
properties
);
typedef
struct
{
int
limit
;
int
fails
;
int
dots
;
}
test_env
;
typedef
struct
{
size_t
size
;
uint8_t
buf
[];
}
rbuf
;
static
void
*
rbuf_alloc_cb
(
struct
theft
*
t
,
theft_hash
seed
,
void
*
env
)
{
test_env
*
te
=
(
test_env
*
)
env
;
//printf("seed is 0x%016llx\n", seed);
size_t
sz
=
(
size_t
)(
seed
%
te
->
limit
)
+
1
;
rbuf
*
r
=
malloc
(
sizeof
(
rbuf
)
+
sz
);
if
(
r
==
NULL
)
{
return
THEFT_ERROR
;
}
r
->
size
=
sz
;
for
(
size_t
i
=
0
;
i
<
sz
;
i
+=
sizeof
(
theft_hash
))
{
theft_hash
s
=
theft_random
(
t
);
for
(
uint8_t
b
=
0
;
b
<
sizeof
(
theft_hash
);
b
++
)
{
if
(
i
+
b
>=
sz
)
{
break
;
}
r
->
buf
[
i
+
b
]
=
(
uint8_t
)
(
s
>>
(
8
*
b
))
&
0xff
;
}
}
return
r
;
}
static
void
rbuf_free_cb
(
void
*
instance
,
void
*
env
)
{
free
(
instance
);
(
void
)
env
;
}
static
uint64_t
rbuf_hash_cb
(
void
*
instance
,
void
*
env
)
{
rbuf
*
r
=
(
rbuf
*
)
instance
;
(
void
)
env
;
return
theft_hash_onepass
(
r
->
buf
,
r
->
size
);
}
/* Make a copy of a buffer, keeping NEW_SZ bytes starting at OFFSET. */
static
void
*
copy_rbuf_subset
(
rbuf
*
cur
,
size_t
new_sz
,
size_t
byte_offset
)
{
if
(
new_sz
==
0
)
{
return
THEFT_DEAD_END
;
}
rbuf
*
nr
=
malloc
(
sizeof
(
rbuf
)
+
new_sz
);
if
(
nr
==
NULL
)
{
return
THEFT_ERROR
;
}
nr
->
size
=
new_sz
;
memcpy
(
nr
->
buf
,
&
cur
->
buf
[
byte_offset
],
new_sz
);
/* printf("%zu -> %zu\n", cur->size, new_sz); */
return
nr
;
}
/* Make a copy of a buffer, but only PORTION, starting OFFSET in
* (e.g. the third quarter is (0.25 at +0.75). Rounds to ints. */
static
void
*
copy_rbuf_percent
(
rbuf
*
cur
,
float
portion
,
float
offset
)
{
size_t
new_sz
=
cur
->
size
*
portion
;
size_t
byte_offset
=
(
size_t
)(
cur
->
size
*
offset
);
return
copy_rbuf_subset
(
cur
,
new_sz
,
byte_offset
);
}
/* How to shrink a random buffer to a simpler one. */
static
void
*
rbuf_shrink_cb
(
void
*
instance
,
uint32_t
tactic
,
void
*
env
)
{
rbuf
*
cur
=
(
rbuf
*
)
instance
;
if
(
tactic
==
0
)
{
/* first half */
return
copy_rbuf_percent
(
cur
,
0
.
5
,
0
);
}
else
if
(
tactic
==
1
)
{
/* second half */
return
copy_rbuf_percent
(
cur
,
0
.
5
,
0
.
5
);
}
else
if
(
tactic
<=
18
)
{
/* drop 1-16 bytes at start */
const
int
last_tactic
=
1
;
const
size_t
drop
=
tactic
-
last_tactic
;
if
(
cur
->
size
<
drop
)
{
return
THEFT_DEAD_END
;
}
return
copy_rbuf_subset
(
cur
,
cur
->
size
-
drop
,
drop
);
}
else
if
(
tactic
<=
34
)
{
/* drop 1-16 bytes at end */
const
int
last_tactic
=
18
;
const
size_t
drop
=
tactic
-
last_tactic
;
if
(
cur
->
size
<
drop
)
{
return
THEFT_DEAD_END
;
}
return
copy_rbuf_subset
(
cur
,
cur
->
size
-
drop
,
0
);
}
else
if
(
tactic
==
35
)
{
/* Divide every byte by 2, saturating at 0 */
rbuf
*
cp
=
copy_rbuf_percent
(
cur
,
1
,
0
);
if
(
cp
==
NULL
)
{
return
THEFT_ERROR
;
}
for
(
size_t
i
=
0
;
i
<
cp
->
size
;
i
++
)
{
cp
->
buf
[
i
]
/=
2
;
}
return
cp
;
}
else
if
(
tactic
==
36
)
{
/* subtract 1 from every byte, saturating at 0 */
rbuf
*
cp
=
copy_rbuf_percent
(
cur
,
1
,
0
);
if
(
cp
==
NULL
)
{
return
THEFT_ERROR
;
}
for
(
size_t
i
=
0
;
i
<
cp
->
size
;
i
++
)
{
if
(
cp
->
buf
[
i
]
>
0
)
{
cp
->
buf
[
i
]
--
;
}
}
return
cp
;
}
else
{
(
void
)
env
;
return
THEFT_NO_MORE_TACTICS
;
}
return
THEFT_NO_MORE_TACTICS
;
}
static
void
rbuf_print_cb
(
FILE
*
f
,
void
*
instance
,
void
*
env
)
{
rbuf
*
r
=
(
rbuf
*
)
instance
;
(
void
)
env
;
fprintf
(
f
,
"buf[%zd]:
\n
"
,
r
->
size
);
uint8_t
bytes
=
0
;
for
(
size_t
i
=
0
;
i
<
r
->
size
;
i
++
)
{
fprintf
(
f
,
"%02x"
,
r
->
buf
[
i
]);
bytes
++
;
if
(
bytes
==
16
)
{
fprintf
(
f
,
"
\n
"
);
bytes
=
0
;
}
}
fprintf
(
f
,
"
\n
"
);
}
static
struct
theft_type_info
rbuf_info
=
{
.
alloc
=
rbuf_alloc_cb
,
.
free
=
rbuf_free_cb
,
.
hash
=
rbuf_hash_cb
,
.
shrink
=
rbuf_shrink_cb
,
.
print
=
rbuf_print_cb
,
};
static
theft_progress_callback_res
progress_cb
(
struct
theft_trial_info
*
info
,
void
*
env
)
{
test_env
*
te
=
(
test_env
*
)
env
;
if
((
info
->
trial
&
0xff
)
==
0
)
{
printf
(
"."
);
fflush
(
stdout
);
te
->
dots
++
;
if
(
te
->
dots
==
64
)
{
printf
(
"
\n
"
);
te
->
dots
=
0
;
}
}
if
(
info
->
status
==
THEFT_TRIAL_FAIL
)
{
te
->
fails
++
;
rbuf
*
cur
=
info
->
args
[
0
];
if
(
cur
->
size
<
5
)
{
return
THEFT_PROGRESS_HALT
;
}
}
if
(
te
->
fails
>
10
)
{
return
THEFT_PROGRESS_HALT
;
}
return
THEFT_PROGRESS_CONTINUE
;
}
/* For an arbitrary input buffer, it should never get stuck in a
* state where the data has been sunk but no data can be polled. */
static
theft_trial_res
prop_should_not_get_stuck
(
void
*
input
)
{
/* Make a buffer large enough for the output: 4 KB of input with
* each 16 bits becoming up to 16 bytes will fit in a 64 KB buffer.
* (4 KB of input comes from `env.limit = 1 << 12;` below.) */
uint8_t
output
[
64
*
1024
];
heatshrink_decoder
*
hsd
=
heatshrink_decoder_alloc
((
64
*
1024L
)
-
1
,
12
,
4
);
if
(
hsd
==
NULL
)
{
return
THEFT_TRIAL_ERROR
;
}
rbuf
*
r
=
(
rbuf
*
)
input
;
size_t
count
=
0
;
HSD_sink_res
sres
=
heatshrink_decoder_sink
(
hsd
,
r
->
buf
,
r
->
size
,
&
count
);
if
(
sres
!=
HSDR_SINK_OK
)
{
return
THEFT_TRIAL_ERROR
;
}
size_t
out_sz
=
0
;
HSD_poll_res
pres
=
heatshrink_decoder_poll
(
hsd
,
output
,
sizeof
(
output
),
&
out_sz
);
if
(
pres
!=
HSDR_POLL_EMPTY
)
{
return
THEFT_TRIAL_FAIL
;
}
HSD_finish_res
fres
=
heatshrink_decoder_finish
(
hsd
);
heatshrink_decoder_free
(
hsd
);
if
(
fres
!=
HSDR_FINISH_DONE
)
{
return
THEFT_TRIAL_FAIL
;
}
return
THEFT_TRIAL_PASS
;
}
static
bool
get_time_seed
(
theft_seed
*
seed
)
{
struct
timeval
tv
;
if
(
-
1
==
gettimeofday
(
&
tv
,
NULL
))
{
return
false
;
}
*
seed
=
(
theft_seed
)((
tv
.
tv_sec
<<
32
)
|
tv
.
tv_usec
);
/* printf("seed is 0x%016llx\n", *seed); */
return
true
;
}
TEST
decoder_fuzzing_should_not_detect_stuck_state
(
void
)
{
// Get a random number seed based on the time
theft_seed
seed
;
if
(
!
get_time_seed
(
&
seed
))
{
FAIL
();
}
/* Pass the max buffer size for this property (4 KB) in a closure */
test_env
env
=
{
.
limit
=
1
<<
12
};
theft_seed
always_seeds
=
{
0xe87bb1f61032a061
};
struct
theft
*
t
=
theft_init
(
0
);
struct
theft_cfg
cfg
=
{
.
name
=
__func__
,
.
fun
=
prop_should_not_get_stuck
,
.
type_info
=
{
&
rbuf_info
},
.
seed
=
seed
,
.
trials
=
100000
,
.
progress_cb
=
progress_cb
,
.
env
=
&
env
,
.
always_seeds
=
&
always_seeds
,
.
always_seed_count
=
1
,
};
theft_run_res
res
=
theft_run
(
t
,
&
cfg
);
theft_free
(
t
);
printf
(
"
\n
"
);
GREATEST_ASSERT_EQm
(
"should_not_get_stuck"
,
THEFT_RUN_PASS
,
res
);
PASS
();
}
static
theft_trial_res
prop_encoded_and_decoded_data_should_match
(
void
*
input
)
{
uint8_t
e_output
[
64
*
1024
];
uint8_t
d_output
[
64
*
1024
];
heatshrink_encoder
*
hse
=
heatshrink_encoder_alloc
(
12
,
4
);
if
(
hse
==
NULL
)
{
return
THEFT_TRIAL_ERROR
;
}
heatshrink_decoder
*
hsd
=
heatshrink_decoder_alloc
(
4096
,
12
,
4
);
if
(
hsd
==
NULL
)
{
return
THEFT_TRIAL_ERROR
;
}
rbuf
*
r
=
(
rbuf
*
)
input
;
size_t
e_input_size
=
0
;
HSE_sink_res
esres
=
heatshrink_encoder_sink
(
hse
,
r
->
buf
,
r
->
size
,
&
e_input_size
);
if
(
esres
!=
HSER_SINK_OK
)
{
return
THEFT_TRIAL_ERROR
;
}
if
(
e_input_size
!=
r
->
size
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
HSE_finish_res
efres
=
heatshrink_encoder_finish
(
hse
);
if
(
efres
!=
HSER_FINISH_MORE
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
size_t
e_output_size
=
0
;
HSE_poll_res
epres
=
heatshrink_encoder_poll
(
hse
,
e_output
,
sizeof
(
e_output
),
&
e_output_size
);
if
(
epres
!=
HSER_POLL_EMPTY
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
size_t
count
=
0
;
HSD_sink_res
sres
=
heatshrink_decoder_sink
(
hsd
,
e_output
,
e_output_size
,
&
count
);
if
(
sres
!=
HSDR_SINK_OK
)
{
return
THEFT_TRIAL_ERROR
;
}
size_t
d_output_size
=
0
;
HSD_poll_res
pres
=
heatshrink_decoder_poll
(
hsd
,
d_output
,
sizeof
(
d_output
),
&
d_output_size
);
if
(
pres
!=
HSDR_POLL_EMPTY
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
if
(
d_output_size
!=
r
->
size
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
if
(
0
!=
memcmp
(
d_output
,
r
->
buf
,
d_output_size
))
{
return
THEFT_TRIAL_FAIL
;
}
HSD_finish_res
fres
=
heatshrink_decoder_finish
(
hsd
);
if
(
fres
!=
HSDR_FINISH_DONE
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
heatshrink_encoder_free
(
hse
);
heatshrink_decoder_free
(
hsd
);
return
THEFT_TRIAL_PASS
;
}
TEST
encoded_and_decoded_data_should_match
(
void
)
{
test_env
env
=
{
.
limit
=
1
<<
11
};
theft_seed
seed
;
if
(
!
get_time_seed
(
&
seed
))
{
FAIL
();
}
struct
theft
*
t
=
theft_init
(
0
);
struct
theft_cfg
cfg
=
{
.
name
=
__func__
,
.
fun
=
prop_encoded_and_decoded_data_should_match
,
.
type_info
=
{
&
rbuf_info
},
.
seed
=
seed
,
.
trials
=
1000000
,
.
env
=
&
env
,
.
progress_cb
=
progress_cb
,
};
theft_run_res
res
=
theft_run
(
t
,
&
cfg
);
theft_free
(
t
);
printf
(
"
\n
"
);
ASSERT_EQ
(
THEFT_RUN_PASS
,
res
);
PASS
();
}
static
size_t
ceil_nine_eighths
(
size_t
sz
)
{
return
sz
+
sz
/
8
+
(
sz
&
0x07
?
1
:
0
);
}
static
theft_trial_res
prop_encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst
(
void
*
input
)
{
uint8_t
output
[
32
*
1024
];
heatshrink_encoder
*
hse
=
heatshrink_encoder_alloc
(
12
,
4
);
if
(
hse
==
NULL
)
{
return
THEFT_TRIAL_ERROR
;
}
rbuf
*
r
=
(
rbuf
*
)
input
;
size_t
input_size
=
0
;
HSE_sink_res
esres
=
heatshrink_encoder_sink
(
hse
,
r
->
buf
,
r
->
size
,
&
input_size
);
if
(
esres
!=
HSER_SINK_OK
)
{
return
THEFT_TRIAL_ERROR
;
}
/* Assumes data fits in one sink, failure here means buffer must be larger. */
if
(
input_size
!=
r
->
size
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
HSE_finish_res
efres
=
heatshrink_encoder_finish
(
hse
);
if
(
efres
!=
HSER_FINISH_MORE
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
size_t
output_size
=
0
;
HSE_poll_res
epres
=
heatshrink_encoder_poll
(
hse
,
output
,
sizeof
(
output
),
&
output_size
);
if
(
epres
!=
HSER_POLL_EMPTY
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
size_t
ceil_9_8s
=
ceil_nine_eighths
(
r
->
size
);
if
(
output_size
>
ceil_9_8s
)
{
return
THEFT_TRIAL_FAIL
;
}
heatshrink_encoder_free
(
hse
);
return
THEFT_TRIAL_PASS
;
}
TEST
encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst
(
void
)
{
test_env
env
=
{
.
limit
=
1
<<
11
};
theft_seed
seed
;
if
(
!
get_time_seed
(
&
seed
))
{
FAIL
();
}
struct
theft
*
t
=
theft_init
(
0
);
struct
theft_cfg
cfg
=
{
.
name
=
__func__
,
.
fun
=
prop_encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst
,
.
type_info
=
{
&
rbuf_info
},
.
seed
=
seed
,
.
trials
=
10000
,
.
env
=
&
env
,
.
progress_cb
=
progress_cb
,
};
theft_run_res
res
=
theft_run
(
t
,
&
cfg
);
theft_free
(
t
);
printf
(
"
\n
"
);
ASSERT_EQ
(
THEFT_RUN_PASS
,
res
);
PASS
();
}
static
theft_trial_res
prop_encoder_should_always_make_progress
(
void
*
instance
)
{
uint8_t
output
[
64
*
1024
];
heatshrink_encoder
*
hse
=
heatshrink_encoder_alloc
(
8
,
4
);
if
(
hse
==
NULL
)
{
return
THEFT_TRIAL_ERROR
;
}
rbuf
*
r
=
(
rbuf
*
)
instance
;
size_t
sunk
=
0
;
int
no_progress
=
0
;
while
(
1
)
{
if
(
sunk
<
r
->
size
)
{
size_t
input_size
=
0
;
HSE_sink_res
esres
=
heatshrink_encoder_sink
(
hse
,
&
r
->
buf
[
sunk
],
r
->
size
-
sunk
,
&
input_size
);
if
(
esres
!=
HSER_SINK_OK
)
{
return
THEFT_TRIAL_ERROR
;
}
sunk
+=
input_size
;
}
else
{
HSE_finish_res
efres
=
heatshrink_encoder_finish
(
hse
);
if
(
efres
==
HSER_FINISH_DONE
)
{
break
;
}
else
if
(
efres
!=
HSER_FINISH_MORE
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
}
size_t
output_size
=
0
;
HSE_poll_res
epres
=
heatshrink_encoder_poll
(
hse
,
output
,
sizeof
(
output
),
&
output_size
);
if
(
epres
<
0
)
{
return
THEFT_TRIAL_ERROR
;
}
if
(
output_size
==
0
&&
sunk
==
r
->
size
)
{
no_progress
++
;
if
(
no_progress
>
2
)
{
return
THEFT_TRIAL_FAIL
;
}
}
else
{
no_progress
=
0
;
}
}
heatshrink_encoder_free
(
hse
);
return
THEFT_TRIAL_PASS
;
}
TEST
encoder_should_always_make_progress
(
void
)
{
test_env
env
=
{
.
limit
=
1
<<
15
};
theft_seed
seed
;
if
(
!
get_time_seed
(
&
seed
))
{
FAIL
();
}
struct
theft
*
t
=
theft_init
(
0
);
struct
theft_cfg
cfg
=
{
.
name
=
__func__
,
.
fun
=
prop_encoder_should_always_make_progress
,
.
type_info
=
{
&
rbuf_info
},
.
seed
=
seed
,
.
trials
=
10000
,
.
env
=
&
env
,
.
progress_cb
=
progress_cb
,
};
theft_run_res
res
=
theft_run
(
t
,
&
cfg
);
theft_free
(
t
);
printf
(
"
\n
"
);
ASSERT_EQ
(
THEFT_RUN_PASS
,
res
);
PASS
();
}
static
theft_trial_res
prop_decoder_should_always_make_progress
(
void
*
instance
)
{
uint8_t
output
[
64
*
1024
];
heatshrink_decoder
*
hsd
=
heatshrink_decoder_alloc
(
512
,
8
,
4
);
if
(
hsd
==
NULL
)
{
return
THEFT_TRIAL_ERROR
;
}
rbuf
*
r
=
(
rbuf
*
)
instance
;
size_t
sunk
=
0
;
int
no_progress
=
0
;
while
(
1
)
{
if
(
sunk
<
r
->
size
)
{
size_t
input_size
=
0
;
HSD_sink_res
sres
=
heatshrink_decoder_sink
(
hsd
,
&
r
->
buf
[
sunk
],
r
->
size
-
sunk
,
&
input_size
);
if
(
sres
!=
HSER_SINK_OK
)
{
return
THEFT_TRIAL_ERROR
;
}
sunk
+=
input_size
;
}
else
{
HSD_finish_res
fres
=
heatshrink_decoder_finish
(
hsd
);
if
(
fres
==
HSDR_FINISH_DONE
)
{
break
;
}
else
if
(
fres
!=
HSDR_FINISH_MORE
)
{
printf
(
"FAIL %d
\n
"
,
__LINE__
);
return
THEFT_TRIAL_FAIL
;
}
}
size_t
output_size
=
0
;
HSD_poll_res
pres
=
heatshrink_decoder_poll
(
hsd
,
output
,
sizeof
(
output
),
&
output_size
);
if
(
pres
<
0
)
{
return
THEFT_TRIAL_ERROR
;
}
if
(
output_size
==
0
&&
sunk
==
r
->
size
)
{
no_progress
++
;
if
(
no_progress
>
2
)
{
return
THEFT_TRIAL_FAIL
;
}
}
else
{
no_progress
=
0
;
}
}
heatshrink_decoder_free
(
hsd
);
return
THEFT_TRIAL_PASS
;
}
TEST
decoder_should_always_make_progress
(
void
)
{
test_env
env
=
{
.
limit
=
1
<<
15
};
theft_seed
seed
;
if
(
!
get_time_seed
(
&
seed
))
{
FAIL
();
}
struct
theft
*
t
=
theft_init
(
0
);
struct
theft_cfg
cfg
=
{
.
name
=
__func__
,
.
fun
=
prop_decoder_should_always_make_progress
,
.
type_info
=
{
&
rbuf_info
},
.
seed
=
seed
,
.
trials
=
10000
,
.
env
=
&
env
,
.
progress_cb
=
progress_cb
,
};
theft_run_res
res
=
theft_run
(
t
,
&
cfg
);
theft_free
(
t
);
printf
(
"
\n
"
);
ASSERT_EQ
(
THEFT_RUN_PASS
,
res
);
PASS
();
}
SUITE
(
properties
)
{
RUN_TEST
(
decoder_fuzzing_should_not_detect_stuck_state
);
RUN_TEST
(
encoded_and_decoded_data_should_match
);
RUN_TEST
(
encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst
);
RUN_TEST
(
encoder_should_always_make_progress
);
RUN_TEST
(
decoder_should_always_make_progress
);
}
#else
struct
because_iso_c_requires_at_least_one_declaration
;
#endif
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment