Skip to content

Commit 95e3df3

Browse files
authored
Merge pull request #14 from jfrimmel/code-coverage
Add more tests to improve code coverage
2 parents cad2a6e + 552bba9 commit 95e3df3

File tree

3 files changed

+117
-6
lines changed

3 files changed

+117
-6
lines changed

src/lib.rs

+21-4
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,14 @@
6464
//! # Testing
6565
//! As mentioned before: an allocator is a very critical part in the overall
6666
//! system; a misbehaving allocator can break the whole program. Therefore this
67-
//! create is tested extensively:
67+
//! create is tested extensively[^note]:
6868
//! - there are unit tests and integration tests
6969
//! - the unit tests are run under `miri` to detect undefined behavior, both on
7070
//! a little-endian and big-endian system
7171
//! - a real-world test using the allocator in the popular `ripgrep` program was
7272
//! done (see [here][gist_hosted-test])
7373
//!
74-
//! Note, that the test coverage is not yet high enough and work is done to
75-
//! increase it.
74+
//! [^note]: The test coverage of different metrics and tools is over 96%.
7675
//!
7776
//! # Implementation
7877
//! This algorithm does a linear scan for free blocks. The basic algorithm is as
@@ -350,7 +349,7 @@ mod tests {
350349

351350
#[test]
352351
fn small_alignments() {
353-
let allocator = Allocator::<32>::new();
352+
let allocator = Allocator::<128>::new();
354353

355354
let ptr = unsafe { allocator.alloc(Layout::from_size_align(8, 2).unwrap()) };
356355
assert_alignment!(ptr, 1);
@@ -382,6 +381,24 @@ mod tests {
382381
assert_alignment!(ptr, FOUR_MEG);
383382
}
384383

384+
#[test]
385+
fn allocation_failure() {
386+
let allocator = Allocator::<128>::new();
387+
388+
// try an allocation, that exceeds the total memory size
389+
let ptr = unsafe { allocator.alloc(Layout::from_size_align(129, 1).unwrap()) };
390+
assert_eq!(ptr, ptr::null_mut());
391+
}
392+
393+
#[test]
394+
fn allocation_failure_due_to_alignment() {
395+
let allocator = Allocator::<128>::new();
396+
397+
// try an allocation, that exceeds the total memory size
398+
let ptr = unsafe { allocator.alloc(Layout::from_size_align(8, 128).unwrap()) };
399+
assert_eq!(ptr, ptr::null_mut());
400+
}
401+
385402
#[test]
386403
fn example_usage() {
387404
// do some example allocations. There is an intermediate deallocation,

src/raw_allocator/buffer.rs

+72-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ impl<const N: usize> Buffer<N> {
3030
/// This function panics if the buffer is less than 4 bytes in size, i.e. if
3131
/// `N < 4`.
3232
pub const fn new() -> Self {
33-
assert!(N >= 4, "buffer too small, use N >= 4");
33+
assert!(N >= HEADER_SIZE, "buffer too small, use N >= 4");
34+
assert!(N % HEADER_SIZE == 0, "memory size has to be divisible by 4");
3435
let remaining_size = N - HEADER_SIZE;
3536
let initial_entry = Entry::free(remaining_size).as_raw();
3637

@@ -240,7 +241,24 @@ impl<'buffer, const N: usize> Iterator for EntryIter<'buffer, N> {
240241

241242
#[cfg(test)]
242243
mod tests {
243-
use super::{Buffer, Entry, ValidatedOffset};
244+
use super::{Buffer, Entry, ValidatedOffset, HEADER_SIZE};
245+
246+
#[test]
247+
fn validated_offset_debug() {
248+
assert_eq!(format!("{:?}", ValidatedOffset(12)), "ValidatedOffset(12)");
249+
}
250+
251+
#[test]
252+
fn validated_offset_equality() {
253+
assert_eq!(ValidatedOffset(12), ValidatedOffset(12));
254+
assert_ne!(ValidatedOffset(12), ValidatedOffset(24));
255+
// as this test is primarily to make the code coverage happy, let's test
256+
// something else here: cloning. Since the type is `Copy`, it has to be
257+
// `Clone` as well, despite `clone()` never being called. So let's do it
258+
// here, so that the coverage testing is happy.
259+
assert_eq!(ValidatedOffset(12).clone(), ValidatedOffset(12));
260+
assert_ne!(ValidatedOffset(12).clone(), ValidatedOffset(24));
261+
}
244262

245263
#[test]
246264
fn empty_allocator() {
@@ -250,6 +268,30 @@ mod tests {
250268
assert_eq!(expected, actual);
251269
}
252270

271+
#[test]
272+
fn header_size() {
273+
// the codebase assumes, that the header size is `4`, so make sure that
274+
// assumption holds.
275+
assert_eq!(HEADER_SIZE, 4);
276+
}
277+
278+
#[test]
279+
#[should_panic(expected = "buffer too small")]
280+
fn too_small_buffer() {
281+
// this test ensures, that there is no out of bounds writing when
282+
// setting up the initial entry
283+
Buffer::<3>::new();
284+
}
285+
286+
#[test]
287+
#[should_panic(expected = "memory size has to be divisible by 4")]
288+
fn invalid_buffer_size() {
289+
// the buffer size is not really an issue here, but the code is easier
290+
// to write/read if the buffer size is always a multiple of the header
291+
// size, i.e. the size of an entry, which is `4`.
292+
Buffer::<13>::new();
293+
}
294+
253295
#[test]
254296
fn entry_iter() {
255297
let buffer = Buffer::<32>::new();
@@ -278,6 +320,34 @@ mod tests {
278320
assert_eq!(buffer[ValidatedOffset(8)], Entry::free(12));
279321
}
280322

323+
#[test]
324+
#[should_panic]
325+
fn at_out_of_bounds() {
326+
let buffer = Buffer::<32>::new();
327+
buffer.at(64); // panic here
328+
}
329+
330+
#[test]
331+
#[should_panic]
332+
fn at_mut_out_of_bounds() {
333+
let mut buffer = Buffer::<32>::new();
334+
buffer.at_mut(64); // panic here
335+
}
336+
337+
#[test]
338+
#[should_panic]
339+
fn at_unaligned() {
340+
let buffer = Buffer::<32>::new();
341+
buffer.at(2); // panic here
342+
}
343+
344+
#[test]
345+
#[should_panic]
346+
fn at_mut_unaligned() {
347+
let mut buffer = Buffer::<32>::new();
348+
buffer.at_mut(2); // panic here
349+
}
350+
281351
#[test]
282352
fn following_free_entry() {
283353
let mut buffer = Buffer::<24>::new();

src/raw_allocator/entry.rs

+24
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ impl Debug for Entry {
101101
mod tests {
102102
use super::{Entry, State};
103103

104+
#[test]
105+
fn equality() {
106+
assert_eq!(Entry::used(4), Entry::used(4));
107+
assert_ne!(Entry::used(4), Entry::used(5));
108+
109+
assert_eq!(Entry::free(4), Entry::free(4));
110+
assert_ne!(Entry::free(4), Entry::free(5));
111+
112+
assert_ne!(Entry::used(4), Entry::free(4));
113+
assert_ne!(Entry::used(4), Entry::free(5));
114+
}
115+
104116
#[test]
105117
fn entry_bitpacking_state() {
106118
assert_eq!(Entry::free(5).state(), State::Free);
@@ -131,4 +143,16 @@ mod tests {
131143
// alignment as a `u32`, i.e. `4`
132144
assert_eq!(mem::align_of::<Entry>(), mem::align_of::<u32>());
133145
}
146+
147+
#[test]
148+
fn debug_representation() {
149+
assert_eq!(
150+
format!("{:?}", Entry::used(123)),
151+
"Entry { state: Used, size: 123 }"
152+
);
153+
assert_eq!(
154+
format!("{:?}", Entry::free(456)),
155+
"Entry { state: Free, size: 456 }"
156+
);
157+
}
134158
}

0 commit comments

Comments
 (0)