mirror of
https://github.com/google/pebble.git
synced 2025-05-28 14:03:12 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
347
tests/fw/test_heap.c
Normal file
347
tests/fw/test_heap.c
Normal file
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "util/heap.h"
|
||||
|
||||
#include "applib/app_heap_util.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include "fake_pebble_tasks.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_worker_state.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BLOCK_SIZE sizeof(unsigned long)
|
||||
|
||||
// Stubs
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
void MPU_vTaskSuspendAll(void) {}
|
||||
void MPU_xTaskResumeAll(void) {}
|
||||
|
||||
// Tests
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
void test_heap__should_handle_uniform_blocks(void) {
|
||||
// Smoke test:
|
||||
// - Alloc 15 uniform blocks
|
||||
// - Free 15 uniform blocks
|
||||
// - Alloc 15 uniform blocks
|
||||
const int heap_size_blocks = 30;
|
||||
int heap_size_bytes = BLOCK_SIZE * heap_size_blocks;
|
||||
void* heap_space = malloc(heap_size_bytes);
|
||||
cl_assert(heap_space != NULL);
|
||||
|
||||
Heap heap;
|
||||
|
||||
// Test init
|
||||
heap_init(&heap, (void*) heap_space, (void*) (heap_space + heap_size_bytes), false);
|
||||
cl_assert(heap.begin == heap_space);
|
||||
cl_assert(heap.end == (heap_space + heap_size_bytes));
|
||||
cl_assert(heap.current_size == 0);
|
||||
|
||||
void* ptr = NULL;
|
||||
|
||||
// Alloc
|
||||
for (int i = 0; i < heap_size_blocks / 2; i++) {
|
||||
//printf("Allocating block=<%d>\n", i);
|
||||
// Test malloc
|
||||
ptr = heap_malloc(&heap, sizeof(BLOCK_SIZE), 0);
|
||||
cl_assert(ptr != NULL);
|
||||
|
||||
const int expected_remaining_blocks = heap_size_blocks - 2 * (i + 1);
|
||||
cl_assert(expected_remaining_blocks >= 0);
|
||||
|
||||
if (i == 14) {
|
||||
// Heap should now be full
|
||||
cl_assert(heap.current_size == heap_size_bytes);
|
||||
} else {
|
||||
// Free block list should still be size 1
|
||||
cl_assert(heap.current_size == 2*BLOCK_SIZE * (i + 1));
|
||||
}
|
||||
|
||||
// Block is allocated at the *end* of the heap
|
||||
uint8_t* expected_addr = (uint8_t*) heap_space + i*BLOCK_SIZE*2 + BLOCK_SIZE;
|
||||
printf("Expected addr at=<%p>; got ptr to=<%p>\n", expected_addr, (uint8_t*) ptr);
|
||||
cl_assert(expected_addr == ((uint8_t*) ptr));
|
||||
}
|
||||
|
||||
ptr = heap_malloc(&heap, BLOCK_SIZE, 0);
|
||||
cl_assert(ptr == NULL);
|
||||
cl_assert(heap.current_size == heap_size_blocks*BLOCK_SIZE);
|
||||
|
||||
// Free
|
||||
// Reset ptr to first alloc'd element and iterate over all (uniform) elements
|
||||
ptr = ((uint8_t*) heap_space) + BLOCK_SIZE;
|
||||
for (int i = 0; i < heap_size_blocks / 2; i++) {
|
||||
heap_free(&heap, ptr, 0);
|
||||
cl_assert(heap.current_size == (heap_size_blocks-(2*(i+1)))*BLOCK_SIZE);
|
||||
ptr += 2 * BLOCK_SIZE;
|
||||
}
|
||||
|
||||
cl_assert(heap.current_size == 0);
|
||||
|
||||
// Alloc again to ensure free()'s worked
|
||||
for (int i = 0; i < heap_size_blocks / 2; i++) {
|
||||
// Test malloc
|
||||
ptr = heap_malloc(&heap, BLOCK_SIZE, 0);
|
||||
|
||||
cl_assert(ptr != NULL);
|
||||
const int expected_remaining_blocks = heap_size_blocks - 2 * (i + 1);
|
||||
cl_assert(expected_remaining_blocks >= 0);
|
||||
|
||||
if (i == 14) {
|
||||
// Heap should now be full
|
||||
cl_assert(heap.current_size == heap_size_bytes);
|
||||
} else {
|
||||
// Free block list should still be size 1
|
||||
cl_assert(heap.current_size == 2*BLOCK_SIZE * (i + 1));
|
||||
}
|
||||
// Block is allocated at the *end* of the heap
|
||||
uint8_t* expected_addr = (uint8_t*) heap_space + i*BLOCK_SIZE*2 + BLOCK_SIZE;
|
||||
cl_assert(expected_addr == ((uint8_t*) ptr));
|
||||
}
|
||||
cl_assert(heap.begin == heap_space);
|
||||
|
||||
ptr = heap_malloc(&heap, BLOCK_SIZE, 0);
|
||||
cl_assert(ptr == NULL);
|
||||
cl_assert(heap.current_size == heap_size_blocks*BLOCK_SIZE);
|
||||
|
||||
free(heap_space);
|
||||
}
|
||||
|
||||
void test_heap__realloc(void) {
|
||||
int heap_size_bytes = BLOCK_SIZE * 15;
|
||||
void *heap_space = malloc(heap_size_bytes);
|
||||
cl_assert(heap_space != NULL);
|
||||
|
||||
Heap heap;
|
||||
|
||||
heap_init(&heap, (void*)heap_space, (void*)(heap_space + heap_size_bytes), false);
|
||||
cl_assert(heap.begin == heap_space);
|
||||
|
||||
unsigned int *ptr = NULL;
|
||||
|
||||
// Allocate a block, realloc to the same size, make sure data is the same.
|
||||
ptr = heap_malloc(&heap, sizeof(unsigned int)*5, 0);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ptr[i] = i;
|
||||
}
|
||||
// Realloc, but OOM for requested size. Should return NULL...
|
||||
unsigned int *oom_ptr = heap_realloc(&heap, ptr, heap_size_bytes + 1, 0);
|
||||
cl_assert_equal_p(oom_ptr, NULL);
|
||||
// ... but leave original block untouched:
|
||||
ptr = heap_realloc(&heap, ptr, sizeof(unsigned int)*5, 0);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
cl_assert(ptr[i] == i);
|
||||
}
|
||||
heap_free(&heap, ptr, 0);
|
||||
|
||||
// Allocate a block, realloc to a larger size, make sure all data copied.
|
||||
ptr = heap_malloc(&heap, sizeof(unsigned int)*5, 0);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ptr[i] = i;
|
||||
}
|
||||
ptr = heap_realloc(&heap, ptr, sizeof(unsigned int)*10, 0);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
cl_assert(ptr[i] == i);
|
||||
}
|
||||
heap_free(&heap, ptr, 0);
|
||||
|
||||
// Allocate a block, realloc to a smaller size, make data copied.
|
||||
ptr = heap_malloc(&heap, sizeof(unsigned int)*10, 0);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ptr[i] = i;
|
||||
}
|
||||
ptr = heap_realloc(&heap, ptr, sizeof(unsigned int)*5, 0);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
cl_assert(ptr[i] == i);
|
||||
}
|
||||
heap_free(&heap, ptr, 0);
|
||||
|
||||
// realloc NULL:
|
||||
ptr = heap_realloc(&heap, NULL, 10, 0);
|
||||
cl_assert(ptr);
|
||||
heap_free(&heap, ptr, 0);
|
||||
}
|
||||
|
||||
void test_heap__should_handle_irregular_blocks(void) {
|
||||
}
|
||||
|
||||
void test_heap__unaligned_start_end(void) {
|
||||
// Make a little word aligned buffer to use as our heap.
|
||||
uintptr_t int_buffer[8];
|
||||
char *char_buffer = (char*) &int_buffer[0];
|
||||
|
||||
{
|
||||
Heap heap;
|
||||
heap_init(&heap, char_buffer + 1, char_buffer + 16, false);
|
||||
cl_assert_equal_i(heap_size(&heap), 8);
|
||||
cl_assert(heap_contains_address(&heap, char_buffer + 8));
|
||||
cl_assert(heap_contains_address(&heap, char_buffer + 12));
|
||||
cl_assert(!heap_contains_address(&heap, char_buffer + 2));
|
||||
}
|
||||
|
||||
{
|
||||
Heap heap;
|
||||
heap_init(&heap, char_buffer + 8, char_buffer + 21, false);
|
||||
cl_assert_equal_i(heap_size(&heap), 8);
|
||||
cl_assert(heap_contains_address(&heap, char_buffer + 8));
|
||||
cl_assert(heap_contains_address(&heap, char_buffer + 12));
|
||||
cl_assert(!heap_contains_address(&heap, char_buffer + 18));
|
||||
}
|
||||
}
|
||||
|
||||
void test_heap___heap_bytes_free(void) {
|
||||
stub_pebble_tasks_set_current(PebbleTask_App);
|
||||
|
||||
int heap_size_bytes = 1024;
|
||||
int malloc_size_bytes = 256;
|
||||
|
||||
void *heap_space = malloc(heap_size_bytes);
|
||||
cl_assert(heap_space != NULL);
|
||||
|
||||
// Retrieve application heap, allocate space for it.
|
||||
Heap *heap = app_state_get_heap();
|
||||
heap_init(heap, (void*)heap_space, (void*)(heap_space + heap_size_bytes), false);
|
||||
cl_assert(heap->begin == heap_space);
|
||||
|
||||
int before_available = heap_bytes_free();
|
||||
|
||||
unsigned int *ptr = NULL;
|
||||
ptr = heap_malloc(heap, malloc_size_bytes, 0);
|
||||
memset(ptr, 'X', malloc_size_bytes);
|
||||
|
||||
int after_available = heap_bytes_free();
|
||||
|
||||
// make sure the two values are within 16 bytes (usually shoule be 0-8, but 16 for safety)
|
||||
cl_assert(abs((before_available - malloc_size_bytes) - after_available) < 16);
|
||||
|
||||
heap_free(heap, ptr, 0);
|
||||
free(heap_space);
|
||||
}
|
||||
|
||||
void test_heap__heap_bytes_used(void) {
|
||||
stub_pebble_tasks_set_current(PebbleTask_App);
|
||||
|
||||
int heap_size_bytes = 1024;
|
||||
int malloc_size_bytes = 256;
|
||||
|
||||
void *heap_space = malloc(heap_size_bytes);
|
||||
cl_assert(heap_space != NULL);
|
||||
|
||||
// Retrieve application heap, allocate space for it.
|
||||
Heap *heap = app_state_get_heap();
|
||||
heap_init(heap, (void*)heap_space, (void*)(heap_space + heap_size_bytes), false);
|
||||
cl_assert(heap->begin == heap_space);
|
||||
|
||||
int before_used = heap_bytes_used();
|
||||
|
||||
unsigned int *ptr = NULL;
|
||||
ptr = heap_malloc(heap, malloc_size_bytes, 0);
|
||||
memset(ptr, 'X', malloc_size_bytes);
|
||||
|
||||
int after_used = heap_bytes_used();
|
||||
|
||||
// make sure the two values are within 16 bytes (usually shoule be 0-8, but 16 for safety)
|
||||
cl_assert(abs((before_used + malloc_size_bytes) - (after_used)) < 16);
|
||||
|
||||
heap_free(heap, ptr, 0);
|
||||
free(heap_space);
|
||||
}
|
||||
|
||||
void test_heap__is_allocated(void) {
|
||||
stub_pebble_tasks_set_current(PebbleTask_App);
|
||||
|
||||
const size_t heap_size_bytes = 2048;
|
||||
|
||||
void *heap_space = malloc(heap_size_bytes);
|
||||
cl_assert(heap_space != NULL);
|
||||
|
||||
// Retrieve application heap, allocate space for it.
|
||||
Heap *heap = app_state_get_heap();
|
||||
heap_init(heap, (void*)heap_space, (void*)(heap_space + heap_size_bytes), false);
|
||||
cl_assert(heap->begin == heap_space);
|
||||
|
||||
// Allocate a few things
|
||||
const size_t malloc_size_bytes = 13;
|
||||
const size_t num_allocs = 10;
|
||||
void *allocs[num_allocs];
|
||||
for (size_t i = 0; i < num_allocs; ++i) {
|
||||
allocs[i] = heap_malloc(heap, malloc_size_bytes * i, 0);
|
||||
}
|
||||
|
||||
void *needle = allocs[num_allocs / 2];
|
||||
cl_assert(heap_is_allocated(heap, needle));
|
||||
|
||||
void *bad_needle = allocs[num_allocs / 3] + 1;
|
||||
cl_assert(!heap_is_allocated(heap, bad_needle));
|
||||
|
||||
void *past_heap = (void *)heap->end + 1;
|
||||
cl_assert(!heap_is_allocated(heap, past_heap));
|
||||
|
||||
void *pre_heap = (void *)heap->begin - 1;
|
||||
cl_assert(!heap_is_allocated(heap, pre_heap));
|
||||
|
||||
void *freed_needle = allocs[num_allocs / 2];
|
||||
heap_free(heap, freed_needle, 0);
|
||||
cl_assert(!heap_is_allocated(heap, freed_needle));
|
||||
|
||||
free(heap_space);
|
||||
}
|
||||
|
||||
static void prv_alloc_and_test_fuzz_on_free(bool enabled) {
|
||||
stub_pebble_tasks_set_current(PebbleTask_App);
|
||||
|
||||
const size_t heap_size_bytes = 2048;
|
||||
|
||||
void *heap_space = malloc(heap_size_bytes);
|
||||
cl_assert(heap_space != NULL);
|
||||
|
||||
// Retrieve application heap, allocate space for it.
|
||||
Heap *heap = app_state_get_heap();
|
||||
heap_init(heap, (void*)heap_space, (void*)(heap_space + heap_size_bytes), enabled);
|
||||
cl_assert(heap->begin == heap_space);
|
||||
|
||||
char *test_string = "data to store in heap";
|
||||
uint8_t *test = heap_malloc(heap, strlen(test_string), 0);
|
||||
memcpy(test, test_string, strlen(test_string));
|
||||
|
||||
cl_assert_equal_i(memcmp(test, test_string, strlen(test_string)), 0);
|
||||
heap_free(heap, test, 0);
|
||||
|
||||
if (enabled) {
|
||||
// the memory was fuzz'ed, better not match
|
||||
cl_assert(memcmp(test, test_string, strlen(test_string)) != 0);
|
||||
} else {
|
||||
// free'd data should match what was already there
|
||||
cl_assert_equal_i(memcmp(test, test_string, strlen(test_string)), 0);
|
||||
}
|
||||
|
||||
free(heap_space);
|
||||
}
|
||||
|
||||
void test_heap__fuzz_on_free(void) {
|
||||
prv_alloc_and_test_fuzz_on_free(true);
|
||||
prv_alloc_and_test_fuzz_on_free(false);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue