Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View file

@ -0,0 +1,90 @@
/*
* 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/base64.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "clar.h"
#include "stubs_passert.h"
// Stubs
///////////////////////////////////////////////////////////
int g_pbl_log_level = 0;
void pbl_log(const char* src_filename, int src_line_number, const char* fmt, ...) { }
// Tests
///////////////////////////////////////////////////////////
static void prv_test_decode_encode(const char* test_name, char* buffer, unsigned int buffer_length,
const uint8_t* expected, unsigned int expected_length) {
cl_assert(buffer_length % 4 == 0);
char original_in[buffer_length + 1];
memcpy(original_in, buffer, buffer_length + 1);
// test decode
unsigned int num_bytes = base64_decode_inplace(buffer, buffer_length);
cl_assert(num_bytes == expected_length);
cl_assert(memcmp(buffer, expected, num_bytes) == 0);
// test encode
char out[buffer_length + 1];
int result = base64_encode(out, buffer_length + 1, expected, expected_length);
cl_assert_equal_i(result, buffer_length);
cl_assert_equal_m(out, original_in, buffer_length);
}
void test_base64__initialize(void) {
}
void test_base64__cleanup(void) {
}
void test_base64__decode(void) {
{
char buffer[] = "abcd";
const uint8_t expected[] = { 0x69, 0xb7, 0x1d };
prv_test_decode_encode("basic", buffer, 4, expected, 3);
}
{
char buffer[] = "ABCD";
const uint8_t expected[] = { 0x0, 0x10, 0x83 };
prv_test_decode_encode("upper", buffer, 4, expected, 3);
}
{
char buffer[] = "abcdABCD";
const uint8_t expected[] = { 0x69, 0xb7, 0x1d, 0x0, 0x10, 0x83 };
prv_test_decode_encode("twobyte", buffer, 8, expected, 6);
}
{
char buffer[] = "vu8=";
const uint8_t expected[] = { 0xbe, 0xef };
prv_test_decode_encode("1pad", buffer, 4, expected, 2);
}
{
char buffer[] = "aQ==";
const uint8_t expected[] = { 0x69 };
prv_test_decode_encode("2pad", buffer, 4, expected, 1);
}
}

132
tests/fw/util/test_buffer.c Normal file
View file

@ -0,0 +1,132 @@
/*
* 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/buffer.h"
#include "clar.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include <string.h>
static const char* test_data = "This is a very complicated case, Maude.";
void test_buffer__should_add_data_until_full(void) {
const size_t buffer_size = 101;
Buffer* b = buffer_create(buffer_size);
int bytes_written = 0;
int num_elements = buffer_size / sizeof(test_data);
for (int i = 0; i < num_elements; ++i) {
cl_assert_equal_i(b->bytes_written, i * sizeof(test_data));
cl_assert_equal_i(buffer_get_bytes_remaining(b), buffer_size - (i * sizeof(test_data)));
bytes_written += buffer_add(b, (uint8_t *) test_data, sizeof(test_data));
cl_assert_equal_i(bytes_written, (i + 1) * sizeof(test_data));
cl_assert_equal_i(buffer_get_bytes_remaining(b), buffer_size - ((i+1) * sizeof(test_data)));
}
cl_assert(buffer_get_bytes_remaining(b) > 0);
bytes_written = buffer_add(b, (uint8_t *) test_data, sizeof(test_data));
cl_assert_equal_i(bytes_written, 0);
free(b);
}
void test_buffer__cannot_remove_beyond_written(void) {
Buffer *b = buffer_create(5);
cl_assert_equal_i(0, buffer_remove(b, 0, 0));
cl_assert_passert(buffer_remove(b, 0, 1));
uint8_t b1 = 1;
buffer_add(b, &b1, sizeof(uint8_t));
cl_assert_passert(buffer_remove(b, 0, 2));
cl_assert_equal_i(1, b->bytes_written);
cl_assert_equal_i(1, buffer_remove(b, 0, 1));
cl_assert_equal_i(0, b->bytes_written);
free(b);
}
void test_buffer__can_remove(void) {
uint8_t b1 = 1;
uint8_t b2 = 2;
uint8_t b3 = 3;
uint8_t b4 = 4;
Buffer *b = buffer_create(5);
// works on empty buffer
cl_assert_equal_i(0, buffer_remove(b, 0, 0));
buffer_add(b, &b1, sizeof(uint8_t));
buffer_add(b, &b2, sizeof(uint8_t));
buffer_add(b, &b3, sizeof(uint8_t));
buffer_add(b, &b4, sizeof(uint8_t));
// handles out of bounds cases
cl_assert_passert(buffer_remove(b, 0, 5));
cl_assert_passert(buffer_remove(b, 1, 4));
cl_assert_equal_i(4, b->bytes_written);
cl_assert_equal_i(b->data[0], b1);
cl_assert_equal_i(b->data[1], b2);
cl_assert_equal_i(b->data[2], b3);
cl_assert_equal_i(b->data[3], b4);
// moves removed remaining bytes to close the gap
cl_assert_equal_i(2, buffer_remove(b, 1*sizeof(uint8_t), 2*sizeof(uint8_t)));
cl_assert_equal_i(2, b->bytes_written);
cl_assert_equal_i(b->data[0], b1);
cl_assert_equal_i(b->data[1], b4);
free(b);
}
void test_buffer__can_remove_interior_data(void) {
uint8_t b1 = 1;
uint8_t b2 = 2;
uint8_t b3 = 3;
uint8_t b4 = 4;
Buffer *b = buffer_create(4);
buffer_add(b, &b1, sizeof(uint8_t));
buffer_add(b, &b2, sizeof(uint8_t));
buffer_add(b, &b3, sizeof(uint8_t));
buffer_add(b, &b4, sizeof(uint8_t));
// removing second element shifts elements three and four to overwrite it
cl_assert_equal_i(sizeof(uint8_t), buffer_remove(b, 1 * sizeof(uint8_t), sizeof(uint8_t)));
cl_assert_equal_i(b->bytes_written, 3);
cl_assert_equal_i(b->data[0], b1);
cl_assert_equal_i(b->data[1], b3);
cl_assert_equal_i(b->data[2], b4);
free(b);
}
void test_buffer__can_read_and_write_uint32(void) {
uint32_t expected = 0x12345678;
Buffer *b = buffer_create(4);
buffer_add(b, (const uint8_t* const)&expected, sizeof(expected));
cl_assert_equal_i(expected, *(uint32_t*)b->data);
free(b);
}

393
tests/fw/util/test_dict.c Normal file
View file

@ -0,0 +1,393 @@
/*
* 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/dict.h"
#include "util/math.h"
#include "util/net.h"
#include "util/size.h"
#include "clar.h"
#include <string.h>
#include <stdbool.h>
#include <strings.h>
// Stubs
///////////////////////////////////////////////////////////
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
// Tests
///////////////////////////////////////////////////////////
void test_dict__initialize(void) {
}
void test_dict__cleanup(void) {
}
static const uint32_t SOME_DATA_KEY = 0xb00bf00b;
static const uint8_t SOME_DATA[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
static const uint32_t SOME_STRING_KEY = 0xbeefbabe;
static const char *SOME_STRING = "Hello World";
static const uint32_t SOME_NULL_KEY = 0x0;
static const uint32_t SOME_EMPTY_STRING_KEY = 0x1;
static const char *SOME_EMPTY_STRING = "";
static const uint32_t SOME_UINT8_KEY = 0x88888888;
static const uint32_t SOME_UINT16_KEY = 0x16161616;
static const uint32_t SOME_UINT32_KEY = 0x32323232;
static const uint32_t SOME_INT8_KEY = 0x11888888;
static const uint32_t SOME_INT16_KEY = 0x11161616;
static const uint32_t SOME_INT32_KEY = 0x11323232;
void test_dict__calc_size(void) {
uint32_t size;
size = dict_calc_buffer_size(0);
cl_assert(size == sizeof(Dictionary));
size = dict_calc_buffer_size(1, 1);
cl_assert(size == sizeof(Dictionary) + sizeof(Tuple) + 1);
size = dict_calc_buffer_size(3, 10, 100, 1000);
cl_assert(size == sizeof(Dictionary) + (3 * sizeof(Tuple)) + 10 + 100 + 1000);
}
struct SerializeTestResult {
bool okay;
uint16_t expected_size;
};
static void serialize_callback(const uint8_t * const data, const uint16_t size, void *context) {
struct SerializeTestResult *result = context;
result->okay = true;
result->expected_size = size;
// Read back:
DictionaryIterator iter;
Tuple *tuple = dict_read_begin_from_buffer(&iter, data, size);
cl_assert(tuple != NULL);
cl_assert(iter.dictionary->count == 3);
}
void test_dict__tuplets_utils(void) {
Tuplet tuplets[] = {
TupletBytes(SOME_DATA_KEY, SOME_DATA, sizeof(SOME_DATA)),
TupletCString(SOME_STRING_KEY, SOME_STRING),
TupletInteger(SOME_UINT32_KEY, (uint32_t) 32),
};
const uint32_t size = dict_calc_buffer_size_from_tuplets(tuplets, 3);
cl_assert(size == sizeof(Dictionary) + (3 * sizeof(Tuple)) + sizeof(SOME_DATA) + strlen(SOME_STRING) + 1 + sizeof(uint32_t));
struct SerializeTestResult context = { .okay = false, .expected_size = size };
DictionaryResult result = dict_serialize_tuplets(serialize_callback, &context,
tuplets, 3);
cl_assert(result == DICT_OK);
cl_assert(context.okay == true);
cl_assert(context.expected_size == size);
}
void test_dict__write_read(void) {
// Stack allocated buffer:
const uint8_t key_count = 10;
const uint32_t size = dict_calc_buffer_size(key_count,
sizeof(SOME_DATA),
strlen(SOME_STRING) + 1,
sizeof(uint8_t),
sizeof(uint16_t),
sizeof(uint32_t),
sizeof(int8_t),
sizeof(int16_t),
sizeof(int32_t),
0,
strlen(SOME_EMPTY_STRING) + 1);
const uint32_t surplus = 16; // allocate more than needed, see comment with the `final_size` test
uint8_t buffer[size + surplus];
// Write:
DictionaryIterator iter;
DictionaryResult result;
result = dict_write_begin(&iter, buffer, sizeof(buffer));
cl_assert(result == DICT_OK);
result = dict_write_data(&iter, SOME_DATA_KEY, SOME_DATA, sizeof(SOME_DATA));
cl_assert(result == DICT_OK);
result = dict_write_cstring(&iter, SOME_STRING_KEY, SOME_STRING);
cl_assert(result == DICT_OK);
result = dict_write_uint8(&iter, SOME_UINT8_KEY, 8);
cl_assert(result == DICT_OK);
result = dict_write_uint16(&iter, SOME_UINT16_KEY, 16);
cl_assert(result == DICT_OK);
result = dict_write_uint32(&iter, SOME_UINT32_KEY, 32);
cl_assert(result == DICT_OK);
result = dict_write_int8(&iter, SOME_INT8_KEY, -8);
cl_assert(result == DICT_OK);
result = dict_write_int16(&iter, SOME_INT16_KEY, -16);
cl_assert(result == DICT_OK);
result = dict_write_int32(&iter, SOME_INT32_KEY, -32);
cl_assert(result == DICT_OK);
result = dict_write_cstring(&iter, SOME_NULL_KEY, NULL);
cl_assert(result == DICT_OK);
result = dict_write_cstring(&iter, SOME_EMPTY_STRING_KEY, SOME_EMPTY_STRING);
cl_assert(result == DICT_OK);
const uint32_t final_size = dict_write_end(&iter);
cl_assert(result == DICT_OK);
cl_assert(final_size == size);
cl_assert(iter.dictionary->count == key_count);
// Read:
Tuple *tuple = dict_read_begin_from_buffer(&iter, buffer, final_size);
uint8_t count = 0;
bool data_found = false;
bool string_found = false;
bool uint8_found = false;
bool uint16_found = false;
bool uint32_found = false;
bool int8_found = false;
bool int16_found = false;
bool int32_found = false;
bool null_cstring_found = false;
bool empty_cstring_found = false;
while (tuple != NULL) {
++count;
switch (tuple->key) {
case SOME_DATA_KEY:
cl_assert(tuple->length == sizeof(SOME_DATA));
cl_assert(memcmp(tuple->value->data, SOME_DATA, sizeof(SOME_DATA)) == 0);
data_found = true;
break;
case SOME_STRING_KEY:
cl_assert(tuple->length == strlen(SOME_STRING) + 1);
cl_assert(strncmp(tuple->value->cstring, SOME_STRING, strlen(SOME_STRING) + 1) == 0);
// Check zero termination:
cl_assert(tuple->value->cstring[strlen(SOME_STRING)] == 0);
string_found = true;
break;
case SOME_UINT8_KEY:
cl_assert(tuple->length == sizeof(uint8_t));
cl_assert(tuple->value->uint8 == 8);
uint8_found = true;
break;
case SOME_UINT16_KEY:
cl_assert(tuple->length == sizeof(uint16_t));
cl_assert(tuple->value->uint16 == 16);
uint16_found = true;
break;
case SOME_UINT32_KEY:
cl_assert(tuple->length == sizeof(uint32_t));
cl_assert(tuple->value->uint32 == 32);
uint32_found = true;
break;
case SOME_INT8_KEY:
cl_assert(tuple->length == sizeof(int8_t));
cl_assert(tuple->value->int8 == -8);
int8_found = true;
break;
case SOME_INT16_KEY:
cl_assert(tuple->length == sizeof(int16_t));
cl_assert(tuple->value->int16 == -16);
int16_found = true;
break;
case SOME_INT32_KEY:
cl_assert(tuple->length == sizeof(int32_t));
cl_assert(tuple->value->int32 == -32);
int32_found = true;
break;
case SOME_NULL_KEY:
cl_assert(tuple->length == 0);
null_cstring_found = true;
break;
case SOME_EMPTY_STRING_KEY:
cl_assert(tuple->length == strlen(SOME_EMPTY_STRING) + 1);
cl_assert(strncmp(tuple->value->cstring, SOME_EMPTY_STRING, strlen(SOME_EMPTY_STRING) + 1) == 0);
// Check zero termination:
cl_assert(tuple->value->cstring[strlen(SOME_EMPTY_STRING)] == 0);
empty_cstring_found = true;
break;
}
tuple = dict_read_next(&iter);
}
cl_assert(count == key_count);
cl_assert(data_found);
cl_assert(string_found);
cl_assert(uint8_found);
cl_assert(uint16_found);
cl_assert(uint32_found);
cl_assert(int8_found);
cl_assert(int16_found);
cl_assert(int32_found);
cl_assert(null_cstring_found);
cl_assert(empty_cstring_found);
}
void test_dict__out_of_storage(void) {
uint8_t buffer[1];
DictionaryIterator iter;
DictionaryResult result;
result = dict_write_begin(&iter, buffer, 0);
cl_assert(result == DICT_NOT_ENOUGH_STORAGE);
result = dict_write_begin(&iter, buffer, sizeof(buffer));
cl_assert(result == DICT_OK);
result = dict_write_cstring(&iter, SOME_STRING_KEY, SOME_STRING);
cl_assert(result == DICT_NOT_ENOUGH_STORAGE);
}
void test_dict__tuple_header_size(void) {
Tuple t;
t.type = 0;
t.type = ~t.type;
uint8_t num_bits = ffs(t.type + 1) - 1;
cl_assert(num_bits % 8 == 0);
// Test that the .value field isn't part of the header:
cl_assert(sizeof(Tuple) == sizeof(t.key) + sizeof(t.length) + (num_bits / 8));
}
static void *CONTEXT = (void *)0xabcdabcd;
static const char *NEW_STRING = "Bye, bye, World";
static bool is_int8_updated = false;
static bool is_string_updated = false;
static bool should_update_existing_keys_only = false;
static bool test_not_enough_storage = false;
static bool is_data_updated = false;
static void update_key_callback(const uint32_t key, const Tuple *new_tuple, const Tuple *old_tuple, void *context) {
cl_assert(CONTEXT == context);
switch (key) {
case SOME_INT8_KEY:
is_int8_updated = true;
cl_assert(should_update_existing_keys_only == false);
cl_assert(new_tuple->type == TUPLE_INT);
cl_assert(new_tuple->length == sizeof(int8_t));
cl_assert(new_tuple->value->int8 == -3);
cl_assert(old_tuple == NULL_TUPLE);
break;
case SOME_STRING_KEY:
is_string_updated = true;
cl_assert(new_tuple->type == TUPLE_CSTRING);
cl_assert(new_tuple->length == strlen(NEW_STRING) + 1);
cl_assert(strcmp(new_tuple->value->cstring, NEW_STRING) == 0);
cl_assert(old_tuple->type == TUPLE_CSTRING);
cl_assert(old_tuple->length == strlen(SOME_STRING) + 1);
cl_assert(strcmp(old_tuple->value->cstring, SOME_STRING) == 0);
break;
case SOME_DATA_KEY:
is_data_updated = true;
cl_assert(new_tuple->type == TUPLE_BYTE_ARRAY);
cl_assert(new_tuple->length == sizeof(SOME_DATA));
cl_assert(old_tuple->type == TUPLE_BYTE_ARRAY);
cl_assert(old_tuple->length == sizeof(SOME_DATA));
cl_assert(memcmp(new_tuple->value->data, old_tuple->value->data, sizeof(SOME_DATA)) == 0);
break;
default:
break;
}
}
void test_dict__merge(void) {
Tuplet dest_tuplets[] = {
TupletBytes(SOME_DATA_KEY, SOME_DATA, sizeof(SOME_DATA)), // unchanged value
TupletCString(SOME_STRING_KEY, SOME_STRING),
};
Tuplet source_tuplets[] = {
TupletCString(SOME_STRING_KEY, NEW_STRING),
TupletInteger(SOME_INT8_KEY, (int8_t) -3),
};
for (int i = 0; i < 3; ++i) {
is_int8_updated = false;
is_string_updated = false;
is_data_updated = false;
test_not_enough_storage = (i == 2);
should_update_existing_keys_only = (i == 0) || test_not_enough_storage;
uint32_t tmp_size = 0;
const uint32_t source_size = dict_calc_buffer_size_from_tuplets(source_tuplets, ARRAY_LENGTH(source_tuplets));
const uint32_t min_dest_size = dict_calc_buffer_size_from_tuplets(dest_tuplets, ARRAY_LENGTH(dest_tuplets));
const uint32_t dest_size = test_not_enough_storage ? min_dest_size : min_dest_size + source_size;
uint8_t source_buffer[source_size];
tmp_size = source_size; // dict_serialize_tuplets_to_buffer modifies this.
dict_serialize_tuplets_to_buffer(source_tuplets, ARRAY_LENGTH(source_tuplets), source_buffer, &tmp_size);
DictionaryIterator source_iter;
dict_read_begin_from_buffer(&source_iter, source_buffer, source_size);
uint8_t dest_buffer[dest_size];
tmp_size = dest_size; // dict_serialize_tuplets_to_buffer modifies this.
dict_serialize_tuplets_to_buffer(dest_tuplets, ARRAY_LENGTH(dest_tuplets), dest_buffer, &tmp_size);
DictionaryIterator dest_iter;
dict_read_begin_from_buffer(&dest_iter, dest_buffer, dest_size);
tmp_size = dest_size;
dict_merge(&dest_iter, &tmp_size, &source_iter, should_update_existing_keys_only, update_key_callback, (void *) CONTEXT);
cl_assert(is_int8_updated == !should_update_existing_keys_only);
cl_assert(is_string_updated == !test_not_enough_storage);
cl_assert(is_data_updated == !test_not_enough_storage);
enum {
INT8_IDX,
STRING_IDX,
DATA_IDX,
NUM_TUPLES,
};
bool has_tuple[NUM_TUPLES] = { false, false, false };
Tuple *tuple = dict_read_begin_from_buffer(&dest_iter, dest_buffer, tmp_size);
while (tuple) {
switch (tuple->key) {
case SOME_DATA_KEY:
has_tuple[DATA_IDX] = true;
cl_assert(tuple->type == TUPLE_BYTE_ARRAY);
cl_assert(tuple->length == sizeof(SOME_DATA));
cl_assert(memcmp(tuple->value->data, SOME_DATA, sizeof(SOME_DATA)) == 0);
break;
case SOME_STRING_KEY:
has_tuple[STRING_IDX] = true;
cl_assert(tuple->type == TUPLE_CSTRING);
if (test_not_enough_storage) {
// If there is insufficient storage, we don't expect this tuple to
// have been updated (since it can't fit!)
cl_assert(tuple->length == strlen(SOME_STRING) + 1);
cl_assert(strcmp(tuple->value->cstring, SOME_STRING) == 0);
} else {
cl_assert(tuple->length == strlen(NEW_STRING) + 1);
cl_assert(strcmp(tuple->value->cstring, NEW_STRING) == 0);
}
break;
case SOME_INT8_KEY:
has_tuple[INT8_IDX] = true;
cl_assert(should_update_existing_keys_only == false);
cl_assert(tuple->type == TUPLE_INT);
cl_assert(tuple->length == sizeof(int8_t));
cl_assert(tuple->value->int8 == -3);
break;
default:
break;
}
tuple = dict_read_next(&dest_iter);
}
if (test_not_enough_storage || should_update_existing_keys_only) {
cl_assert(has_tuple[INT8_IDX] == false);
} else {
cl_assert(has_tuple[INT8_IDX] == true);
}
cl_assert(has_tuple[STRING_IDX] == true);
cl_assert(has_tuple[DATA_IDX] == true);
}
}

View file

@ -0,0 +1,209 @@
/*
* 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 "clar.h"
#include "services/normal/voice_endpoint_private.h"
#include "fake_pebble_tasks.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_rand_ptr.h"
// setup and teardown
void test_generic_attribute__initialize(void) {
}
void test_generic_attribute__cleanup(void) {
}
// tests
void test_generic_attribute__find_attribute(void) {
uint8_t data1[] = {
0x02, // attribute list - num attributes
0x02, // attribute type - transcription
0x2F, 0x00, // attribute length
// Transcription
0x01, // Transcription type
0x02, // Sentence count
// Sentence #1
0x02, 0x00, // Word count
// Word #1
85, // Confidence
0x05, 0x00, // Word length
'H', 'e', 'l', 'l', 'o',
// Word #2
74, // Confidence
0x08, 0x00, // Word length
'c', 'o', 'm', 'p', 'u', 't', 'e', 'r',
// Sentence #2
0x03, 0x00, // Word count
// Word #1
13, // Confidence
0x04, 0x00, // Word length
'h', 'e', 'l', 'l',
// Word #1
3, // Confidence
0x02, 0x00, // Word length
'o', 'h',
// Word #2
0, // Confidence
0x07, 0x00, // Word length
'c', 'o', 'm', 'p', 'u', 't', 'a',
0x03, // attribute type - App UUID
0x10, 0x00, // attribute length
0xa8, 0xc5, 0x63, 0x17, 0xa2, 0x89, 0x46, 0x5c,
0xbe, 0xf1, 0x5b, 0x98, 0x0d, 0xfd, 0xb0, 0x8a,
};
// same as data1, but with the attribute order swapped
uint8_t data2[] = {
0x02, // attribute list - num attributes
0x03, // attribute type - App UUID
0x10, 0x00, // attribute length
0xa8, 0xc5, 0x63, 0x17, 0xa2, 0x89, 0x46, 0x5c,
0xbe, 0xf1, 0x5b, 0x98, 0x0d, 0xfd, 0xb0, 0x8a,
0x02, // attribute type - transcription
0x2F, 0x00, // attribute length
// Transcription
0x01, // Transcription type
0x02, // Sentence count
// Sentence #1
0x02, 0x00, // Word count
// Word #1
85, // Confidence
0x05, 0x00, // Word length
'H', 'e', 'l', 'l', 'o',
// Word #2
74, // Confidence
0x08, 0x00, // Word length
'c', 'o', 'm', 'p', 'u', 't', 'e', 'r',
// Sentence #2
0x03, 0x00, // Word count
// Word #1
13, // Confidence
0x04, 0x00, // Word length
'h', 'e', 'l', 'l',
// Word #1
3, // Confidence
0x02, 0x00, // Word length
'o', 'h',
// Word #2
0, // Confidence
0x07, 0x00, // Word length
'c', 'o', 'm', 'p', 'u', 't', 'a',
};
GenericAttributeList *attr_list1 = (GenericAttributeList *)data1;
GenericAttributeList *attr_list2 = (GenericAttributeList *)data2;
GenericAttribute *attr1 = generic_attribute_find_attribute(attr_list1, VEAttributeIdTranscription,
sizeof(data1));
cl_assert(attr1);
cl_assert_equal_i(attr1->id, VEAttributeIdTranscription);
cl_assert_equal_i(attr1->length, 0x2F);
size_t offset = sizeof(GenericAttributeList) + sizeof(GenericAttribute);
cl_assert_equal_p(attr1->data, &data1[offset]);
GenericAttribute *attr2 = generic_attribute_find_attribute(attr_list1, VEAttributeIdAppUuid,
sizeof(data1));
cl_assert(attr2);
cl_assert_equal_i(attr2->id, VEAttributeIdAppUuid);
cl_assert_equal_i(attr2->length, 16);
offset = sizeof(GenericAttributeList) + sizeof(GenericAttribute) +
attr1->length + sizeof(GenericAttribute);
cl_assert_equal_p(attr2->data, &data1[offset]);
attr1 = generic_attribute_find_attribute(attr_list2, VEAttributeIdAppUuid, sizeof(data2));
cl_assert(attr1);
cl_assert_equal_i(attr1->id, VEAttributeIdAppUuid);
cl_assert_equal_i(attr1->length, 16);
offset = sizeof(GenericAttributeList) + sizeof(GenericAttribute);
cl_assert_equal_p(attr1->data, &data2[offset]);
attr2 = generic_attribute_find_attribute(attr_list2, VEAttributeIdTranscription, sizeof(data2));
cl_assert(attr2);
cl_assert_equal_i(attr2->id, VEAttributeIdTranscription);
cl_assert_equal_i(attr2->length, 0x2F);
offset = sizeof(GenericAttributeList) + sizeof(GenericAttribute) +
attr1->length + sizeof(GenericAttribute);
cl_assert_equal_p(attr2->data, &data2[offset]);
GenericAttribute *attr3 = generic_attribute_find_attribute(attr_list1, VEAttributeIdAppUuid,
sizeof(data1) - 1);
cl_assert(!attr3);
attr3 = generic_attribute_find_attribute(attr_list1, VEAttributeIdAppUuid,
sizeof(data1) - sizeof(Uuid));
cl_assert(!attr3);
attr3 = generic_attribute_find_attribute(attr_list1, VEAttributeIdAppUuid,
sizeof(data1) - sizeof(Uuid) - 1);
cl_assert(!attr3);
}
void test_generic_attribute__add_attribute(void) {
uint8_t data[] = {
0x01, 0x55, 0x77, 0x54, 0x47
};
uint8_t data_out[(2 * sizeof(GenericAttribute)) + sizeof(data) + sizeof(Uuid)];
GenericAttribute *next = (GenericAttribute *)data_out;
next = generic_attribute_add_attribute(next, VEAttributeIdTranscription, data, sizeof(data));
size_t offset = sizeof(GenericAttribute) + sizeof(data);
cl_assert_equal_p((uint8_t*)next, &data_out[offset]);
GenericAttribute expected = {
.id = VEAttributeIdTranscription,
.length = sizeof(data)
};
cl_assert_equal_m(&expected, data_out, sizeof(GenericAttribute));
cl_assert_equal_m(&data_out[sizeof(GenericAttribute)], data, sizeof(data));
Uuid uuid;
uuid_generate(&uuid);
next = generic_attribute_add_attribute(next, VEAttributeIdAppUuid, &uuid, sizeof(uuid));
cl_assert_equal_p((uint8_t*)next, data_out + sizeof(data_out));
expected = (GenericAttribute) {
.id = VEAttributeIdAppUuid,
.length = sizeof(Uuid)
};
cl_assert_equal_m(&expected, &data_out[offset], sizeof(GenericAttribute));
offset += sizeof(GenericAttribute);
cl_assert_equal_m(&uuid, &data_out[offset], sizeof(uuid));
}

View file

@ -0,0 +1,54 @@
/*
* 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 <clar.h>
#include "util/graphics.h"
// Make sure that row stride, bit depth is all being used correctly.
// Really, these are just some simple cases and sanity checks.
void test_graphics__raw_image_get_value_for_bitdepth(void) {
uint8_t test0[] = { 0b11000000 };
uint8_t test1[] = { 0, 0, 0, 0b11000000, 0 };
uint8_t test2[] = { 0b11000000, 0b00110000, 0b00001100, 0b00000011 };
cl_assert(raw_image_get_value_for_bitdepth(test0, 0, 0, 1, 1) == 1);
cl_assert(raw_image_get_value_for_bitdepth(test0, 7, 0, 1, 1) == 0);
cl_assert(raw_image_get_value_for_bitdepth(test0, 0, 0, 1, 2) == 3);
cl_assert(raw_image_get_value_for_bitdepth(test0, 0, 0, 1, 4) == 12);
cl_assert(raw_image_get_value_for_bitdepth(test0, 0, 0, 1, 8) == 192);
cl_assert(raw_image_get_value_for_bitdepth(test0, 0, 0, 1, 1) == 1);
cl_assert(raw_image_get_value_for_bitdepth(test0, 1, 0, 1, 1) == 1);
cl_assert(raw_image_get_value_for_bitdepth(test1, 0, 3, 1, 2) == 3);
cl_assert(raw_image_get_value_for_bitdepth(test1, 0, 3, 1, 1) == 1);
cl_assert(raw_image_get_value_for_bitdepth(test1, 0, 3, 1, 4) == 12);
cl_assert(raw_image_get_value_for_bitdepth(test1, 0, 3, 1, 8) == 192);
cl_assert(raw_image_get_value_for_bitdepth(test1, 4, 1, 2, 2) == 3);
cl_assert(raw_image_get_value_for_bitdepth(test2, 5, 2, 1, 1) == 1);
cl_assert(raw_image_get_value_for_bitdepth(test2, 6, 2, 1, 1) == 0);
cl_assert(raw_image_get_value_for_bitdepth(test2, 5, 0, 2, 2) == 3);
cl_assert(raw_image_get_value_for_bitdepth(test2, 5, 0, 2, 2) == 3);
cl_assert(raw_image_get_value_for_bitdepth(test2, 1, 1, 2, 8) == 3);
cl_assert(raw_image_get_value_for_bitdepth(test2, 0, 1, 3, 8) == 3);
cl_assert(raw_image_get_value_for_bitdepth(test2, 1, 1, 1, 2) == 3);
cl_assert(raw_image_get_value_for_bitdepth(test2, 0, 1, 1, 4) == 3);
cl_assert(raw_image_get_value_for_bitdepth(test2, 1, 1, 2, 4) == 12);
cl_assert(raw_image_get_value_for_bitdepth(test2, 0, 1, 1, 8) == 48);
cl_assert(raw_image_get_value_for_bitdepth(test2, 3, 0, 4, 8) == 3);
}

180
tests/fw/util/test_hdlc.c Normal file
View file

@ -0,0 +1,180 @@
/*
* 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 "clar.h"
#include "util/hdlc.h"
#include <string.h>
#include "stubs_passert.h"
// Setup
void test_hdlc__initialize(void) {
}
void test_hdlc__cleanup(void) {
}
// Tests
void test_hdlc__decode_no_special(void) {
// without any special characters
const char *str = "\x7eThis is a long string without any special characters to be escaped.\x7e";
int len = strlen(str);
HdlcStreamingContext ctx;
hdlc_streaming_decode_reset(&ctx);
for (int i = 0; i < len; i++) {
char c = str[i];
bool should_store, is_invalid;
bool is_complete = hdlc_streaming_decode(&ctx, (uint8_t *)&c, &should_store, &is_invalid);
cl_assert(is_invalid == false);
if (i == 0 || i == len - 1) {
cl_assert(is_complete == true);
cl_assert(should_store == false);
} else {
cl_assert(is_complete == false);
cl_assert(should_store == true);
cl_assert(c == str[i]);
}
}
}
void test_hdlc__special_characters(void) {
// make sure the escape characters haven't changed
cl_assert(HDLC_FLAG == 0x7e);
cl_assert(HDLC_ESCAPE == 0x7d);
cl_assert(HDLC_ESCAPE_MASK == 0x20);
}
void test_hdlc__decode_empty(void) {
// consecutive empty frames
const uint8_t str[4] = {HDLC_FLAG, HDLC_FLAG, HDLC_FLAG, HDLC_FLAG};
HdlcStreamingContext ctx;
hdlc_streaming_decode_reset(&ctx);
for (int i = 0; i < 4; i++) {
char c = str[i];
bool should_store, is_invalid;
bool is_complete = hdlc_streaming_decode(&ctx, (uint8_t *)&c, &should_store, &is_invalid);
cl_assert(is_complete == true);
cl_assert(should_store == false);
cl_assert(is_invalid == false);
}
}
void test_hdlc__decode_invalid(void) {
// invalid sequences
uint8_t data;
bool should_store, is_invalid, is_complete;
HdlcStreamingContext ctx;
// two consecutive escape characters
hdlc_streaming_decode_reset(&ctx);
data = HDLC_ESCAPE;
is_complete = hdlc_streaming_decode(&ctx, &data, &should_store, &is_invalid);
cl_assert(is_complete == false);
cl_assert(should_store == false);
cl_assert(is_invalid == false);
data = HDLC_ESCAPE;
is_complete = hdlc_streaming_decode(&ctx, &data, &should_store, &is_invalid);
cl_assert(is_complete == false);
cl_assert(should_store == false);
cl_assert(is_invalid == true);
// an escape character followed by a flag
hdlc_streaming_decode_reset(&ctx);
data = HDLC_ESCAPE;
is_complete = hdlc_streaming_decode(&ctx, &data, &should_store, &is_invalid);
cl_assert(is_complete == false);
cl_assert(should_store == false);
cl_assert(is_invalid == false);
data = HDLC_FLAG;
is_complete = hdlc_streaming_decode(&ctx, &data, &should_store, &is_invalid);
cl_assert(is_complete == true);
cl_assert(should_store == false);
cl_assert(is_invalid == true);
}
void test_hdlc__decode_escaped_special(void) {
// 2 escaped special characters
uint8_t data;
bool should_store, is_invalid, is_complete;
HdlcStreamingContext ctx;
hdlc_streaming_decode_reset(&ctx);
// escaped escape character
data = HDLC_ESCAPE;
is_complete = hdlc_streaming_decode(&ctx, &data, &should_store, &is_invalid);
cl_assert(is_complete == false);
cl_assert(should_store == false);
cl_assert(is_invalid == false);
data = HDLC_ESCAPE ^ HDLC_ESCAPE_MASK;
is_complete = hdlc_streaming_decode(&ctx, &data, &should_store, &is_invalid);
cl_assert(is_complete == false);
cl_assert(should_store == true);
cl_assert(is_invalid == false);
cl_assert(data == HDLC_ESCAPE);
// escaped flag
data = HDLC_ESCAPE;
is_complete = hdlc_streaming_decode(&ctx, &data, &should_store, &is_invalid);
cl_assert(is_complete == false);
cl_assert(should_store == false);
cl_assert(is_invalid == false);
data = HDLC_FLAG ^ HDLC_ESCAPE_MASK;
is_complete = hdlc_streaming_decode(&ctx, &data, &should_store, &is_invalid);
cl_assert(is_complete == false);
cl_assert(should_store == true);
cl_assert(is_invalid == false);
cl_assert(data == HDLC_FLAG);
}
void test_hdlc__encode_decode(void) {
const char *str = "this is a string with the special \x7e \x7d \x7e\x7d \x7d\x7e characters";
char buffer[100];
int write_idx = 0;
for (int i = 0; i < strlen(str); i++) {
char c = str[i];
if (hdlc_encode((uint8_t *)&c)) {
buffer[write_idx++] = HDLC_ESCAPE;
}
buffer[write_idx++] = c;
}
buffer[write_idx++] = HDLC_FLAG;
cl_assert(write_idx == strlen(str) + 7 /* 6 special characters to escape + 1 flag at end */);
HdlcStreamingContext ctx;
hdlc_streaming_decode_reset(&ctx);
int read_idx = 0;
int i = 0;
while (true) {
char c = buffer[i++];
bool should_store, is_invalid;
bool is_complete = hdlc_streaming_decode(&ctx, (uint8_t *)&c, &should_store, &is_invalid);
cl_assert(is_invalid == false);
if (should_store) {
cl_assert(is_complete == false);
cl_assert(c == str[read_idx++]);
}
if (is_complete) {
cl_assert(should_store == false);
cl_assert(i == write_idx);
break;
}
}
cl_assert(read_idx == strlen(str));
}

