/* * 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/circular_buffer.h" #include "clar.h" #include #include "stubs_passert.h" #include "stubs_pbl_malloc.h" void test_circular_buffer__initialize(void) { } void test_circular_buffer__cleanup(void) { } void test_circular_buffer__circular_buffer(void) { CircularBuffer buffer; uint8_t storage[8]; circular_buffer_init(&buffer, storage, sizeof(storage)); const uint8_t* out_buffer; uint16_t out_length; // We should start out empty cl_assert(!circular_buffer_read(&buffer, 1, &out_buffer, &out_length)); cl_assert(circular_buffer_write(&buffer, (uint8_t*) "123", 3)); cl_assert_equal_i(circular_buffer_get_write_space_remaining(&buffer), 5); cl_assert(circular_buffer_write(&buffer, (uint8_t*) "456", 3)); cl_assert_equal_i(circular_buffer_get_write_space_remaining(&buffer), 2); cl_assert(!circular_buffer_write(&buffer, (uint8_t*) "789", 3)); // too big cl_assert_equal_i(circular_buffer_get_write_space_remaining(&buffer), 2); cl_assert(circular_buffer_read(&buffer, 4, &out_buffer, &out_length)); cl_assert_equal_i(out_length, 4); cl_assert(memcmp(out_buffer, "1234", 4) == 0); cl_assert_equal_i(circular_buffer_get_write_space_remaining(&buffer), 2); cl_assert(circular_buffer_consume(&buffer, 4)); cl_assert_equal_i(circular_buffer_get_write_space_remaining(&buffer), 6); // Now there's just 56 in the buffer. Fill it to the brim cl_assert(circular_buffer_write(&buffer, (uint8_t*) "789", 3)); cl_assert_equal_i(circular_buffer_get_write_space_remaining(&buffer), 3); cl_assert(circular_buffer_write(&buffer, (uint8_t*) "abc", 3)); cl_assert_equal_i(circular_buffer_get_write_space_remaining(&buffer), 0); cl_assert(!circular_buffer_write(&buffer, (uint8_t*) "d", 1)); // too full cl_assert_equal_i(circular_buffer_get_write_space_remaining(&buffer), 0); // Try a wrapped read cl_assert(circular_buffer_read(&buffer, 6, &out_buffer, &out_length)); cl_assert_equal_i(out_length, 4); cl_assert(memcmp(out_buffer, (uint8_t*) "5678", 4) == 0); cl_assert(circular_buffer_consume(&buffer, 4)); // Get the rest of the wrapped read cl_assert(circular_buffer_read(&buffer, 2, &out_buffer, &out_length)); cl_assert_equal_i(out_length, 2); cl_assert(memcmp(out_buffer, (uint8_t*) "9a", 2) == 0); cl_assert(circular_buffer_consume(&buffer, 2)); // Consume one without reading it cl_assert(circular_buffer_consume(&buffer, 1)); // Read the last little bit cl_assert(circular_buffer_read(&buffer, 1, &out_buffer, &out_length)); cl_assert_equal_i(out_length, 1); cl_assert(memcmp(out_buffer, (uint8_t*) "c", 1) == 0); cl_assert(circular_buffer_consume(&buffer, 1)); // And we should be empty cl_assert(!circular_buffer_read(&buffer, 1, &out_buffer, &out_length)); cl_assert(!circular_buffer_consume(&buffer, 1)); } void test_circular_buffer__copy(void) { CircularBuffer buffer; uint8_t storage[8]; circular_buffer_init(&buffer, storage, sizeof(storage)); const uint16_t data_out_size = 8; uint8_t data_out[data_out_size]; // Test copy when there is nothing in the buffer: cl_assert_equal_i(circular_buffer_copy(&buffer, data_out, data_out_size), 0); // Write + consume, so read index is at 2: circular_buffer_write(&buffer, (uint8_t *)"0123", 4); circular_buffer_consume(&buffer, 2); // Write data that will be wrapped: circular_buffer_write(&buffer, (uint8_t *)"456789", 6); // Test copying the whole thing (providing buffer of 8 bytes): memset(data_out, 0, data_out_size); cl_assert_equal_i(circular_buffer_copy(&buffer, data_out, data_out_size), 8); cl_assert(memcmp("23456789", data_out, 8) == 0); // Test partial copy (providing buffer of 6 bytes): memset(data_out, 0, data_out_size); cl_assert_equal_i(circular_buffer_copy(&buffer, data_out, 6), 6); cl_assert(memcmp("234567", data_out, 6) == 0); } void test_circular_buffer__copy_offset(void) { CircularBuffer buffer; uint8_t storage[8]; circular_buffer_init(&buffer, storage, sizeof(storage)); const uint16_t data_out_size = 8; uint8_t data_out[data_out_size]; // Assert zero bytes copied, empty buffer: cl_assert_equal_i(circular_buffer_copy_offset(&buffer, 0 /* start_offset */, data_out, data_out_size), 0); // Assert zero bytes copied, start offset > storage size: cl_assert_equal_i(circular_buffer_copy_offset(&buffer, sizeof(storage) + 1 /* start_offset */, data_out, data_out_size), 0); // Valid offset, non-wrapping copy: circular_buffer_write(&buffer, (uint8_t *)"0123", 4); cl_assert_equal_i(circular_buffer_copy_offset(&buffer, 3 /* start_offset */, data_out, data_out_size), 1); cl_assert(memcmp("3", data_out, 1) == 0); // Offset as long as the available data: cl_assert_equal_i(circular_buffer_copy_offset(&buffer, 4 /* start_offset */, data_out, data_out_size), 0); // Free up 2 bytes at the beginning: circular_buffer_consume(&buffer, 2); // Write data that will be wrapped: cl_assert_equal_b(circular_buffer_write(&buffer, (uint8_t *)"456789", 6), true); cl_assert_equal_i(circular_buffer_copy_offset(&buffer, 2 /* start_offset */, data_out, data_out_size), 6); cl_assert(memcmp("456789", data_out, 6) == 0); } void test_circular_buffer__direct_write(void) { CircularBuffer buffer; uint8_t storage[8]; circular_buffer_init(&buffer, storage, sizeof(storage)); circular_buffer_write(&buffer, (uint8_t *)"0123", 4); uint8_t *data_out; uint16_t contiguous_num_bytes_left = circular_buffer_write_prepare(&buffer, &data_out); cl_assert_equal_i(contiguous_num_bytes_left, 4); memcpy(data_out, "456", 3); circular_buffer_write_finish(&buffer, 3); cl_assert_equal_i(circular_buffer_get_write_space_remaining(&buffer), 1); cl_assert_equal_i(circular_buffer_get_read_space_remaining(&buffer), 7); contiguous_num_bytes_left = circular_buffer_write_prepare(&buffer, &data_out); cl_assert_equal_i(contiguous_num_bytes_left, 1); memcpy(data_out, "7", 1); circular_buffer_write_finish(&buffer, 1); cl_assert_equal_i(circular_buffer_get_write_space_remaining(&buffer), 0); cl_assert_equal_i(circular_buffer_get_read_space_remaining(&buffer), 8); contiguous_num_bytes_left = circular_buffer_write_prepare(&buffer, &data_out); cl_assert_equal_i(contiguous_num_bytes_left, 0); cl_assert_equal_p(data_out, NULL); const uint16_t copy_out_size = 8; uint8_t copy_out[copy_out_size]; cl_assert_equal_i(circular_buffer_copy(&buffer, copy_out, copy_out_size), 8); cl_assert_equal_i(memcmp(copy_out, "01234567", 8), 0); circular_buffer_consume(&buffer, 2); cl_assert_equal_i(circular_buffer_copy(&buffer, copy_out, copy_out_size), 6); cl_assert_equal_i(memcmp(copy_out, "234567", 6), 0); contiguous_num_bytes_left = circular_buffer_write_prepare(&buffer, &data_out); cl_assert_equal_i(contiguous_num_bytes_left, 2); memcpy(data_out, "AB", 2); circular_buffer_write_finish(&buffer, 2); cl_assert_equal_i(circular_buffer_copy(&buffer, copy_out, copy_out_size), 8); cl_assert_equal_i(memcmp(copy_out, "234567AB", 8), 0); contiguous_num_bytes_left = circular_buffer_write_prepare(&buffer, &data_out); cl_assert_equal_i(contiguous_num_bytes_left, 0); cl_assert_equal_p(data_out, NULL); } void test_circular_buffer__read_or_copy_returns_false_when_length_is_too_long(void) { CircularBuffer buffer; uint8_t storage[1]; circular_buffer_init(&buffer, storage, sizeof(storage)); uint8_t *data_out = NULL; bool caller_should_free = false; cl_assert_equal_b(false, circular_buffer_read_or_copy(&buffer, &data_out, sizeof(storage) + 1, malloc, &caller_should_free)); } void test_circular_buffer__read_or_copy_doesnt_copy_when_already_continguously_stored(void) { CircularBuffer buffer; uint8_t storage[8]; circular_buffer_init(&buffer, storage, sizeof(storage)); circular_buffer_write(&buffer, (uint8_t *)"01234567", sizeof(storage)); uint8_t *data_out = NULL; bool caller_should_free = true; cl_assert_equal_b(true, circular_buffer_read_or_copy(&buffer, &data_out, sizeof(storage), malloc, &caller_should_free)); cl_assert_equal_b(false, caller_should_free); cl_assert_equal_p(data_out, storage); } static void *prv_oom_malloc(size_t length) { return NULL; } void test_circular_buffer__read_or_copy_does_copy_when_not_continguously_stored(void) { CircularBuffer buffer; uint8_t storage[8]; circular_buffer_init(&buffer, storage, sizeof(storage)); circular_buffer_write(&buffer, (uint8_t *)"01234567", sizeof(storage)); circular_buffer_consume(&buffer, 1); circular_buffer_write(&buffer, (uint8_t *)"8", 1); uint8_t *data_out = NULL; bool caller_should_free = false; cl_assert_equal_b(true, circular_buffer_read_or_copy(&buffer, &data_out, sizeof(storage), malloc, &caller_should_free)); cl_assert_equal_b(true, caller_should_free); cl_assert_equal_m(data_out, "12345678", sizeof(storage)); free(data_out); // Test OOM scenario: cl_assert_equal_b(false, circular_buffer_read_or_copy(&buffer, &data_out, sizeof(storage), prv_oom_malloc, &caller_should_free)); cl_assert_equal_p(data_out, NULL); cl_assert_equal_b(false, caller_should_free); } void test_circular_buffer__read_while_write_pending(void) { CircularBuffer buffer; uint8_t storage[8]; circular_buffer_init(&buffer, storage, sizeof(storage)); uint8_t letterA = 'A'; cl_assert(circular_buffer_write(&buffer, &letterA, sizeof(letterA))); uint8_t *data_buf; uint16_t num_bytes = circular_buffer_write_prepare(&buffer, &data_buf); cl_assert_equal_i(sizeof(storage) - sizeof(letterA), num_bytes); uint8_t letterB = 'B'; data_buf[0] = letterB; cl_assert(circular_buffer_read( &buffer, sizeof(letterA), (const uint8_t **)&data_buf, &num_bytes)); cl_assert_equal_i(num_bytes, sizeof(letterA)); cl_assert_equal_m(data_buf, &letterA, sizeof(letterA)); cl_assert(circular_buffer_consume(&buffer, sizeof(letterA))); circular_buffer_write_finish(&buffer, sizeof(letterB)); cl_assert(circular_buffer_read( &buffer, sizeof(letterB), (const uint8_t **)&data_buf, &num_bytes)); cl_assert_equal_i(num_bytes, sizeof(letterB)); cl_assert_equal_m(data_buf, &letterB, sizeof(letterB)); }