61
tests/fw/util/test_ihex.c Normal file
View file

@ -0,0 +1,61 @@
/*
* 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 "clar.h"
#include "util/ihex.h"
#include <string.h>
static uint8_t s_result[300];
void test_ihex__initialize(void) {
// Set the result buffer to a known value so that it can be checked
// later to see if the code under test wrote more than it should have.
memset(s_result, 0x20, sizeof(s_result));
}
static void prv_assert_ihex(const char *expected) {
int len = strlen(expected);
// Cehck that bytes aren't touched past the end of the record.
for (int i=len; i < sizeof(s_result); ++i) {
cl_assert_equal_i(0x20, s_result[i]);
}
// NULL-terminate the result so that it can be compared as a string.
s_result[len] = '\0';
cl_assert_equal_s(expected, (char *)s_result);
}
void test_ihex__eof_record(void) {
ihex_encode(s_result, IHEX_TYPE_EOF, 0, NULL, 0);
prv_assert_ihex(":00000001FF");
}
void test_ihex__data_record(void) {
uint8_t data[7] = { 1, 2, 3, 4, 5, 6, 7 };
ihex_encode(s_result, IHEX_TYPE_DATA, 0xABCD, data, sizeof(data));
prv_assert_ihex(":07ABCD000102030405060765");
}
void test_ihex__empty_record_length(void) {
cl_assert_equal_i(11, IHEX_RECORD_LENGTH(0));
}
void test_ihex__record_length(void) {
cl_assert_equal_i(15, IHEX_RECORD_LENGTH(2));
}

View file

@ -0,0 +1,115 @@
/*
* 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 "clar.h"
#include "util/legacy_checksum.h"
#include <inttypes.h>
#include <stdio.h>
LegacyChecksum cksum;
void test_legacy_checksum__initialize(void) {
memset(&cksum, 0xcc, sizeof cksum);
legacy_defective_checksum_init(&cksum);
}
static void update(const void * restrict data, size_t length) {
legacy_defective_checksum_update(&cksum, data, length);
}
#define assert_checksum(EXPECTED) \
do { \
uint32_t checksum = legacy_defective_checksum_finish(&cksum); \
uint32_t expected = (EXPECTED); \
if (checksum != expected) { \
char error_msg[256]; \
sprintf(error_msg, \
"%#08"PRIx32" != %#08"PRIx32"\n", \
expected, checksum); \
clar__assert(0, __FILE__, __LINE__, \
" expected != checksum", error_msg, 1); \
} \
} while (0)
void test_legacy_checksum__no_data(void) {
assert_checksum(0xffffffff);
}
void test_legacy_checksum__one_byte(void) {
update("A", 1);
assert_checksum(0xf743b0bb);
}
void test_legacy_checksum__standard(void) {
update("123456789", 9);
assert_checksum(0xaff19057);
}
void test_legacy_checksum__one_word(void) {
update("1234", 4);
assert_checksum(0xc2091428);
}
void test_legacy_checksum__repeated_byte(void) {
update("1111", 4);
assert_checksum(0x13cbc447);
}
void test_legacy_checksum__two_words(void) {
update("abcd", 4);
update("efgh", 4);
assert_checksum(0x18c4859c);
}
void test_legacy_checksum__split_word(void) {
update("123", 3);
update("4", 1);
assert_checksum(0xc2091428);
}
void test_legacy_checksum__finish_with_partial(void) {
update("1234", 4);
update("5", 1);
assert_checksum(0xec5baa37);
}
void test_legacy_checksum__start_with_partial(void) {
update("123", 3);
update("4567", 4);
update("8", 1);
assert_checksum(0xfefc54f9);
}
void test_legacy_checksum__start_and_finish_with_partial(void) {
update("12", 2);
update("3456", 4);
update("78", 2);
assert_checksum(0xfefc54f9);
}
void test_legacy_checksum__long_input(void) {
update("1234567890abcdefghijklmnopqrstuvwxyz", 36);
assert_checksum(0x586c447d);
}
void test_legacy_checksum__convenience_wrapper(void) {
uint32_t sum = legacy_defective_checksum_memory("12345", 5);
cl_assert_equal_i(sum, 0xec5baa37);
}

View file

@ -0,0 +1,125 @@
/*
* 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/lru_cache.h"
#include "clar.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
#include <string.h>
const uint8_t CACHE_BUFFER_SIZE = 80;
uint8_t s_buffer[CACHE_BUFFER_SIZE];
LRUCache s_cache;
void test_lru_cache__initialize(void) {
lru_cache_init(&s_cache, sizeof(uint32_t), s_buffer, CACHE_BUFFER_SIZE);
}
void test_lru_cache__cleanup(void) {
lru_cache_flush(&s_cache);
}
void test_lru_cache__one_put(void) {
uint32_t input = 0xdeadbeef;
lru_cache_put(&s_cache, 1, &input);
uint32_t *output = lru_cache_get(&s_cache, 1);
cl_assert(output);
cl_assert(*output == input);
}
void test_lru_cache__one_put_two_get(void) {
uint32_t input = 0xdeadbeef;
lru_cache_put(&s_cache, 1, &input);
uint32_t *output;
for (int i = 0; i < 2; i++) {
output = lru_cache_get(&s_cache, 1);
cl_assert(output);
cl_assert(*output == input);
}
}
void test_lru_cache__two_puts_one_get(void) {
uint32_t input = 0xdeadbeef;
lru_cache_put(&s_cache, 1, &input);
lru_cache_put(&s_cache, 1, &input);
uint32_t *output;
output = lru_cache_get(&s_cache, 1);
cl_assert(output);
cl_assert(*output == input);
}
void test_lru_cache__flush(void) {
uint32_t input = 0xdeadbeef;
lru_cache_put(&s_cache, 1, &input);
lru_cache_flush(&s_cache);
uint32_t *output = lru_cache_get(&s_cache, 1);
cl_assert(output == NULL);
}
void test_lru_cache__evict(void) {
for (int i = 0; i <= CACHE_BUFFER_SIZE / (sizeof(CacheEntry) + sizeof(uint32_t)); ++i) {
uint32_t input = i;
lru_cache_put(&s_cache, i, &input);
}
uint32_t *output = lru_cache_get(&s_cache, 0);
// check that the oldest entry got evicted
cl_assert(output == NULL);
for (int i = 1; i <= CACHE_BUFFER_SIZE / (sizeof(CacheEntry) + sizeof(uint32_t)); ++i) {
output = lru_cache_get(&s_cache, i);
// check that the others are still around
cl_assert(output);
cl_assert(*output == i);
}
}
void test_lru_cache__use_and_evict(void) {
int i;
for (i = 0; i < CACHE_BUFFER_SIZE / (sizeof(CacheEntry) + sizeof(uint32_t)); ++i) {
uint32_t input = i;
lru_cache_put(&s_cache, i, &input);
}
// use entry 0 to keep it around
uint32_t *output = lru_cache_get(&s_cache, 0);
cl_assert(output);
cl_assert(*output == 0);
// add one more entry
uint32_t input = i;
lru_cache_put(&s_cache, i, &input);
// check that entry 0 is around
output = lru_cache_get(&s_cache, 0);
cl_assert(output);
cl_assert(*output == 0);
// check that entry 1 got evicted
output = lru_cache_get(&s_cache, 1);
cl_assert(output == NULL);
// check that the others are still around
for (int i = 2; i <= CACHE_BUFFER_SIZE / (sizeof(CacheEntry) + sizeof(uint32_t)); ++i) {
output = lru_cache_get(&s_cache, i);
// check that the others are still around
cl_assert(output);
cl_assert(*output == i);
}
}

202
tests/fw/util/test_mbuf.c Normal file
View file

@ -0,0 +1,202 @@
/*
* 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 "clar.h"
#include "util/mbuf.h"
#include "util/mbuf_iterator.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_serial.h"
extern MBuf *s_free_list;
// Setup
void test_mbuf__initialize(void) {
}
void test_mbuf__cleanup(void) {
}
// Tests
void test_mbuf__length(void) {
// test the mbuf_get_length()/mbuf_get_chain_length() functions
char *data = __FILE_NAME__; // dummy data
char data_length = sizeof(__FILE_NAME__);
MBuf mbuf1 = MBUF_EMPTY;
MBuf mbuf2 = MBUF_EMPTY;
MBuf mbuf3 = MBUF_EMPTY;
// empty mbuf chain
cl_assert(mbuf_get_chain_length(NULL) == 0);
// single empty mbuf in the chain
mbuf1 = MBUF_EMPTY;
cl_assert(mbuf_get_length(&mbuf1) == 0);
cl_assert(mbuf_get_chain_length(&mbuf1) == 0);
// single mbuf of non-zero length
mbuf1 = MBUF_EMPTY;
mbuf_set_data(&mbuf1, data, data_length);
cl_assert(mbuf_get_length(&mbuf1) == data_length);
cl_assert(mbuf_get_chain_length(&mbuf1) == data_length);
// three mbufs of 0 length
mbuf1 = MBUF_EMPTY;
mbuf2 = MBUF_EMPTY;
mbuf3 = MBUF_EMPTY;
mbuf_append(&mbuf1, &mbuf2);
mbuf_append(&mbuf1, &mbuf3);
cl_assert(mbuf_get_length(&mbuf1) == 0);
cl_assert(mbuf_get_chain_length(&mbuf1) == 0);
// three mbufs of non-zero length in a chain
mbuf1 = MBUF_EMPTY;
mbuf2 = MBUF_EMPTY;
mbuf3 = MBUF_EMPTY;
mbuf_set_data(&mbuf1, data, data_length);
mbuf_set_data(&mbuf2, data, data_length);
mbuf_set_data(&mbuf3, data, data_length);
mbuf_append(&mbuf1, &mbuf2);
mbuf_append(&mbuf1, &mbuf3);
cl_assert(mbuf_get_length(&mbuf1) == data_length);
cl_assert(mbuf_get_length(&mbuf2) == data_length);
cl_assert(mbuf_get_length(&mbuf3) == data_length);
cl_assert(mbuf_get_chain_length(&mbuf1) == (3 * data_length));
// three mbufs with one of zero length
mbuf1 = MBUF_EMPTY;
mbuf2 = MBUF_EMPTY;
mbuf3 = MBUF_EMPTY;
mbuf_set_data(&mbuf1, data, data_length);
mbuf_set_data(&mbuf3, data, data_length);
mbuf_append(&mbuf1, &mbuf2);
mbuf_append(&mbuf1, &mbuf3);
cl_assert(mbuf_get_length(&mbuf1) == data_length);
cl_assert(mbuf_get_length(&mbuf2) == 0);
cl_assert(mbuf_get_length(&mbuf3) == data_length);
cl_assert(mbuf_get_chain_length(&mbuf1) == (2 * data_length));
}
void test_mbuf__iter_empty(void) {
// test iteratoring over empty mbuf chains
MBuf mbuf1 = MBUF_EMPTY;
MBuf mbuf2 = MBUF_EMPTY;
MBufIterator iter;
mbuf_append(&mbuf1, &mbuf2);
mbuf_iterator_init(&iter, NULL);
cl_assert(mbuf_iterator_is_finished(&iter));
mbuf_iterator_init(&iter, &mbuf2);
cl_assert(mbuf_iterator_is_finished(&iter));
mbuf_iterator_init(&iter, &mbuf1);
cl_assert(mbuf_iterator_is_finished(&iter));
uint8_t data;
cl_assert(!mbuf_iterator_read_byte(&iter, &data));
cl_assert(!mbuf_iterator_get_current_mbuf(&iter));
}
void test_mbuf__iter_modify(void) {
// modify (read and then write) the data in an mbuf chain using an mbuf iterator
// test reading from an mbuf chain via an mbuf iterator
uint8_t data1[] = {10, 11, 12};
uint8_t data3[] = {13, 14, 15};
MBufIterator write_iter, read_iter;
MBuf mbuf1 = MBUF_EMPTY;
MBuf mbuf2 = MBUF_EMPTY;
MBuf mbuf3 = MBUF_EMPTY;
mbuf_set_data(&mbuf1, data1, 3);
mbuf_set_data(&mbuf3, data3, 3);
mbuf_append(&mbuf1, &mbuf2);
mbuf_append(&mbuf1, &mbuf3);
mbuf_iterator_init(&write_iter, &mbuf1);
mbuf_iterator_init(&read_iter, &mbuf1);
for (int i = 0; i < 6; i++) {
cl_assert(!mbuf_iterator_is_finished(&write_iter));
cl_assert(!mbuf_iterator_is_finished(&read_iter));
// check we're on the exected mbuf
if (i < 3) {
cl_assert(mbuf_iterator_get_current_mbuf(&write_iter) == &mbuf1);
cl_assert(mbuf_iterator_get_current_mbuf(&read_iter) == &mbuf1);
} else {
cl_assert(mbuf_iterator_get_current_mbuf(&write_iter) == &mbuf3);
cl_assert(mbuf_iterator_get_current_mbuf(&read_iter) == &mbuf3);
}
uint8_t data_byte = 0;
bool have_byte = mbuf_iterator_read_byte(&read_iter, &data_byte);
cl_assert(have_byte);
// check that the data is what we expect
cl_assert(data_byte == (i + 10));
// modify the data by increasing the value by 10
cl_assert(mbuf_iterator_write_byte(&write_iter, data_byte + 10));
}
cl_assert(mbuf_iterator_is_finished(&write_iter));
cl_assert(mbuf_iterator_is_finished(&read_iter));
// verify the final value of the data
for (int i = 0; i < 6; i++) {
uint8_t *data;
int index;
if (i < 3) {
data = data1;
index = i;
} else {
data = data3;
index = i - 3;
}
cl_assert(data[index] == (i + 20));
}
}
static int prv_get_free_list_length(void) {
int len = 0;
for (MBuf *m = s_free_list; m; m = mbuf_get_next(m)) {
len++;
}
return len;
}
void test_mbuf__mbuf_pool(void) {
// get an MBuf and the pool should still be empty
MBuf *mbuf1 = mbuf_get(NULL, 0, MBufPoolUnitTest);
cl_assert(prv_get_free_list_length() == 0);
// free the mbuf and the pool should now contain it
mbuf_free(mbuf1);
cl_assert(prv_get_free_list_length() == 1);
cl_assert(s_free_list == mbuf1);
// get another mbuf and expect that it's the same one and the pool is empty
MBuf *mbuf2 = mbuf_get(NULL, 0, MBufPoolUnitTest);
cl_assert(mbuf2 == mbuf1);
cl_assert(prv_get_free_list_length() == 0);
// get another mbuf and expect that it's not the same as the previous one
MBuf *mbuf3 = mbuf_get(NULL, 0, MBufPoolUnitTest);
cl_assert(mbuf3 != mbuf2);
cl_assert(prv_get_free_list_length() == 0);
// free both of the mbufs (one at a time)
mbuf_free(mbuf2);
cl_assert(prv_get_free_list_length() == 1);
mbuf_free(mbuf3);
cl_assert(prv_get_free_list_length() == 2);
}

View file

@ -0,0 +1,89 @@
/*
* 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 <math.h>
#include <time.h>
#include <clar.h>
// Tests
///////////////////////////////////////////////////////////
void test_mktime__bithdays(void) {
struct tm francois_birthday = {
.tm_sec = 0,
.tm_min = 44,
.tm_hour = 10,
.tm_mday = 30,
.tm_mon = 4,
.tm_year = 89,
};
cl_assert_equal_i(mktime(&francois_birthday), 612528240);
struct tm rons_birthday = {
.tm_sec = 17,
.tm_min = 1,
.tm_hour = 9,
.tm_mday = 10,
.tm_mon = 4,
.tm_year = 63,
};
cl_assert_equal_i(mktime(&rons_birthday), -1);
struct tm alex_marianetti_birthday = {
.tm_sec = 29,
.tm_min = 4,
.tm_hour = 17,
.tm_mday = 2,
.tm_mon = 9,
.tm_year = 107,
};
cl_assert_equal_i(mktime(&alex_marianetti_birthday), 1191344669);
struct tm chris_birthday = {
.tm_sec = 59,
.tm_min = 3,
.tm_hour = 10,
.tm_mday = 15,
.tm_mon = 5,
.tm_year = 89,
};
cl_assert_equal_i(mktime(&chris_birthday), 613908239);
}
void test_mktime__epoch(void) {
struct tm epoch = {
.tm_sec = 0,
.tm_min = 0,
.tm_hour = 0,
.tm_mday = 1,
.tm_mon = 0,
.tm_year = 70,
};
cl_assert_equal_i(mktime(&epoch), 0);
}
void test_mktime__leap(void) {
struct tm real_leap = {
.tm_sec = 0,
.tm_min = 0,
.tm_hour = 10,
.tm_mday = 29,
.tm_mon = 1,
.tm_year = 112,
};
cl_assert_equal_i(mktime(&real_leap), 1330509600);
}

View file

@ -0,0 +1,81 @@
/*
* 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 "clar.h"
#include "util/pstring.h"
#include "stubs_pbl_malloc.h"
#include "stubs_logging.h"
void test_pstring__initialize(void) {
}
void test_pstring__cleanup(void) {
}
void test_pstring__equal(void) {
const char *ps1_str = "Phil";
uint8_t ps1_buf[128];
PascalString16 *ps1 = (PascalString16 *)&ps1_buf;
ps1->str_length = strlen(ps1_str);
memcpy(ps1->str_value, ps1_str, strlen(ps1_str));
const char *ps2_str = "Four";
uint8_t ps2_buf[128];
PascalString16 *ps2 = (PascalString16 *)&ps2_buf;
ps2->str_length = strlen(ps2_str);
memcpy(ps2->str_value, ps2_str, strlen(ps2_str));
const char *ps3_str = "PhilG";
uint8_t ps3_buf[128];
PascalString16 *ps3 = (PascalString16 *)&ps3_buf;
ps3->str_length = strlen(ps3_str);
memcpy(ps3->str_value, ps3_str, strlen(ps3_str));
const char *ps4_str = "Phil";
uint8_t ps4_buf[128];
PascalString16 *ps4 = (PascalString16 *)&ps4_buf;
ps4->str_length = strlen(ps4_str);
memcpy(ps4->str_value, ps4_str, strlen(ps4_str));
cl_assert(pstring_equal(ps1, ps4));
cl_assert(!pstring_equal(ps1, ps2));
cl_assert(!pstring_equal(ps1, ps3));
cl_assert(!pstring_equal(ps2, ps3));
cl_assert(!pstring_equal(ps1, NULL));
cl_assert(!pstring_equal(NULL, NULL));
}
void test_pstring__equal_cstring(void) {
const char *str1 = "Phil";
uint8_t ps1_buf[128];
PascalString16 *ps1 = (PascalString16 *)&ps1_buf;
ps1->str_length = strlen(str1);
memcpy(ps1->str_value, str1, strlen(str1));
const char *str2 = "PhilG";
cl_assert(pstring_equal_cstring(ps1, str1));
cl_assert(!pstring_equal_cstring(ps1, str2));
cl_assert(!pstring_equal_cstring(ps1, NULL));
cl_assert(!pstring_equal_cstring(NULL, NULL));
}

49
tests/fw/util/test_rand.c Normal file
View file

@ -0,0 +1,49 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <clar.h>
#include "kernel/pebble_tasks.h"
#include "util/rand.h"
// Stubs
///////////////////////////////////////////////////////////
#include "stubs_passert.h"
#include "stubs_rand_ptr.h"
PebbleTask pebble_task_get_current(void) {
return PebbleTask_KernelMain; // System seed
}
// Tests
///////////////////////////////////////////////////////////
void test_rand__smoke_test(void) {
#define RANDOM_CHECK_LENGTH 512
uint32_t values[RANDOM_CHECK_LENGTH];
for(size_t i = 0; i < RANDOM_CHECK_LENGTH; i++) {
values[i] = rand32();
for(size_t l = 0; l < i; l++) {
printf("i,l: %zu,%zu\n", i,l);
cl_assert(values[i] != values[l]);
}
}
}

View file

@ -0,0 +1,373 @@
/*
* 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/shared_circular_buffer.h"
#include "clar.h"
#include <string.h>
#include "stubs_passert.h"
// Stubs
///////////////////////////////////////////////////////////
int g_pbl_log_level = 0;
void pbl_log(int level, const char* src_filename, int src_line_number, const char* fmt, ...) { }
void test_shared_circular_buffer__initialize(void) {
}
void test_shared_circular_buffer__cleanup(void) {
}
static void prv_read_and_consume(SharedCircularBuffer *buffer, SharedCircularBufferClient *client,
uint8_t *data, uint32_t num_bytes) {
while (num_bytes) {
uint16_t chunk;
const uint8_t *read_ptr;
cl_assert(shared_circular_buffer_read(buffer, client, num_bytes, &read_ptr, &chunk));
memcpy(data, read_ptr, chunk);
cl_assert(shared_circular_buffer_consume(buffer, client, chunk));
buffer += chunk;
num_bytes -= chunk;
}
}
void test_shared_circular_buffer__one_client(void) {
SharedCircularBuffer buffer;
uint8_t storage[9];
shared_circular_buffer_init(&buffer, storage, sizeof(storage));
const uint8_t* out_buffer;
uint16_t out_length;
// Add a client
SharedCircularBufferClient client = (SharedCircularBufferClient) {};
shared_circular_buffer_add_client(&buffer, &client);
// We should start out empty
cl_assert(!shared_circular_buffer_read(&buffer, &client, 1, &out_buffer, &out_length));
cl_assert(shared_circular_buffer_write(&buffer, (uint8_t*) "123", 3, false));
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 5);
cl_assert(shared_circular_buffer_write(&buffer, (uint8_t*) "456", 3, false));
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 2);
cl_assert(!shared_circular_buffer_write(&buffer, (uint8_t*) "789", 3, false)); // too big
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 2);
cl_assert(shared_circular_buffer_read(&buffer, &client, 4, &out_buffer, &out_length));
cl_assert_equal_i(out_length, 4);
cl_assert(memcmp(out_buffer, "1234", 4) == 0);
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 2);
cl_assert(shared_circular_buffer_consume(&buffer, &client, 4));
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 6);
// Now there's just 56 in the buffer. Fill it to the brim
cl_assert(shared_circular_buffer_write(&buffer, (uint8_t*) "789", 3, false));
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 3);
cl_assert(shared_circular_buffer_write(&buffer, (uint8_t*) "abc", 3, false));
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 0);
cl_assert(!shared_circular_buffer_write(&buffer, (uint8_t*) "d", 1, false)); // too full
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 0);
// Try a wrapped read
cl_assert(shared_circular_buffer_read(&buffer, &client, 6, &out_buffer, &out_length));
cl_assert_equal_i(out_length, 5);
cl_assert(memcmp(out_buffer, (uint8_t*) "56789", 5) == 0);
cl_assert(shared_circular_buffer_consume(&buffer, &client, 5));
// Get the rest of the wrapped read
cl_assert(shared_circular_buffer_read(&buffer, &client, 1, &out_buffer, &out_length));
cl_assert_equal_i(out_length, 1);
cl_assert(memcmp(out_buffer, (uint8_t*) "a", 1) == 0);
cl_assert(shared_circular_buffer_consume(&buffer, &client, 1));
// Consume one without reading it
cl_assert(shared_circular_buffer_consume(&buffer, &client, 1));
// Read the last little bit
cl_assert(shared_circular_buffer_read(&buffer, &client, 1, &out_buffer, &out_length));
cl_assert_equal_i(out_length, 1);
cl_assert(memcmp(out_buffer, (uint8_t*) "c", 1) == 0);
cl_assert(shared_circular_buffer_consume(&buffer, &client, 1));
// And we should be empty
cl_assert(!shared_circular_buffer_read(&buffer, &client, 1, &out_buffer, &out_length));
cl_assert(!shared_circular_buffer_consume(&buffer, &client, 1));
}
void test_shared_circular_buffer__two_clients(void) {
SharedCircularBuffer buffer;
uint8_t storage[9];
shared_circular_buffer_init(&buffer, storage, sizeof(storage));
const uint8_t* out_buffer;
uint16_t out_length;
// Add clients
SharedCircularBufferClient client1 = (SharedCircularBufferClient) {};
shared_circular_buffer_add_client(&buffer, &client1);
SharedCircularBufferClient client2 = (SharedCircularBufferClient) {};
shared_circular_buffer_add_client(&buffer, &client2);
// We should start out empty
cl_assert(!shared_circular_buffer_read(&buffer, &client1, 1, &out_buffer, &out_length));
cl_assert(!shared_circular_buffer_read(&buffer, &client2, 1, &out_buffer, &out_length));
// Fill with data
cl_assert(shared_circular_buffer_write(&buffer, (uint8_t*) "123456", 6, false));
// Read different amounts from each client
cl_assert(shared_circular_buffer_read(&buffer, &client1, 4, &out_buffer, &out_length));
cl_assert(shared_circular_buffer_consume(&buffer, &client1, 4));
cl_assert(memcmp(out_buffer, "1234", 4) == 0);
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 2);
cl_assert(shared_circular_buffer_read(&buffer, &client2, 4, &out_buffer, &out_length));
cl_assert(shared_circular_buffer_consume(&buffer, &client2, 4));
cl_assert(memcmp(out_buffer, "1234", 4) == 0);
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 6);
// Make client2 fall behind
cl_assert(shared_circular_buffer_write(&buffer, (uint8_t*) "abcdef", 6, false));
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 0);
cl_assert(shared_circular_buffer_read(&buffer, &client1, 3, &out_buffer, &out_length));
cl_assert_equal_i(out_length, 3);
cl_assert(memcmp(out_buffer, "56a", 3) == 0);
cl_assert(shared_circular_buffer_consume(&buffer, &client1, 3));
cl_assert(shared_circular_buffer_read(&buffer, &client1, 2, &out_buffer, &out_length));
cl_assert_equal_i(out_length, 2);
cl_assert(memcmp(out_buffer, "bc", 2) == 0);
cl_assert(shared_circular_buffer_consume(&buffer, &client1, 2));
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 0);
// Should fail, not enough room because client 2 is full
cl_assert(!shared_circular_buffer_write(&buffer, (uint8_t*) "gh", 2, false));
// This should pass and reset client 2's read index
cl_assert(shared_circular_buffer_write(&buffer, (uint8_t*) "gh", 2, true));
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 3);
cl_assert_equal_i(shared_circular_buffer_get_read_space_remaining(&buffer, &client1), 5);
cl_assert_equal_i(shared_circular_buffer_get_read_space_remaining(&buffer, &client2), 2);
// Make client2 fall behind again
cl_assert(shared_circular_buffer_write(&buffer, (uint8_t*) "abc", 3, false));
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 0);
cl_assert(shared_circular_buffer_read(&buffer, &client1, 3, &out_buffer, &out_length));
cl_assert_equal_i(out_length, 3);
cl_assert(memcmp(out_buffer, "def", 3) == 0);
cl_assert(shared_circular_buffer_consume(&buffer, &client1, 3));
cl_assert(shared_circular_buffer_read(&buffer, &client1, 2, &out_buffer, &out_length));
cl_assert_equal_i(out_length, 2);
cl_assert(memcmp(out_buffer, "gh", 2) == 0);
cl_assert(shared_circular_buffer_consume(&buffer, &client1, 2));
cl_assert_equal_i(shared_circular_buffer_get_read_space_remaining(&buffer, &client1), 3);
cl_assert_equal_i(shared_circular_buffer_get_read_space_remaining(&buffer, &client2), 5);
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 3);
// If we remove client2, it should create more space
shared_circular_buffer_remove_client(&buffer, &client2);
cl_assert_equal_i(shared_circular_buffer_get_write_space_remaining(&buffer), 5);
}
void test_shared_circular_buffer__corner_case(void) {
SharedCircularBuffer buffer;
uint8_t storage[4];
shared_circular_buffer_init(&buffer, storage, sizeof(storage));
// Add a client
SharedCircularBufferClient client = (SharedCircularBufferClient) {};
shared_circular_buffer_add_client(&buffer, &client);
// We should start out empty
cl_assert_equal_i(shared_circular_buffer_get_read_space_remaining(&buffer, &client), 0);
// Write 2
cl_assert(shared_circular_buffer_write(&buffer, storage, 2, false));
cl_assert_equal_i(shared_circular_buffer_get_read_space_remaining(&buffer, &client), 2);
// Consume it
prv_read_and_consume(&buffer, &client, storage, 2);
cl_assert_equal_i(shared_circular_buffer_get_read_space_remaining(&buffer, &client), 0);
// Write 2 more
cl_assert(shared_circular_buffer_write(&buffer, storage, 2, false));
cl_assert_equal_i(shared_circular_buffer_get_read_space_remaining(&buffer, &client), 2);
// Consume it
prv_read_and_consume(&buffer, &client, storage, 2);
cl_assert_equal_i(shared_circular_buffer_get_read_space_remaining(&buffer, &client), 0);
}
void test_shared_circular_buffer__subsampling_2of5(void) {
SharedCircularBuffer buffer;
uint16_t item_size = 2;
uint8_t storage[12*item_size];
uint8_t out_buffer[12*item_size];
uint16_t items_read;
shared_circular_buffer_init(&buffer, storage, sizeof(storage));
SubsampledSharedCircularBufferClient client = {};
shared_circular_buffer_add_subsampled_client(&buffer, &client, 2, 5);
cl_assert(shared_circular_buffer_write(
&buffer, (uint8_t*)"0a1b2c3d4e5f6g7h8i", 9*item_size, false));
items_read = shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 100);
cl_assert_equal_i(items_read, 4);
cl_assert_equal_m(out_buffer, "0a3d5f8i", 8);
cl_assert(shared_circular_buffer_write(
&buffer, (uint8_t*)"9j0k1m2n3o4p5q", 7*item_size, false));
items_read = shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 2);
cl_assert_equal_i(items_read, 2);
cl_assert_equal_m(out_buffer, "0k3o", 4);
items_read = shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 2);
cl_assert_equal_i(items_read, 1);
cl_assert_equal_m(out_buffer, "5q", 2);
}
void test_shared_circular_buffer__subsampling_1of3(void) {
SharedCircularBuffer buffer;
uint16_t item_size = 2;
uint8_t storage[12*item_size];
uint8_t out_buffer[12*item_size];
uint16_t items_read;
// Init
shared_circular_buffer_init(&buffer, storage, sizeof(storage));
SubsampledSharedCircularBufferClient client = {};
shared_circular_buffer_add_subsampled_client(&buffer, &client, 1, 3);
cl_assert(shared_circular_buffer_write(
&buffer, (uint8_t*)"0a1b2c3d4e5f6g7h8i9j", 10*item_size, false));
items_read = shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 100);
cl_assert_equal_i(items_read, 4);
cl_assert_equal_m(out_buffer, "0a3d6g9j", 8);
cl_assert(shared_circular_buffer_write(
&buffer, (uint8_t*)"0k1m2n", 3*item_size, false));
items_read = shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 100);
cl_assert_equal_i(items_read, 1);
cl_assert_equal_m(out_buffer, "2n", 2);
}
void test_shared_circular_buffer__subsampling_1of1(void) {
SharedCircularBuffer buffer;
uint16_t item_size = 2;
uint8_t storage[12*item_size];
uint8_t out_buffer[12*item_size];
uint16_t items_read;
// Init
shared_circular_buffer_init(&buffer, storage, sizeof(storage));
SubsampledSharedCircularBufferClient client = {};
shared_circular_buffer_add_subsampled_client(&buffer, &client, 3, 3);
// No subsampling
cl_assert(shared_circular_buffer_write(
&buffer, (uint8_t*)"0a1b2c3d4e5f6g7h8i9j", 10*item_size, false));
items_read = shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 9);
cl_assert_equal_i(items_read, 9);
cl_assert_equal_m(out_buffer, "0a1b2c3d4e5f6g7h8i", 18);
cl_assert(shared_circular_buffer_write(
&buffer, (uint8_t*)"0k1m", 2*item_size, false));
items_read = shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 100);
cl_assert_equal_i(items_read, 3);
cl_assert_equal_m(out_buffer, "9j0k1m", 6);
}
void test_shared_circular_buffer__subsampling_variable_ratio(void) {
SharedCircularBuffer buffer;
uint16_t item_size = 2;
uint8_t storage[12*item_size];
uint8_t out_buffer[12*item_size];
shared_circular_buffer_init(&buffer, storage, sizeof(storage));
SubsampledSharedCircularBufferClient client = {};
shared_circular_buffer_add_subsampled_client(&buffer, &client, 1, 2);
cl_assert(shared_circular_buffer_write(
&buffer, (uint8_t*)"0a1b2c3d4e5f6g7h8i9j", 10*item_size, false));
// Consume "0a1b2c3d4e"
cl_assert_equal_i(shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 3), 3);
cl_assert_equal_m(out_buffer, "0a2c4e", 6);
subsampled_shared_circular_buffer_client_set_ratio(&client, 2, 3);
// Consume "5f6g7h8i"
cl_assert_equal_i(shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 3), 3);
// Normally the next read would skip 5f, but changing the ratio resets the
// subsampling state and the first sample after resetting the state is never
// skipped.
cl_assert_equal_m(out_buffer, "5f7h8i", 6);
}
void test_shared_circular_buffer__subsampling_set_ratio_is_idempotent(void) {
SharedCircularBuffer buffer;
uint16_t item_size = 2;
uint8_t storage[12*item_size];
uint8_t out_buffer[12*item_size];
shared_circular_buffer_init(&buffer, storage, sizeof(storage));
SubsampledSharedCircularBufferClient client = {};
shared_circular_buffer_add_subsampled_client(&buffer, &client, 1, 2);
cl_assert(shared_circular_buffer_write(
&buffer, (uint8_t*)"0a1b2c3d4e5f6g7h8i9j", 10*item_size, false));
// Consume "0a1b2c3d4e"
cl_assert_equal_i(shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 3), 3);
cl_assert_equal_m(out_buffer, "0a2c4e", 6);
// This should be a no-op. the "5f" sample should still be skipped on the next
// read.
subsampled_shared_circular_buffer_client_set_ratio(&client, 1, 2);
cl_assert_equal_i(shared_circular_buffer_read_subsampled(
&buffer, &client, item_size, out_buffer, 1), 1);
cl_assert_equal_m(out_buffer, "6g", 2);
}

95
tests/fw/util/test_sle.c Normal file
View file

@ -0,0 +1,95 @@
/*
* 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 "clar.h"
#include "util/sle.h"
#include "stubs/stubs_passert.h"
void test_sle__simple(void) {
SLEDecodeContext ctx;
uint8_t buf[] = {
0xfd, // escape code
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0xfd, 0x00 // end
};
sle_decode_init(&ctx, buf);
uint8_t byte;
uint8_t expect = 0x00;
uint32_t count = 0;
while (sle_decode(&ctx, &byte)) {
cl_assert_equal_i(byte, expect++);
++count;
}
cl_assert_equal_i(count, 16);
}
void test_sle__short_zeros(void) {
SLEDecodeContext ctx;
uint8_t buf[] = {
0xfd, // escape code
0xfd, 0x5, // 5 zeroes
0xfd, 0x00 // end
};
sle_decode_init(&ctx, buf);
uint8_t byte;
uint32_t count = 0;
while (sle_decode(&ctx, &byte)) {
cl_assert_equal_i(byte, 0x0);
++count;
}
cl_assert_equal_i(count, 5);
}
void test_sle__long_zeros(void) {
SLEDecodeContext ctx;
uint8_t buf[] = {
0xfd, // escape code
0xfd, 0xff, 0xaa, // 32810 zeroes
0xfd, 0x00 // end
};
sle_decode_init(&ctx, buf);
uint8_t byte;
uint32_t count = 0;
while (sle_decode(&ctx, &byte)) {
cl_assert_equal_i(byte, 0x0);
++count;
}
cl_assert_equal_i(count, 32810);
}
void test_sle__escape_byte(void) {
SLEDecodeContext ctx;
uint8_t buf[] = {
0xfd, // escape code
0xfd, 0x01, // literal escape byte
0xfd, 0x00 // end
};
sle_decode_init(&ctx, buf);
uint8_t byte;
uint32_t count = 0;
while (sle_decode(&ctx, &byte)) {
cl_assert_equal_i(byte, 0xfd);
++count;
}
cl_assert_equal_i(count, 1);
}

358
tests/fw/util/test_stats.c Normal file
View file

@ -0,0 +1,358 @@
/*
* 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/size.h"
#include "util/stats.h"
#include "clar.h"
#include <stdio.h>
#include "stubs_pbl_malloc.h"
void test_stats__initialize(void) {
}
void test_stats__cleanup(void) {
}
void test_stats__min(void) {
const int32_t data[] = { 10, 40, 6, 32, 73, 80, 34, 25, 62 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op = StatsBasicOp_Min;
int32_t result;
stats_calculate_basic(op, data, num_data, NULL, NULL, &result);
cl_assert_equal_i(result, 6);
}
void test_stats__max(void) {
const int32_t data[] = { 10, 40, 6, 32, 73, 80, 34, 25, 62 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op = StatsBasicOp_Max;
int32_t result;
stats_calculate_basic(op, data, num_data, NULL, NULL, &result);
cl_assert_equal_i(result, 80);
}
void test_stats__avg(void) {
const int32_t data[] = { 10, 40, 6, 32, 73, 80, 34, 25, 62 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op = StatsBasicOp_Average;
int32_t result;
stats_calculate_basic(op, data, num_data, NULL, NULL, &result);
cl_assert_equal_i(result, 40);
}
void test_stats__sum(void) {
const int32_t data[] = { 10, 40, 6, 32, 73, 80, 34, 25, 62 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op = StatsBasicOp_Sum;
int32_t result;
stats_calculate_basic(op, data, num_data, NULL, NULL, &result);
cl_assert_equal_i(result, 362);
}
static void *s_context = NULL;
static bool prv_filter(int index, int32_t value, void *context) {
cl_assert_equal_p(context, &s_context);
return (value > 0);
}
void test_stats__filtered_count(void) {
const int32_t data[] = { 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op = StatsBasicOp_Count;
int32_t result;
stats_calculate_basic(op, data, num_data, prv_filter, &s_context, &result);
cl_assert_equal_i(result, 14);
}
void test_stats__filtered_consecutive(void) {
const int32_t data[] = { 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op = StatsBasicOp_Consecutive;
int32_t result;
stats_calculate_basic(op, data, num_data, prv_filter, &s_context, &result);
cl_assert_equal_i(result, 5);
}
void test_stats__filtered_consecutive_first(void) {
const int32_t data[] = { 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op = StatsBasicOp_ConsecutiveFirst;
int32_t result;
stats_calculate_basic(op, data, num_data, prv_filter, &s_context, &result);
cl_assert_equal_i(result, 3);
}
void test_stats__median(void) {
const int32_t data[] = { 10, 40, 6, 32, 73, 80, 34, 25, 62 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op = StatsBasicOp_Median;
int32_t result;
stats_calculate_basic(op, data, num_data, NULL, NULL, &result);
cl_assert_equal_i(result, 34);
}
void test_stats__all_basic_ops(void) {
const int32_t data[] = { 10, 0, 40, 6, 0, -5, 0, 32, 73, 0, 80, 34, 25, 62, 0 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op =
(StatsBasicOp_Sum | StatsBasicOp_Average | StatsBasicOp_Min | StatsBasicOp_Max |
StatsBasicOp_Count | StatsBasicOp_Consecutive | StatsBasicOp_ConsecutiveFirst |
StatsBasicOp_Median);
struct {
int32_t sum;
int32_t avg;
int32_t min;
int32_t max;
int32_t count;
int32_t max_streak;
int32_t first_streak;
int32_t median;
} result;
stats_calculate_basic(op, data, num_data, NULL, NULL, &result.sum);
cl_assert_equal_i(result.sum, 357);
cl_assert_equal_i(result.avg, 23);
cl_assert_equal_i(result.min, -5);
cl_assert_equal_i(result.max, 80);
cl_assert_equal_i(result.count, num_data);
cl_assert_equal_i(result.max_streak, num_data);
cl_assert_equal_i(result.first_streak, num_data);
cl_assert_equal_i(result.median, 10);
}
void test_stats__all_basic_ops_filtered(void) {
const int32_t data[] = { 10, 0, 40, 6, 0, 0, 0, 32, 73, 0, 80, 34, 25, 62, 0 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op =
(StatsBasicOp_Sum | StatsBasicOp_Average | StatsBasicOp_Min | StatsBasicOp_Max |
StatsBasicOp_Count | StatsBasicOp_Consecutive | StatsBasicOp_ConsecutiveFirst |
StatsBasicOp_Median);
struct {
int32_t sum;
int32_t avg;
int32_t min;
int32_t max;
int32_t count;
int32_t max_streak;
int32_t first_streak;
int32_t median;
} result;
stats_calculate_basic(op, data, num_data, prv_filter, &s_context, &result.sum);
cl_assert_equal_i(result.sum, 362);
cl_assert_equal_i(result.avg, 40);
cl_assert_equal_i(result.min, 6);
cl_assert_equal_i(result.max, 80);
cl_assert_equal_i(result.count, 9);
cl_assert_equal_i(result.max_streak, 4);
cl_assert_equal_i(result.first_streak, 1);
cl_assert_equal_i(result.median, 34);
}
void test_stats__all_basic_ops_filtered_out(void) {
const int32_t data[] = { 0, 0, 0, 0, 0 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op =
(StatsBasicOp_Sum | StatsBasicOp_Average | StatsBasicOp_Min | StatsBasicOp_Max |
StatsBasicOp_Count | StatsBasicOp_Consecutive | StatsBasicOp_ConsecutiveFirst |
StatsBasicOp_Median);
struct {
int32_t sum;
int32_t avg;
int32_t min;
int32_t max;
int32_t count;
int32_t max_streak;
int32_t first_streak;
int32_t median;
} result;
stats_calculate_basic(op, data, num_data, prv_filter, &s_context, &result.sum);
cl_assert_equal_i(result.sum, 0);
cl_assert_equal_i(result.avg, 0);
cl_assert_equal_i(result.min, INT32_MAX);
cl_assert_equal_i(result.max, INT32_MIN);
cl_assert_equal_i(result.count, 0);
cl_assert_equal_i(result.max_streak, 0);
cl_assert_equal_i(result.first_streak, 0);
cl_assert_equal_i(result.median, 0);
}
void test_stats__all_basic_one_value(void) {
const int32_t data[] = { 42 };
const size_t num_data = ARRAY_LENGTH(data);
const StatsBasicOp op =
(StatsBasicOp_Sum | StatsBasicOp_Average | StatsBasicOp_Min | StatsBasicOp_Max |
StatsBasicOp_Count | StatsBasicOp_Consecutive | StatsBasicOp_ConsecutiveFirst |
StatsBasicOp_Median);
struct {
int32_t sum;
int32_t avg;
int32_t min;
int32_t max;
int32_t count;
int32_t max_streak;
int32_t first_streak;
int32_t median;
} result;
stats_calculate_basic(op, data, num_data, NULL, NULL, &result.sum);
cl_assert_equal_i(result.sum, 42);
cl_assert_equal_i(result.avg, 42);
cl_assert_equal_i(result.min, 42);
cl_assert_equal_i(result.max, 42);
cl_assert_equal_i(result.count, 1);
cl_assert_equal_i(result.max_streak, 1);
cl_assert_equal_i(result.first_streak, 1);
cl_assert_equal_i(result.median, 42);
}
void test_stats__all_basic_no_values(void) {
const int32_t data[] = {};
const size_t num_data = 0;
const StatsBasicOp op =
(StatsBasicOp_Sum | StatsBasicOp_Average | StatsBasicOp_Min | StatsBasicOp_Max |
StatsBasicOp_Count | StatsBasicOp_Consecutive | StatsBasicOp_ConsecutiveFirst |
StatsBasicOp_Median);
struct {
int32_t sum;
int32_t avg;
int32_t min;
int32_t max;
int32_t count;
int32_t max_streak;
int32_t first_streak;
int32_t median;
} result;
stats_calculate_basic(op, data, num_data, NULL, NULL, &result.sum);
cl_assert_equal_i(result.sum, 0);
cl_assert_equal_i(result.avg, 0);
cl_assert_equal_i(result.min, INT32_MAX);
cl_assert_equal_i(result.max, INT32_MIN);
cl_assert_equal_i(result.count, 0);
cl_assert_equal_i(result.max_streak, 0);
cl_assert_equal_i(result.first_streak, 0);
cl_assert_equal_i(result.median, 0);
}
void test_stats__null_data(void) {
const StatsBasicOp op = StatsBasicOp_Average;
int32_t result = 0x73110;
stats_calculate_basic(op, NULL, 0, NULL, NULL, &result);
cl_assert_equal_i(result, 0x73110);
}
typedef struct WeightedValue {
int32_t value;
int32_t weight_x100;
} WeightedValue;
void test_stats__weighted_median(void) {
//! Taken from https://en.wikipedia.org/wiki/Weighted_median
struct {
int32_t values[10];
int32_t weights[10];
int32_t num_values;
int32_t answer;
} test_cases[] = {
{
// Simple test case
.values = {1, 3, 1},
.weights = {2, 4, 1},
.num_values = 3,
.answer = 3,
},
{
// Hit exactly S/2 when iterating. Take the mean of [1,2] and [3,4] -> 2
.values = {1, 3, 1},
.weights = {2, 4, 2},
.num_values = 3,
.answer = 2,
},
{
// Would hit exactly S/2 when iterating if we only did integer division. Added a check to
// prevent this.
.values = {1, 3, 1},
.weights = {2, 4, 3},
.num_values = 3,
.answer = 1,
},
{
// Simple test case
.values = {1, 100},
.weights = {2, 1},
.num_values = 2,
.answer = 1,
},
{
// Simple test case
.values = {100, 1},
.weights = {1, 2},
.num_values = 2,
.answer = 1,
},
{
// Simple test case
.values = {100, 1},
.weights = {2, 1},
.num_values = 2,
.answer = 100,
},
{
// Simple test case
.values = {20, 3, 6},
.weights = {1, 50, 50},
.num_values = 3,
.answer = 6,
},
{
// Test if all weights are zero, zero should be returned
.values = {20, 3, 6},
.weights = {0, 0, 0},
.num_values = 3,
.answer = 0,
},
{
// Simple test case
.values = {10, 35, 5, 10, 15, 5, 20},
.weights = {20, 70, 10, 20, 30, 10, 40},
.num_values = 7,
.answer = 20,
},
{
// Only one value, return that value
.values = {1},
.weights = {100},
.num_values = 1,
.answer = 1,
},
{
// Two values, equal weight. Return the lower of the two
.values = {1, 2},
.weights = {1, 1},
.num_values = 2,
.answer = 1,
},
};
for (size_t i = 0; i < ARRAY_LENGTH(test_cases); i++) {
int32_t w_median = stats_calculate_weighted_median(test_cases[i].values,
test_cases[i].weights,
test_cases[i].num_values);
printf("W_Median test case: %d\n", (int)i);
cl_assert_equal_i(test_cases[i].answer, w_median);
}
}

View file

@ -0,0 +1,144 @@
/*
* 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/stringlist.h"
#include "clar.h"
// Setup
////////////////////////////////////////////////////////////////
void test_stringlist__initialize(void) {
}
void test_stringlist__cleanup(void) {
}
// Tests
////////////////////////////////////////////////////////////////
void test_stringlist__test(void) {
StringList *list = malloc(20);
memset(list, 0, 20);
// no data
list->serialized_byte_length = 0;
cl_assert_equal_i(0, string_list_count(list));
list->serialized_byte_length = 3;
// 4 empty strings
cl_assert_equal_i(4, string_list_count(list));
cl_assert_equal_s("", string_list_get_at(list, 0));
cl_assert_equal_s("", string_list_get_at(list, 1));
cl_assert_equal_s("", string_list_get_at(list, 2));
cl_assert_equal_s("", string_list_get_at(list, 3));
// non-null-terminated string is treated as one string - this is the standard case
// please note that the string will only be terminated if there's another \0 following
// when deserializing the data, the deserializer will append the needed \0
list->serialized_byte_length = 3;
list->data[0] = 'a';
list->data[1] = 'b';
list->data[2] = 'c'; // end of data
list->data[3] = 'd';
list->data[4] = '\0';
cl_assert_equal_i(1, string_list_count(list));
cl_assert_equal_s("abcd", string_list_get_at(list, 0));
// 1 string (null terminated) => 2 strings, last is empty
list->serialized_byte_length = 3;
list->data[0] = 'a';
list->data[1] = 'b';
list->data[2] = '\0'; // end of data
list->data[3] = '\0';
cl_assert_equal_i(2, string_list_count(list));
cl_assert_equal_s("ab", string_list_get_at(list, 0));
cl_assert_equal_s("", string_list_get_at(list, 1));
// 2 strings (non-null terminated) - this is the standard case
list->serialized_byte_length = 4;
list->data[0] = 'a';
list->data[1] = 'b';
list->data[2] = '\0';
list->data[3] = 'c'; // end of data
list->data[4] = '\0';
cl_assert_equal_i(2, string_list_count(list));
cl_assert_equal_s("ab", string_list_get_at(list, 0));
cl_assert_equal_s("c", string_list_get_at(list, 1));
// 3 strings (last two are is empty)
list->serialized_byte_length = 4;
list->data[0] = 'a';
list->data[1] = 'b';
list->data[2] = '\0';
list->data[3] = '\0'; // end of data
list->data[4] = '\0';
cl_assert_equal_i(3, string_list_count(list));
cl_assert_equal_s("ab", string_list_get_at(list, 0));
cl_assert_equal_s("", string_list_get_at(list, 1));
cl_assert_equal_s("", string_list_get_at(list, 2));
cl_assert_equal_s(NULL, string_list_get_at(list, 3));
// 4 strings (first and last two are empty)
list->serialized_byte_length = 4;
list->data[0] = '\0';
list->data[1] = 'b';
list->data[2] = '\0';
list->data[3] = '\0'; // end of data
list->data[4] = '\0';
cl_assert_equal_i(4, string_list_count(list));
cl_assert_equal_s("", string_list_get_at(list, 0));
cl_assert_equal_s("b", string_list_get_at(list, 1));
cl_assert_equal_s("", string_list_get_at(list, 2));
cl_assert_equal_s("", string_list_get_at(list, 3));
// 2 strings (last is not terminated and will fall through) will return 2 strings
// when deserializing, the deserializer puts a \0 at the end
// this case demonstrates the problem with incorrectly initialized data
list->serialized_byte_length = 3;
list->data[0] = 'a';
list->data[1] = '\0';
list->data[2] = 'b'; // end of data
list->data[3] = 'c';
list->data[4] = '\0';
cl_assert_equal_i(2, string_list_count(list));
cl_assert_equal_s("a", string_list_get_at(list, 0));
cl_assert_equal_s("bc", string_list_get_at(list, 1));
// add a string to an empty string list
list->serialized_byte_length = 0;
string_list_add_string(list, 20, "hello", 10);
cl_assert_equal_i(5, list->serialized_byte_length);
cl_assert_equal_i(1, string_list_count(list));
cl_assert_equal_s("hello", string_list_get_at(list, 0));
// add a string to string list with strings
string_list_add_string(list, 20, "world", 10);
cl_assert_equal_i(11, list->serialized_byte_length);
cl_assert_equal_i(2, string_list_count(list));
cl_assert_equal_s("world", string_list_get_at(list, 1));
// truncated because of the max string size
string_list_add_string(list, 20, "foobar", 3);
cl_assert_equal_i(15, list->serialized_byte_length);
cl_assert_equal_i(3, string_list_count(list));
cl_assert_equal_s("foo", string_list_get_at(list, 2));
// truncated because of the max list size
string_list_add_string(list, 20, "abc", 10);
cl_assert_equal_i(17, list->serialized_byte_length);
cl_assert_equal_i(4, string_list_count(list));
cl_assert_equal_s("a", string_list_get_at(list, 3));
}

82
tests/fw/util/test_time.c Normal file
View file

@ -0,0 +1,82 @@
/*
* 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/time/time.h"
#include <clar.h>
// Fakes
///////////////////////////////////////////////////////////
#include "../../fakes/fake_rtc.h"
// Overrides
///////////////////////////////////////////////////////////
int16_t clock_get_timezone_region_id(void) {
return rtc_get_timezone_id();
}
void clock_set_timezone_by_region_id(uint16_t region_id) {
return;
}
// Tests
///////////////////////////////////////////////////////////
void test_time__initialize(void) {
}
void test_time__cleanup(void) {
}
void test_time__serial_distance32(void) {
uint32_t day, hour, minute, second;
{
time_util_split_seconds_into_parts(1, &day, &hour, &minute, &second);
cl_assert_equal_i(day, 0);
cl_assert_equal_i(hour, 0);
cl_assert_equal_i(minute, 0);
cl_assert_equal_i(second, 1);
}
{
time_util_split_seconds_into_parts(61, &day, &hour, &minute, &second);
cl_assert_equal_i(day, 0);
cl_assert_equal_i(hour, 0);
cl_assert_equal_i(minute, 1);
cl_assert_equal_i(second, 1);
}
{
second = 1;
time_util_split_seconds_into_parts((3 * (24 * 60 * 60)), &day, &hour, &minute, &second);
cl_assert_equal_i(day, 3);
cl_assert_equal_i(hour, 0);
cl_assert_equal_i(minute, 0);
cl_assert_equal_i(second, 0);
}
{
time_util_split_seconds_into_parts((3 * (24 * 60 * 60)) + (2 * (60 * 60)) + (4 * 60) + 5, &day, &hour, &minute, &second);
cl_assert_equal_i(day, 3);
cl_assert_equal_i(hour, 2);
cl_assert_equal_i(minute, 4);
cl_assert_equal_i(second, 5);
}
}

77
tests/fw/util/wscript Normal file
View file

@ -0,0 +1,77 @@
from waftools.pebble_test import clar
def build(ctx):
clar(ctx,
sources_ant_glob = "src/fw/util/base64.c",
test_sources_ant_glob = "test_base64.c")
clar(ctx,
sources_ant_glob = "src/fw/util/shared_circular_buffer.c",
test_sources_ant_glob = "test_shared_circular_buffer.c")
clar(ctx,
sources_ant_glob = " src/fw/util/time/time.c" \
" src/fw/util/time/mktime.c" \
" tests/fakes/fake_rtc.c",
test_sources_ant_glob = "test_time.c")
clar(ctx,
sources_ant_glob = "src/fw/util/dict.c",
test_sources_ant_glob = "test_dict.c")
#clar(ctx,
# sources_ant_glob = "src/fw/util/mktime.c",
# test_sources_ant_glob = "test_mktime.c")
clar(ctx,
sources_ant_glob = "src/fw/util/buffer.c",
test_sources_ant_glob = "test_buffer.c")
clar(ctx,
sources_ant_glob = "src/fw/util/lru_cache.c",
test_sources_ant_glob = "test_lru_cache.c")
clar(ctx,
sources_ant_glob = "src/fw/util/mbuf.c src/fw/util/mbuf_iterator.c",
test_sources_ant_glob = "test_mbuf.c")
clar(ctx,
sources_ant_glob = "src/fw/util/rand/rand.c" \
" src/fw/vendor/tinymt32/tinymt32.c",
test_sources_ant_glob = "test_rand.c")
clar(ctx,
sources_ant_glob = "src/fw/util/stats.c",
test_sources_ant_glob = "test_stats.c")
clar(ctx,
sources_ant_glob='src/fw/util/legacy_checksum.c',
test_sources_ant_glob='test_legacy_checksum.c')
clar(ctx,
sources_ant_glob = " src/fw/util/rand/rand.c" \
" src/fw/util/generic_attribute.c" \
" src/fw/vendor/tinymt32/tinymt32.c",
test_sources_ant_glob = "test_generic_attribute.c")
clar(ctx,
sources_ant_glob = "src/fw/util/hdlc.c",
test_sources_ant_glob = "test_hdlc.c")
clar(ctx,
sources_ant_glob = "src/fw/util/stringlist.c",
test_sources_ant_glob = "test_stringlist.c")
clar(ctx,
sources_ant_glob = "src/fw/util/pstring.c",
test_sources_ant_glob = "test_pstring.c")
clar(ctx,
sources_ant_glob = "src/fw/util/ihex.c",
test_sources_ant_glob = "test_ihex.c")
clar(ctx,
sources_ant_glob = "src/fw/util/sle.c",
test_sources_ant_glob = "test_sle.c")
# vim:filetype=python