mirror of
https://github.com/google/pebble.git
synced 2025-07-04 22:00:38 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
363
tests/fakes/fake_GAPAPI.c
Normal file
363
tests/fakes/fake_GAPAPI.c
Normal file
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* 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 "fake_GAPAPI.h"
|
||||
|
||||
#include "bluetopia_interface.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
static bool s_is_le_advertising_enabled;
|
||||
|
||||
static GAP_LE_Event_Callback_t s_le_adv_connection_event_callback;
|
||||
static unsigned long s_le_adv_connection_callback_param;
|
||||
|
||||
static uint16_t s_min_advertising_interval_slots;
|
||||
static uint16_t s_max_advertising_interval_slots;
|
||||
|
||||
void gap_le_set_advertising_disabled(void) {
|
||||
s_is_le_advertising_enabled = false;
|
||||
s_min_advertising_interval_slots = 0;
|
||||
s_max_advertising_interval_slots = 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Advertising_Disable(unsigned int BluetoothStackID) {
|
||||
s_is_le_advertising_enabled = false;
|
||||
s_le_adv_connection_event_callback = NULL;
|
||||
s_le_adv_connection_callback_param = 0;
|
||||
s_min_advertising_interval_slots = 0;
|
||||
s_max_advertising_interval_slots = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Advertising_Enable(unsigned int BluetoothStackID,
|
||||
Boolean_t EnableScanResponse,
|
||||
GAP_LE_Advertising_Parameters_t *GAP_LE_Advertising_Parameters,
|
||||
GAP_LE_Connectability_Parameters_t *GAP_LE_Connectability_Parameters,
|
||||
GAP_LE_Event_Callback_t GAP_LE_Event_Callback,
|
||||
unsigned long CallbackParameter) {
|
||||
s_is_le_advertising_enabled = true;
|
||||
s_le_adv_connection_event_callback = GAP_LE_Event_Callback;
|
||||
s_le_adv_connection_callback_param = CallbackParameter;
|
||||
if (GAP_LE_Advertising_Parameters) {
|
||||
// Convert from ms to slots:
|
||||
s_min_advertising_interval_slots =
|
||||
(GAP_LE_Advertising_Parameters->Advertising_Interval_Min * 16) / 10;
|
||||
s_max_advertising_interval_slots =
|
||||
(GAP_LE_Advertising_Parameters->Advertising_Interval_Max * 16) / 10;
|
||||
} else {
|
||||
s_min_advertising_interval_slots = 0;
|
||||
s_max_advertising_interval_slots = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gap_le_assert_advertising_interval(uint16_t expected_min_slots, uint16_t expected_max_slots) {
|
||||
cl_assert_equal_i(s_min_advertising_interval_slots, expected_min_slots);
|
||||
cl_assert_equal_i(s_max_advertising_interval_slots, expected_max_slots);
|
||||
}
|
||||
|
||||
bool gap_le_is_advertising_enabled(void) {
|
||||
return s_is_le_advertising_enabled;
|
||||
}
|
||||
|
||||
static Advertising_Data_t s_ad_data;
|
||||
static unsigned int s_ad_data_length;
|
||||
|
||||
int GAP_LE_Set_Advertising_Data(unsigned int BluetoothStackID,
|
||||
unsigned int Length,
|
||||
Advertising_Data_t *Advertising_Data) {
|
||||
memcpy(&s_ad_data, Advertising_Data, Length);
|
||||
s_ad_data_length = Length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int gap_le_get_advertising_data(Advertising_Data_t *ad_data_out) {
|
||||
*ad_data_out = s_ad_data;
|
||||
return s_ad_data_length;
|
||||
}
|
||||
|
||||
static Scan_Response_Data_t s_scan_resp_data;
|
||||
static unsigned int s_scan_resp_data_length;
|
||||
|
||||
int GAP_LE_Set_Scan_Response_Data(unsigned int BluetoothStackID,
|
||||
unsigned int Length,
|
||||
Scan_Response_Data_t *Scan_Response_Data) {
|
||||
memcpy(&s_scan_resp_data, Scan_Response_Data, Length);
|
||||
s_scan_resp_data_length = Length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int gap_le_get_scan_response_data(Scan_Response_Data_t *scan_resp_data_out) {
|
||||
*scan_resp_data_out = s_scan_resp_data;
|
||||
return s_scan_resp_data_length;
|
||||
}
|
||||
|
||||
static GAP_LE_Event_Callback_t s_le_create_connection_event_callback;
|
||||
static unsigned long s_le_create_connection_callback_param;
|
||||
|
||||
int GAP_LE_Create_Connection(unsigned int BluetoothStackID,
|
||||
unsigned int ScanInterval,
|
||||
unsigned int ScanWindow,
|
||||
GAP_LE_Filter_Policy_t InitatorFilterPolicy,
|
||||
GAP_LE_Address_Type_t RemoteAddressType,
|
||||
BD_ADDR_t *RemoteDevice,
|
||||
GAP_LE_Address_Type_t LocalAddressType,
|
||||
GAP_LE_Connection_Parameters_t *ConnectionParameters,
|
||||
GAP_LE_Event_Callback_t GAP_LE_Event_Callback,
|
||||
unsigned long CallbackParameter) {
|
||||
|
||||
s_le_create_connection_event_callback = GAP_LE_Event_Callback;
|
||||
s_le_create_connection_callback_param = CallbackParameter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void prv_fake_gap_le_create_connection_event_put(GAP_LE_Event_Data_t *event) {
|
||||
cl_assert(s_le_create_connection_event_callback != NULL);
|
||||
s_le_create_connection_event_callback(1, event, s_le_create_connection_callback_param);
|
||||
}
|
||||
|
||||
void prv_fake_gap_le_adv_connection_event_put(GAP_LE_Event_Data_t *event) {
|
||||
cl_assert(s_le_adv_connection_event_callback != NULL);
|
||||
s_le_adv_connection_event_callback(1, event, s_le_adv_connection_callback_param);
|
||||
}
|
||||
|
||||
void fake_gap_put_connection_event(uint8_t status,
|
||||
bool is_master,
|
||||
const BTDeviceInternal *device) {
|
||||
GAP_LE_Connection_Complete_Event_Data_t event_data =
|
||||
(GAP_LE_Connection_Complete_Event_Data_t) {
|
||||
.Status = status,
|
||||
.Master = is_master,
|
||||
.Peer_Address_Type = device->is_random_address ? latRandom : latPublic,
|
||||
.Peer_Address = BTDeviceAddressToBDADDR(device->address),
|
||||
};
|
||||
GAP_LE_Event_Data_t event = (GAP_LE_Event_Data_t) {
|
||||
.Event_Data_Type = etLE_Connection_Complete,
|
||||
.Event_Data_Size = sizeof(GAP_LE_Connection_Complete_Event_Data_t),
|
||||
.Event_Data.GAP_LE_Connection_Complete_Event_Data = &event_data,
|
||||
};
|
||||
if (is_master) {
|
||||
prv_fake_gap_le_create_connection_event_put(&event);
|
||||
} else {
|
||||
prv_fake_gap_le_adv_connection_event_put(&event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fake_gap_put_disconnection_event(uint8_t status, uint8_t reason,
|
||||
bool is_master,
|
||||
const BTDeviceInternal *device) {
|
||||
GAP_LE_Disconnection_Complete_Event_Data_t event_data =
|
||||
(GAP_LE_Disconnection_Complete_Event_Data_t) {
|
||||
.Status = status,
|
||||
.Reason = reason,
|
||||
.Peer_Address_Type = device->is_random_address ? latRandom : latPublic,
|
||||
.Peer_Address = BTDeviceAddressToBDADDR(device->address),
|
||||
};
|
||||
GAP_LE_Event_Data_t event = (GAP_LE_Event_Data_t) {
|
||||
.Event_Data_Type = etLE_Disconnection_Complete,
|
||||
.Event_Data_Size = sizeof(GAP_LE_Disconnection_Complete_Event_Data_t),
|
||||
.Event_Data.GAP_LE_Disconnection_Complete_Event_Data = &event_data,
|
||||
};
|
||||
if (is_master) {
|
||||
prv_fake_gap_le_create_connection_event_put(&event);
|
||||
} else {
|
||||
prv_fake_gap_le_adv_connection_event_put(&event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fake_GAPAPI_put_encryption_change_event(bool encrypted, uint8_t status, bool is_master,
|
||||
const BTDeviceInternal *device) {
|
||||
GAP_LE_Encryption_Change_Event_Data_t event_data =
|
||||
(GAP_LE_Encryption_Change_Event_Data_t) {
|
||||
.BD_ADDR = BTDeviceAddressToBDADDR(device->address),
|
||||
.Encryption_Change_Status = status,
|
||||
.Encryption_Mode = encrypted ? emEnabled : emDisabled,
|
||||
};
|
||||
GAP_LE_Event_Data_t event = (GAP_LE_Event_Data_t) {
|
||||
.Event_Data_Type = etLE_Encryption_Change,
|
||||
.Event_Data_Size = sizeof(GAP_LE_Encryption_Change_Event_Data_t),
|
||||
.Event_Data.GAP_LE_Encryption_Change_Event_Data = &event_data,
|
||||
};
|
||||
if (is_master) {
|
||||
prv_fake_gap_le_create_connection_event_put(&event);
|
||||
} else {
|
||||
prv_fake_gap_le_adv_connection_event_put(&event);
|
||||
}
|
||||
}
|
||||
|
||||
int GAP_LE_Cancel_Create_Connection(unsigned int BluetoothStackID) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Puts the event that the BT Controller will emit after a succesfull
|
||||
// GAP_LE_Cancel_Create_Connection call.
|
||||
void fake_gap_le_put_cancel_create_event(const BTDeviceInternal *device, bool is_master) {
|
||||
fake_gap_put_connection_event(HCI_ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER,
|
||||
is_master,
|
||||
device);
|
||||
}
|
||||
|
||||
int GAP_LE_Disconnect(unsigned int BluetoothStackID,
|
||||
BD_ADDR_t BD_ADDR) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Pair_Remote_Device(unsigned int BluetoothStackID,
|
||||
BD_ADDR_t BD_ADDR,
|
||||
GAP_LE_Pairing_Capabilities_t *Capabilities,
|
||||
GAP_LE_Event_Callback_t GAP_LE_Event_Callback,
|
||||
unsigned long CallbackParameter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// Bluetopia's Security Manager API
|
||||
|
||||
int GAP_LE_Authentication_Response(unsigned int BluetoothStackID, BD_ADDR_t BD_ADDR,
|
||||
GAP_LE_Authentication_Response_Information_t *GAP_LE_Authentication_Information) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Diversify_Function(unsigned int BluetoothStackID, Encryption_Key_t *Key, Word_t DIn,
|
||||
Word_t RIn, Encryption_Key_t *Result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Generate_Long_Term_Key(unsigned int BluetoothStackID, Encryption_Key_t *DHK,
|
||||
Encryption_Key_t *ER, Long_Term_Key_t *LTK_Result,
|
||||
Word_t *DIV_Result, Word_t *EDIV_Result,
|
||||
Random_Number_t *Rand_Result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BD_ADDR_t s_encrypted_device;
|
||||
|
||||
int GAP_LE_Query_Encryption_Mode(unsigned int BluetoothStackID, BD_ADDR_t BD_ADDR,
|
||||
GAP_Encryption_Mode_t *GAP_Encryption_Mode) {
|
||||
*GAP_Encryption_Mode = (COMPARE_BD_ADDR(s_encrypted_device, BD_ADDR)) ? emEnabled : emDisabled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fake_GAPAPI_set_encrypted_for_device(const BTDeviceInternal *device) {
|
||||
s_encrypted_device = BTDeviceAddressToBDADDR(device->address);
|
||||
}
|
||||
|
||||
int GAP_LE_Regenerate_Long_Term_Key(unsigned int BluetoothStackID, Encryption_Key_t *DHK,
|
||||
Encryption_Key_t *ER, Word_t EDIV, Random_Number_t *Rand,
|
||||
Long_Term_Key_t *LTK_Result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Register_Remote_Authentication(unsigned int BluetoothStackID, GAP_LE_Event_Callback_t GAP_LE_Event_Callback, unsigned long CallbackParameter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Un_Register_Remote_Authentication(unsigned int BluetoothStackID) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Request_Security(unsigned int BluetoothStackID, BD_ADDR_t BD_ADDR,
|
||||
GAP_LE_Bonding_Type_t Bonding_Type, Boolean_t MITM,
|
||||
GAP_LE_Event_Callback_t GAP_LE_Event_Callback,
|
||||
unsigned long CallbackParameter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Set_Pairability_Mode(unsigned int BluetoothStackID,
|
||||
GAP_LE_Pairability_Mode_t PairableMode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Generate_Resolvable_Address(unsigned int BluetoothStackID, Encryption_Key_t *IRK,
|
||||
BD_ADDR_t *ResolvableAddress_Result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_LE_Set_Random_Address(unsigned int BluetoothStackID, BD_ADDR_t RandomAddress) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GAP_Query_Local_BD_ADDR(unsigned int BluetoothStackID, BD_ADDR_t *BD_ADDR) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const Encryption_Key_t s_fake_irk = {
|
||||
0xaa,
|
||||
0xaa,
|
||||
};
|
||||
|
||||
static const BD_ADDR_t s_resolving_bd_addr = {
|
||||
0xaa, 0xff, 0xff, 0xff, 0xff,
|
||||
0x7f /* 6th byte: bit 6 set, bit 7 unset indicates "resolvable private address" */
|
||||
};
|
||||
|
||||
static const BD_ADDR_t s_not_resolving_bd_addr = {
|
||||
0xff,
|
||||
};
|
||||
|
||||
const Encryption_Key_t *fake_GAPAPI_get_fake_irk(void) {
|
||||
return &s_fake_irk;
|
||||
}
|
||||
|
||||
const BD_ADDR_t *fake_GAPAPI_get_bd_addr_not_resolving_to_fake_irk(void) {
|
||||
return &s_not_resolving_bd_addr;
|
||||
}
|
||||
|
||||
const BTDeviceInternal *fake_GAPAPI_get_device_not_resolving_to_fake_irk(void) {
|
||||
static BTDeviceInternal s_not_resolving_device;
|
||||
s_not_resolving_device = (const BTDeviceInternal) {
|
||||
.address = BDADDRToBTDeviceAddress(s_not_resolving_bd_addr),
|
||||
.is_random_address = true,
|
||||
};
|
||||
return &s_not_resolving_device;
|
||||
}
|
||||
|
||||
const BD_ADDR_t *fake_GAPAPI_get_bd_addr_resolving_to_fake_irk(void) {
|
||||
return &s_resolving_bd_addr;
|
||||
}
|
||||
|
||||
const BTDeviceInternal *fake_GAPAPI_get_device_resolving_to_fake_irk(void) {
|
||||
static BTDeviceInternal s_resolving_device;
|
||||
s_resolving_device = (const BTDeviceInternal) {
|
||||
.address = BDADDRToBTDeviceAddress(s_resolving_bd_addr),
|
||||
.is_random_address = true,
|
||||
};
|
||||
return &s_resolving_device;
|
||||
}
|
||||
|
||||
Boolean_t GAP_LE_Resolve_Address(unsigned int BluetoothStackID, Encryption_Key_t *IRK,
|
||||
BD_ADDR_t ResolvableAddress) {
|
||||
return COMPARE_BD_ADDR(ResolvableAddress, s_resolving_bd_addr) &&
|
||||
COMPARE_ENCRYPTION_KEY(*IRK, s_fake_irk);
|
||||
}
|
||||
|
||||
void fake_GAPAPI_init(void) {
|
||||
memset(&s_encrypted_device, 0, sizeof(s_encrypted_device));
|
||||
s_is_le_advertising_enabled = false;
|
||||
s_le_adv_connection_event_callback = NULL;
|
||||
s_le_adv_connection_callback_param = 0;
|
||||
memset(&s_ad_data, 0, sizeof(s_ad_data));
|
||||
s_ad_data_length = 0;
|
||||
s_le_create_connection_event_callback = NULL;
|
||||
s_le_create_connection_callback_param = 0;
|
||||
memset(&s_scan_resp_data, 0, sizeof(s_scan_resp_data));
|
||||
s_scan_resp_data_length = 0;
|
||||
}
|
58
tests/fakes/fake_GAPAPI.h
Normal file
58
tests/fakes/fake_GAPAPI.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GAPAPI.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//! Provided to simulate stopping advertising because of an inbound connection.
|
||||
void gap_le_set_advertising_disabled(void);
|
||||
|
||||
bool gap_le_is_advertising_enabled(void);
|
||||
|
||||
void gap_le_assert_advertising_interval(uint16_t expected_min_slots, uint16_t expected_max_slots);
|
||||
|
||||
unsigned int gap_le_get_advertising_data(Advertising_Data_t *ad_data_out);
|
||||
unsigned int gap_le_get_scan_response_data(Scan_Response_Data_t *scan_resp_data_out);
|
||||
|
||||
void fake_gap_put_connection_event(uint8_t status, bool is_master, const BTDeviceInternal *device);
|
||||
|
||||
void fake_gap_put_disconnection_event(uint8_t status, uint8_t reason, bool is_master,
|
||||
const BTDeviceInternal *device);
|
||||
|
||||
void fake_GAPAPI_put_encryption_change_event(bool encrypted, uint8_t status, bool is_master,
|
||||
const BTDeviceInternal *device);
|
||||
|
||||
void fake_gap_le_put_cancel_create_event(const BTDeviceInternal *device, bool is_master);
|
||||
|
||||
void fake_GAPAPI_set_encrypted_for_device(const BTDeviceInternal *device);
|
||||
|
||||
const Encryption_Key_t *fake_GAPAPI_get_fake_irk(void);
|
||||
|
||||
const BD_ADDR_t *fake_GAPAPI_get_bd_addr_not_resolving_to_fake_irk(void);
|
||||
|
||||
const BTDeviceInternal *fake_GAPAPI_get_device_not_resolving_to_fake_irk(void);
|
||||
|
||||
const BD_ADDR_t *fake_GAPAPI_get_bd_addr_resolving_to_fake_irk(void);
|
||||
|
||||
const BTDeviceInternal *fake_GAPAPI_get_device_resolving_to_fake_irk(void);
|
||||
|
||||
void fake_GAPAPI_init(void);
|
191
tests/fakes/fake_GATTAPI.c
Normal file
191
tests/fakes/fake_GATTAPI.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* 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 "fake_GATTAPI.h"
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static GATT_Connection_Event_Callback_t s_connection_event_callback;
|
||||
static unsigned int s_stack_id;
|
||||
static unsigned long s_connection_callback_param;
|
||||
|
||||
static int s_start_count;
|
||||
static int s_stop_count;
|
||||
|
||||
static int s_start_ret_val;
|
||||
static int s_stop_ret_val;
|
||||
|
||||
static int s_service_changed_indication_count;
|
||||
|
||||
struct FakeGATTServiceDiscoveryContext {
|
||||
bool is_running;
|
||||
unsigned int stack_id;
|
||||
unsigned int connection_id;
|
||||
unsigned int num_of_uuids;
|
||||
GATT_UUID_t *uuids;
|
||||
GATT_Service_Discovery_Event_Callback_t callback;
|
||||
unsigned long callback_param;
|
||||
} s_service_discovery_ctx;
|
||||
|
||||
int GATT_Initialize(unsigned int BluetoothStackID,
|
||||
unsigned long Flags,
|
||||
GATT_Connection_Event_Callback_t ConnectionEventCallback,
|
||||
unsigned long CallbackParameter) {
|
||||
s_stack_id = BluetoothStackID;
|
||||
s_connection_event_callback = ConnectionEventCallback;
|
||||
s_connection_callback_param = CallbackParameter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GATT_Cleanup(unsigned int BluetoothStackID) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GATT_Start_Service_Discovery_Handle_Range(unsigned int stack_id,
|
||||
unsigned int connection_id,
|
||||
GATT_Attribute_Handle_Group_t *DiscoveryHandleRange,
|
||||
unsigned int NumberOfUUID,
|
||||
GATT_UUID_t *UUIDList,
|
||||
GATT_Service_Discovery_Event_Callback_t ServiceDiscoveryCallback,
|
||||
unsigned long CallbackParameter) {
|
||||
s_service_discovery_ctx = (struct FakeGATTServiceDiscoveryContext) {
|
||||
.is_running = true,
|
||||
.stack_id = stack_id,
|
||||
.connection_id = connection_id,
|
||||
.num_of_uuids = NumberOfUUID,
|
||||
.uuids = UUIDList,
|
||||
.callback = ServiceDiscoveryCallback,
|
||||
.callback_param = CallbackParameter
|
||||
};
|
||||
++s_start_count;
|
||||
return s_start_ret_val;
|
||||
}
|
||||
|
||||
int GATT_Stop_Service_Discovery(unsigned int BluetoothStackID, unsigned int ConnectionID) {
|
||||
s_service_discovery_ctx.is_running = false;
|
||||
++s_stop_count;
|
||||
return s_stop_ret_val;
|
||||
}
|
||||
|
||||
bool fake_gatt_is_service_discovery_running(void) {
|
||||
return s_service_discovery_ctx.is_running;
|
||||
}
|
||||
|
||||
int fake_gatt_is_service_discovery_start_count(void) {
|
||||
return s_start_count;
|
||||
}
|
||||
|
||||
int fake_gatt_is_service_discovery_stop_count(void) {
|
||||
return s_stop_count;
|
||||
}
|
||||
|
||||
void fake_gatt_set_start_return_value(int ret_value) {
|
||||
s_start_ret_val = ret_value;
|
||||
}
|
||||
|
||||
void fake_gatt_set_stop_return_value(int ret_value) {
|
||||
s_stop_ret_val = ret_value;
|
||||
}
|
||||
|
||||
void fake_gatt_put_service_discovery_event(GATT_Service_Discovery_Event_Data_t *event) {
|
||||
cl_assert_equal_b(s_service_discovery_ctx.is_running, true);
|
||||
if (event->Event_Data_Type == etGATT_Service_Discovery_Complete) {
|
||||
s_service_discovery_ctx.is_running = false;
|
||||
}
|
||||
s_service_discovery_ctx.callback(s_service_discovery_ctx.stack_id,
|
||||
event,
|
||||
s_service_discovery_ctx.callback_param);
|
||||
}
|
||||
|
||||
void fake_gatt_init(void) {
|
||||
memset(&s_service_discovery_ctx, 0,
|
||||
sizeof(struct FakeGATTServiceDiscoveryContext));
|
||||
s_stack_id = 0;
|
||||
s_connection_callback_param = 0;
|
||||
s_connection_event_callback = NULL;
|
||||
s_start_count = 0;
|
||||
s_stop_count = 0;
|
||||
s_start_ret_val = 0;
|
||||
s_stop_ret_val = 0;
|
||||
s_service_changed_indication_count = 0;
|
||||
}
|
||||
|
||||
int GATT_Service_Changed_CCCD_Read_Response(unsigned int BluetoothStackID,
|
||||
unsigned int TransactionID,
|
||||
Word_t CCCD) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GATT_Service_Changed_Indication(unsigned int BluetoothStackID,
|
||||
unsigned int ConnectionID,
|
||||
GATT_Service_Changed_Data_t *Service_Changed_Data) {
|
||||
++s_service_changed_indication_count;
|
||||
return 1; // fake transaction ID
|
||||
}
|
||||
|
||||
int fake_gatt_get_service_changed_indication_count(void) {
|
||||
return s_service_changed_indication_count;
|
||||
}
|
||||
|
||||
int GATT_Service_Changed_Read_Response(unsigned int BluetoothStackID,
|
||||
unsigned int TransactionID,
|
||||
GATT_Service_Changed_Data_t *Service_Changed_Data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t s_write_request_length;
|
||||
static GATT_Client_Event_Callback_t s_write_cb;
|
||||
static unsigned long s_write_cb_param;
|
||||
static unsigned int s_write_connection_id;
|
||||
static unsigned int s_write_stack_id;
|
||||
static uint16_t s_write_handle;
|
||||
|
||||
int GATT_Write_Request(unsigned int BluetoothStackID, unsigned int ConnectionID,
|
||||
Word_t AttributeHandle, Word_t AttributeLength, void *AttributeValue,
|
||||
GATT_Client_Event_Callback_t ClientEventCallback,
|
||||
unsigned long CallbackParameter) {
|
||||
s_write_handle = AttributeHandle;
|
||||
s_write_request_length = AttributeLength;
|
||||
s_write_cb = ClientEventCallback;
|
||||
s_write_cb_param = CallbackParameter;
|
||||
s_write_connection_id = ConnectionID;
|
||||
s_write_stack_id = BluetoothStackID;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t fake_gatt_write_last_written_handle(void) {
|
||||
return s_write_handle;
|
||||
}
|
||||
|
||||
void fake_gatt_put_write_response_for_last_write(void) {
|
||||
cl_assert_(s_write_cb, "GATT_Write_Request need to be called first!");
|
||||
GATT_Write_Response_Data_t data = {
|
||||
.ConnectionID = s_write_connection_id,
|
||||
.TransactionID = 1,
|
||||
.ConnectionType = gctLE,
|
||||
// .RemoteDevice // TODO
|
||||
.BytesWritten = s_write_request_length,
|
||||
};
|
||||
GATT_Client_Event_Data_t event = {
|
||||
.Event_Data_Type = etGATT_Client_Write_Response,
|
||||
.Event_Data_Size = sizeof(GATT_Write_Response_Data_t),
|
||||
.Event_Data.GATT_Write_Response_Data = &data,
|
||||
};
|
||||
s_write_cb(s_write_stack_id, &event, s_write_cb_param);
|
||||
s_write_cb = NULL;
|
||||
}
|
48
tests/fakes/fake_GATTAPI.h
Normal file
48
tests/fakes/fake_GATTAPI.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GATTAPI.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool fake_gatt_is_service_discovery_running(void);
|
||||
|
||||
//! @return Number of times GATT_Start_Service_Discovery has been called since fake_gatt_init()
|
||||
int fake_gatt_is_service_discovery_start_count(void);
|
||||
|
||||
//! @return Number of times GATT_Stop_Service_Discovery has been called since fake_gatt_init()
|
||||
int fake_gatt_is_service_discovery_stop_count(void);
|
||||
|
||||
//! Sets the value that the GATT_Start_Service_Discovery fake should return
|
||||
//! @note fake_gatt_init() will reset this to 0
|
||||
void fake_gatt_set_start_return_value(int ret_value);
|
||||
|
||||
//! Sets the value that the GATT_Stop_Service_Discovery fake should return
|
||||
//! @note fake_gatt_init() will reset this to 0
|
||||
void fake_gatt_set_stop_return_value(int ret_value);
|
||||
|
||||
int fake_gatt_get_service_changed_indication_count(void);
|
||||
|
||||
void fake_gatt_put_service_discovery_event(GATT_Service_Discovery_Event_Data_t *event);
|
||||
|
||||
uint16_t fake_gatt_write_last_written_handle(void);
|
||||
|
||||
void fake_gatt_put_write_response_for_last_write(void);
|
||||
|
||||
void fake_gatt_init(void);
|
475
tests/fakes/fake_GATTAPI_test_vectors.c
Normal file
475
tests/fakes/fake_GATTAPI_test_vectors.c
Normal file
|
@ -0,0 +1,475 @@
|
|||
/*
|
||||
* 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 "fake_GATTAPI_test_vectors.h"
|
||||
#include "fake_GATTAPI.h"
|
||||
|
||||
#include <btutil/bt_uuid.h>
|
||||
|
||||
void fake_gatt_put_discovery_complete_event(uint8_t status,
|
||||
unsigned int connection_id) {
|
||||
GATT_Service_Discovery_Complete_Data_t data =
|
||||
(GATT_Service_Discovery_Complete_Data_t) {
|
||||
.ConnectionID = connection_id,
|
||||
.Status = status,
|
||||
};
|
||||
|
||||
GATT_Service_Discovery_Event_Data_t event =
|
||||
(GATT_Service_Discovery_Event_Data_t) {
|
||||
.Event_Data_Type = etGATT_Service_Discovery_Complete,
|
||||
.Event_Data_Size = GATT_SERVICE_DISCOVERY_COMPLETE_DATA_SIZE,
|
||||
.Event_Data = {
|
||||
.GATT_Service_Discovery_Complete_Data = &data,
|
||||
},
|
||||
};
|
||||
fake_gatt_put_service_discovery_event(&event);
|
||||
}
|
||||
|
||||
void fake_gatt_put_discovery_indication_health_thermometer_service(unsigned int connection_id) {
|
||||
GATT_Characteristic_Descriptor_Information_t cccd1 = {
|
||||
.Characteristic_Descriptor_Handle = 0x15,
|
||||
.Characteristic_Descriptor_UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x02,
|
||||
.UUID_Byte1 = 0x29,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
GATT_Characteristic_Information_t characteristics[1] = {
|
||||
[0] = {
|
||||
.Characteristic_UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x1c,
|
||||
.UUID_Byte1 = 0x2a,
|
||||
},
|
||||
},
|
||||
},
|
||||
.Characteristic_Handle = 0x13,
|
||||
.Characteristic_Properties = 0x2,
|
||||
.NumberOfDescriptors = 0x1,
|
||||
.DescriptorList = &cccd1,
|
||||
},
|
||||
};
|
||||
|
||||
GATT_Service_Discovery_Indication_Data_t data = {
|
||||
.ConnectionID = connection_id,
|
||||
.ServiceInformation = {
|
||||
.Service_Handle = 0x11,
|
||||
.End_Group_Handle = 0x15,
|
||||
.UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x09,
|
||||
.UUID_Byte1 = 0x18,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
.NumberOfCharacteristics = 0x1,
|
||||
.CharacteristicInformationList = characteristics,
|
||||
};
|
||||
|
||||
GATT_Service_Discovery_Event_Data_t event = {
|
||||
.Event_Data_Type = etGATT_Service_Discovery_Indication,
|
||||
.Event_Data_Size = GATT_SERVICE_DISCOVERY_INDICATION_DATA_SIZE,
|
||||
.Event_Data = {
|
||||
.GATT_Service_Discovery_Indication_Data = &data,
|
||||
},
|
||||
};
|
||||
|
||||
fake_gatt_put_service_discovery_event(&event);
|
||||
}
|
||||
|
||||
|
||||
static Service s_health_thermometer_service;
|
||||
|
||||
const Service * fake_gatt_get_health_thermometer_service(void) {
|
||||
s_health_thermometer_service = (const Service) {
|
||||
.uuid = bt_uuid_expand_16bit(0x1809),
|
||||
.handle = 0x11,
|
||||
.num_characteristics = 1,
|
||||
.characteristics = {
|
||||
[0] = {
|
||||
.uuid = bt_uuid_expand_16bit(0x2a1c),
|
||||
.properties = 0x02,
|
||||
.handle = 0x13,
|
||||
.num_descriptors = 1,
|
||||
.descriptors = {
|
||||
[0] = {
|
||||
.uuid = bt_uuid_expand_16bit(0x2902),
|
||||
.handle = 0x15,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
return &s_health_thermometer_service;
|
||||
}
|
||||
|
||||
void fake_gatt_put_discovery_indication_blood_pressure_service(
|
||||
unsigned int connection_id) {
|
||||
GATT_Characteristic_Descriptor_Information_t cccd1 = {
|
||||
.Characteristic_Descriptor_Handle = 0x05,
|
||||
.Characteristic_Descriptor_UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x02,
|
||||
.UUID_Byte1 = 0x29,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
GATT_Characteristic_Descriptor_Information_t cccd2 = {
|
||||
.Characteristic_Descriptor_Handle = 0x09,
|
||||
.Characteristic_Descriptor_UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x02,
|
||||
.UUID_Byte1 = 0x29,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
GATT_Characteristic_Information_t characteristics[2] = {
|
||||
[0] = {
|
||||
.Characteristic_UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x35,
|
||||
.UUID_Byte1 = 0x2a,
|
||||
},
|
||||
},
|
||||
},
|
||||
.Characteristic_Handle = 0x3,
|
||||
.Characteristic_Properties = 0x20,
|
||||
.NumberOfDescriptors = 0x1,
|
||||
.DescriptorList = &cccd1,
|
||||
},
|
||||
[1] = {
|
||||
.Characteristic_UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x49,
|
||||
.UUID_Byte1 = 0x2a,
|
||||
},
|
||||
},
|
||||
},
|
||||
.Characteristic_Handle = 0x7,
|
||||
.Characteristic_Properties = 0x2,
|
||||
.NumberOfDescriptors = 0x1,
|
||||
.DescriptorList = &cccd2,
|
||||
},
|
||||
};
|
||||
|
||||
// Including Health Thermometer Service as "Included Service":
|
||||
GATT_Service_Information_t inc_service_list = {
|
||||
.Service_Handle = 0x11,
|
||||
.End_Group_Handle = 0x15,
|
||||
.UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x09,
|
||||
.UUID_Byte1 = 0x18,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
GATT_Service_Discovery_Indication_Data_t data = {
|
||||
.ConnectionID = connection_id,
|
||||
.ServiceInformation = {
|
||||
.Service_Handle = 0x1,
|
||||
.End_Group_Handle = 0x9,
|
||||
.UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x10,
|
||||
.UUID_Byte1 = 0x18,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
.NumberOfIncludedService = 0x1,
|
||||
.IncludedServiceList = &inc_service_list,
|
||||
.NumberOfCharacteristics = 0x2,
|
||||
.CharacteristicInformationList = characteristics,
|
||||
};
|
||||
|
||||
GATT_Service_Discovery_Event_Data_t event = {
|
||||
.Event_Data_Type = etGATT_Service_Discovery_Indication,
|
||||
.Event_Data_Size = GATT_SERVICE_DISCOVERY_INDICATION_DATA_SIZE,
|
||||
.Event_Data = {
|
||||
.GATT_Service_Discovery_Indication_Data = &data,
|
||||
},
|
||||
};
|
||||
|
||||
fake_gatt_put_service_discovery_event(&event);
|
||||
}
|
||||
|
||||
|
||||
static Service s_blood_pressure_service;
|
||||
#define BP_START_ATT_HANDLE 0x1
|
||||
#define BP_END_ATT_HANDLE 0x9
|
||||
|
||||
const Service * fake_gatt_get_blood_pressure_service(void) {
|
||||
s_blood_pressure_service = (const Service) {
|
||||
.uuid = bt_uuid_expand_16bit(0x1810),
|
||||
.handle = BP_START_ATT_HANDLE,
|
||||
.num_characteristics = 2,
|
||||
.characteristics = {
|
||||
[0] = {
|
||||
.uuid = bt_uuid_expand_16bit(0x2a35),
|
||||
.properties = 0x20, // Indicatable
|
||||
.handle = 0x3,
|
||||
.num_descriptors = 1,
|
||||
.descriptors = {
|
||||
[0] = {
|
||||
.uuid = bt_uuid_expand_16bit(0x2902),
|
||||
.handle = 0x05,
|
||||
},
|
||||
},
|
||||
},
|
||||
[1] = {
|
||||
.uuid = bt_uuid_expand_16bit(0x2a49),
|
||||
.properties = 0x02,
|
||||
.handle = 0x7,
|
||||
.num_descriptors = 1,
|
||||
.descriptors = {
|
||||
[0] = {
|
||||
.uuid = bt_uuid_expand_16bit(0x2902),
|
||||
.handle = BP_END_ATT_HANDLE,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
.num_included_services = 1,
|
||||
.included_services = {
|
||||
[0] = &s_health_thermometer_service,
|
||||
}
|
||||
};
|
||||
return &s_blood_pressure_service;
|
||||
}
|
||||
|
||||
void fake_gatt_get_bp_att_handle_range(uint16_t *start, uint16_t *end) {
|
||||
*start = BP_START_ATT_HANDLE;
|
||||
*end = BP_END_ATT_HANDLE;
|
||||
}
|
||||
|
||||
static Service s_random_128bit_service;
|
||||
|
||||
#define RANDOM_S_START_ATT_HANDLE 0x17
|
||||
#define RANDOM_S_END_ATT_HANDLE 0x25
|
||||
|
||||
void fake_gatt_put_discovery_indication_random_128bit_uuid_service(unsigned int connection_id) {
|
||||
GATT_Characteristic_Descriptor_Information_t cccd1 = {
|
||||
.Characteristic_Descriptor_Handle = 0x21,
|
||||
.Characteristic_Descriptor_UUID = {
|
||||
.UUID_Type = guUUID_128,
|
||||
.UUID = {
|
||||
.UUID_128 = { 0xB2, 0xF9, 0x66, 0xAC, 0xED, 0xFD, 0xEE, 0x97, 0x63, 0x4F, 0xFA, 0x1B, 0x5B, 0x09, 0x68, 0xF7 },
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
GATT_Characteristic_Descriptor_Information_t cccd2 = {
|
||||
.Characteristic_Descriptor_Handle = RANDOM_S_END_ATT_HANDLE,
|
||||
.Characteristic_Descriptor_UUID = {
|
||||
.UUID_Type = guUUID_128,
|
||||
.UUID = {
|
||||
.UUID_128 = { 0xB4, 0xF9, 0x66, 0xAC, 0xED, 0xFD, 0xEE, 0x97, 0x63, 0x4F, 0xFA, 0x1B, 0x5B, 0x09, 0x68, 0xF7 },
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
GATT_Characteristic_Information_t characteristics[2] = {
|
||||
[0] = {
|
||||
.Characteristic_UUID = {
|
||||
.UUID_Type = guUUID_128,
|
||||
.UUID = {
|
||||
.UUID_128 = { 0xB1, 0xF9, 0x66, 0xAC, 0xED, 0xFD, 0xEE, 0x97, 0x63, 0x4F, 0xFA, 0x1B, 0x5B, 0x09, 0x68, 0xF7 },
|
||||
},
|
||||
},
|
||||
.Characteristic_Handle = 0x19,
|
||||
.Characteristic_Properties = 0x2,
|
||||
.NumberOfDescriptors = 0x1,
|
||||
.DescriptorList = &cccd1,
|
||||
},
|
||||
[1] = {
|
||||
.Characteristic_UUID = {
|
||||
.UUID_Type = guUUID_128,
|
||||
.UUID = {
|
||||
.UUID_128 = { 0xB3, 0xF9, 0x66, 0xAC, 0xED, 0xFD, 0xEE, 0x97, 0x63, 0x4F, 0xFA, 0x1B, 0x5B, 0x09, 0x68, 0xF7 },
|
||||
},
|
||||
},
|
||||
.Characteristic_Handle = 0x23,
|
||||
.Characteristic_Properties = 0x2,
|
||||
.NumberOfDescriptors = 0x1,
|
||||
.DescriptorList = &cccd2,
|
||||
},
|
||||
};
|
||||
|
||||
GATT_Service_Discovery_Indication_Data_t data = {
|
||||
.ConnectionID = connection_id,
|
||||
.ServiceInformation = {
|
||||
.Service_Handle = RANDOM_S_START_ATT_HANDLE,
|
||||
.End_Group_Handle = 0x9,
|
||||
.UUID = {
|
||||
.UUID_Type = guUUID_128,
|
||||
.UUID = {
|
||||
.UUID_128 = { 0xB0, 0xF9, 0x66, 0xAC, 0xED, 0xFD, 0xEE, 0x97, 0x63, 0x4F, 0xFA, 0x1B, 0x5B, 0x09, 0x68, 0xF7 },
|
||||
},
|
||||
},
|
||||
},
|
||||
.NumberOfCharacteristics = 0x2,
|
||||
.CharacteristicInformationList = characteristics,
|
||||
};
|
||||
|
||||
GATT_Service_Discovery_Event_Data_t event = {
|
||||
.Event_Data_Type = etGATT_Service_Discovery_Indication,
|
||||
.Event_Data_Size = GATT_SERVICE_DISCOVERY_INDICATION_DATA_SIZE,
|
||||
.Event_Data = {
|
||||
.GATT_Service_Discovery_Indication_Data = &data,
|
||||
},
|
||||
};
|
||||
|
||||
fake_gatt_put_service_discovery_event(&event);
|
||||
}
|
||||
|
||||
const Service * fake_gatt_get_random_128bit_uuid_service(void) {
|
||||
s_random_128bit_service = (const Service) {
|
||||
.uuid = UuidMake(0xF7, 0x68, 0x09, 0x5B, 0x1B, 0xFA, 0x4F, 0x63, 0x97, 0xEE, 0xFD, 0xED, 0xAC, 0x66, 0xF9, 0xB0),
|
||||
.handle = 0x01,
|
||||
.num_characteristics = 2,
|
||||
.characteristics = {
|
||||
[0] = {
|
||||
.uuid = UuidMake(0xF7, 0x68, 0x09, 0x5B, 0x1B, 0xFA, 0x4F, 0x63, 0x97, 0xEE, 0xFD, 0xED, 0xAC, 0x66, 0xF9, 0xB1),
|
||||
.properties = 0x02,
|
||||
.handle = 0x3,
|
||||
.num_descriptors = 1,
|
||||
.descriptors = {
|
||||
[0] = {
|
||||
.uuid = UuidMake(0xF7, 0x68, 0x09, 0x5B, 0x1B, 0xFA, 0x4F, 0x63, 0x97, 0xEE, 0xFD, 0xED, 0xAC, 0x66, 0xF9, 0xB2),
|
||||
.handle = 0x05,
|
||||
},
|
||||
},
|
||||
},
|
||||
[1] = {
|
||||
.uuid = UuidMake(0xF7, 0x68, 0x09, 0x5B, 0x1B, 0xFA, 0x4F, 0x63, 0x97, 0xEE, 0xFD, 0xED, 0xAC, 0x66, 0xF9, 0xB3),
|
||||
.properties = 0x02,
|
||||
.handle = 0x7,
|
||||
.num_descriptors = 1,
|
||||
.descriptors = {
|
||||
[0] = {
|
||||
.uuid = UuidMake(0xF7, 0x68, 0x09, 0x5B, 0x1B, 0xFA, 0x4F, 0x63, 0x97, 0xEE, 0xFD, 0xED, 0xAC, 0x66, 0xF9, 0xB4),
|
||||
.handle = 0x09,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
return &s_random_128bit_service;
|
||||
}
|
||||
|
||||
|
||||
void fake_gatt_put_discovery_indication_gatt_profile_service(unsigned int connection_id,
|
||||
bool has_service_changed_characteristic) {
|
||||
GATT_Characteristic_Descriptor_Information_t cccd1 = {
|
||||
.Characteristic_Descriptor_Handle = 0x05,
|
||||
.Characteristic_Descriptor_UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x02,
|
||||
.UUID_Byte1 = 0x29,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
GATT_Characteristic_Information_t characteristics[1] = {
|
||||
[0] = {
|
||||
.Characteristic_UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x05,
|
||||
.UUID_Byte1 = 0x2a,
|
||||
},
|
||||
},
|
||||
},
|
||||
.Characteristic_Handle = 0x3,
|
||||
.Characteristic_Properties = 0x20,
|
||||
.NumberOfDescriptors = 1,
|
||||
.DescriptorList = &cccd1,
|
||||
},
|
||||
};
|
||||
|
||||
GATT_Service_Discovery_Indication_Data_t data = {
|
||||
.ConnectionID = connection_id,
|
||||
.ServiceInformation = {
|
||||
.Service_Handle = 0x1,
|
||||
.End_Group_Handle = 0x5,
|
||||
.UUID = {
|
||||
.UUID_Type = guUUID_16,
|
||||
.UUID = {
|
||||
.UUID_16 = {
|
||||
.UUID_Byte0 = 0x01,
|
||||
.UUID_Byte1 = 0x18,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
.NumberOfIncludedService = 0,
|
||||
.IncludedServiceList = NULL,
|
||||
.NumberOfCharacteristics = has_service_changed_characteristic ? 1 : 0,
|
||||
.CharacteristicInformationList = has_service_changed_characteristic ? characteristics : NULL,
|
||||
};
|
||||
|
||||
GATT_Service_Discovery_Event_Data_t event = {
|
||||
.Event_Data_Type = etGATT_Service_Discovery_Indication,
|
||||
.Event_Data_Size = GATT_SERVICE_DISCOVERY_INDICATION_DATA_SIZE,
|
||||
.Event_Data = {
|
||||
.GATT_Service_Discovery_Indication_Data = &data,
|
||||
},
|
||||
};
|
||||
|
||||
fake_gatt_put_service_discovery_event(&event);
|
||||
}
|
||||
|
||||
uint16_t fake_gatt_gatt_profile_service_service_changed_att_handle(void) {
|
||||
return 3; // .Characteristic_Handle = 0x3,
|
||||
}
|
||||
|
||||
uint16_t fake_gatt_gatt_profile_service_service_changed_cccd_att_handle(void) {
|
||||
return 5; // .Characteristic_Descriptor_Handle = 0x05,
|
||||
}
|
101
tests/fakes/fake_GATTAPI_test_vectors.h
Normal file
101
tests/fakes/fake_GATTAPI_test_vectors.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/uuid.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// These structures are only used in the unit tests:
|
||||
|
||||
typedef struct {
|
||||
Uuid uuid;
|
||||
uint16_t handle;
|
||||
} Descriptor;
|
||||
|
||||
typedef struct {
|
||||
Uuid uuid;
|
||||
uint8_t properties;
|
||||
uint16_t handle;
|
||||
uint8_t num_descriptors;
|
||||
Descriptor descriptors[3];
|
||||
} Characteristic;
|
||||
|
||||
typedef struct Service {
|
||||
Uuid uuid;
|
||||
uint16_t handle;
|
||||
uint8_t num_characteristics;
|
||||
Characteristic characteristics[3];
|
||||
uint8_t num_included_services;
|
||||
struct Service *included_services[2];
|
||||
} Service;
|
||||
|
||||
//! Simulates receiving the Bluetopia service discovery complete event
|
||||
void fake_gatt_put_discovery_complete_event(uint8_t status,
|
||||
unsigned int connection_id);
|
||||
|
||||
// Health Thermometer Service 0x1809 : 0x11
|
||||
// Temperature Measurement 0x2a1c : 0x13 (properties=0x02)
|
||||
// CCCD 0x2902 : 0x15
|
||||
|
||||
//! Simulates receiving the Bluetopia service discovery indication event
|
||||
void fake_gatt_put_discovery_indication_health_thermometer_service(
|
||||
unsigned int connection_id);
|
||||
//! Returns the Service data structure that can be used for reference
|
||||
const Service * fake_gatt_get_health_thermometer_service(void);
|
||||
|
||||
|
||||
|
||||
// Blood Pressure Service 0x1810 : 0x01
|
||||
// Pressure Characteristic 0x2a35 : 0x03 (properties=0x20)
|
||||
// CCCD 0x2902 : 0x05
|
||||
// Feature Characteristic 0x2a49 : 0x07 (properties=0x02)
|
||||
// CCCD 0x2902 : 0x09
|
||||
// Included Services : Points to the fake Health Thermometer Service
|
||||
|
||||
//! Simulates receiving the Bluetopia service discovery indication event
|
||||
void fake_gatt_put_discovery_indication_blood_pressure_service(
|
||||
unsigned int connection_id);
|
||||
//! Returns the Service data structure that can be used for reference
|
||||
const Service * fake_gatt_get_blood_pressure_service(void);
|
||||
|
||||
|
||||
// Service F768095B-1BFA-4F63-97EE-FDEDAC66F9B0 : 0x17
|
||||
// Char1 F768095B-1BFA-4F63-97EE-FDEDAC66F9B1 : 0x19 (properties=0x02)
|
||||
// Desc1 F768095B-1BFA-4F63-97EE-FDEDAC66F9B2 : 0x21
|
||||
// Char2 F768095B-1BFA-4F63-97EE-FDEDAC66F9B3 : 0x23 (properties=0x02)
|
||||
// Desc2 F768095B-1BFA-4F63-97EE-FDEDAC66F9B4 : 0x25
|
||||
|
||||
void fake_gatt_put_discovery_indication_random_128bit_uuid_service(
|
||||
unsigned int connection_id);
|
||||
|
||||
// Returns the starting ATT handle (Service, 0x1) and ending ATT handle (Desc2 0x09)
|
||||
// for the BP service
|
||||
void fake_gatt_get_bp_att_handle_range(uint16_t *start, uint16_t *end);
|
||||
|
||||
const Service * fake_gatt_get_random_128bit_uuid_service(void);
|
||||
|
||||
|
||||
|
||||
//! Simulates receiving the Bluetopia service discovery indication event
|
||||
void fake_gatt_put_discovery_indication_gatt_profile_service(unsigned int connection_id,
|
||||
bool has_service_changed_characteristic);
|
||||
|
||||
uint16_t fake_gatt_gatt_profile_service_service_changed_att_handle(void);
|
||||
|
||||
|
||||
uint16_t fake_gatt_gatt_profile_service_service_changed_cccd_att_handle(void);
|
151
tests/fakes/fake_HCIAPI.c
Normal file
151
tests/fakes/fake_HCIAPI.c
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* 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 "fake_HCIAPI.h"
|
||||
|
||||
#include "bluetopia_interface.h"
|
||||
|
||||
#include "HCIAPI.h"
|
||||
|
||||
#include "util/list.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
Byte_t Address_Type;
|
||||
BD_ADDR_t Address;
|
||||
} WhitelistEntry;
|
||||
|
||||
static WhitelistEntry *s_head;
|
||||
|
||||
static uint32_t s_whitelist_error_count;
|
||||
|
||||
#define MAX_CC2564_WHITELIST_ENTRIES (25)
|
||||
|
||||
int HCI_LE_Read_Advertising_Channel_Tx_Power(unsigned int BluetoothStackID,
|
||||
Byte_t *StatusResult,
|
||||
Byte_t *Transmit_Power_LevelResult) {
|
||||
*Transmit_Power_LevelResult = -55;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool prv_whitelist_filter(ListNode *found_node, void *data) {
|
||||
const WhitelistEntry *entry1 = (WhitelistEntry *) found_node;
|
||||
const WhitelistEntry *entry2 = (WhitelistEntry *) data;
|
||||
return COMPARE_BD_ADDR(entry1->Address, entry2->Address) &&
|
||||
entry1->Address_Type == entry2->Address_Type;
|
||||
}
|
||||
|
||||
static WhitelistEntry * prv_find_whitelist_entry(const WhitelistEntry *model) {
|
||||
return (WhitelistEntry *) list_find(&s_head->node,
|
||||
prv_whitelist_filter,
|
||||
(void *) model);
|
||||
}
|
||||
|
||||
int HCI_LE_Rand(unsigned int BluetoothStackID, Byte_t *StatusResult,
|
||||
Random_Number_t *Random_NumberResult) {
|
||||
uint8_t *data = (uint8_t *) Random_NumberResult;
|
||||
for (int i = 0; i < sizeof(*Random_NumberResult); ++i) {
|
||||
data[i] = i;
|
||||
}
|
||||
*StatusResult = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HCI_LE_Add_Device_To_White_List(unsigned int BluetoothStackID,
|
||||
Byte_t Address_Type,
|
||||
BD_ADDR_t Address,
|
||||
Byte_t *StatusResult) {
|
||||
const uint32_t count = list_count(&s_head->node);
|
||||
if (count > MAX_CC2564_WHITELIST_ENTRIES) {
|
||||
++s_whitelist_error_count;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const WhitelistEntry model = {
|
||||
.Address_Type = Address_Type,
|
||||
.Address = Address,
|
||||
};
|
||||
|
||||
{
|
||||
WhitelistEntry *e = prv_find_whitelist_entry(&model);
|
||||
// Already present
|
||||
if (e) {
|
||||
++s_whitelist_error_count;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
WhitelistEntry *e = (WhitelistEntry *) malloc(sizeof(WhitelistEntry));
|
||||
*e = (const WhitelistEntry) {
|
||||
.Address_Type = Address_Type,
|
||||
.Address = Address,
|
||||
};
|
||||
s_head = (WhitelistEntry *) list_prepend(&s_head->node, &e->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HCI_LE_Remove_Device_From_White_List(unsigned int BluetoothStackID,
|
||||
Byte_t Address_Type,
|
||||
BD_ADDR_t Address,
|
||||
Byte_t *StatusResult) {
|
||||
const WhitelistEntry model = {
|
||||
.Address_Type = Address_Type,
|
||||
.Address = Address,
|
||||
};
|
||||
WhitelistEntry *e = prv_find_whitelist_entry(&model);
|
||||
if (e) {
|
||||
list_remove(&e->node, (ListNode **) &s_head, NULL);
|
||||
free(e);
|
||||
return 0;
|
||||
} else {
|
||||
// Doesn't exist
|
||||
++s_whitelist_error_count;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool fake_HCIAPI_whitelist_contains(const BTDeviceInternal *device) {
|
||||
const WhitelistEntry model = {
|
||||
.Address_Type = device->is_random_address ? 0x01 : 0x00,
|
||||
.Address = BTDeviceAddressToBDADDR(device->address),
|
||||
};
|
||||
return (prv_find_whitelist_entry(&model) != NULL);
|
||||
}
|
||||
|
||||
uint32_t fake_HCIAPI_whitelist_count(void) {
|
||||
return list_count(&s_head->node);
|
||||
}
|
||||
|
||||
uint32_t fake_HCIAPI_whitelist_error_count(void) {
|
||||
return s_whitelist_error_count;
|
||||
}
|
||||
|
||||
void fake_HCIAPI_deinit(void) {
|
||||
WhitelistEntry *e = s_head;
|
||||
while (e) {
|
||||
WhitelistEntry *next = (WhitelistEntry *) e->node.next;
|
||||
free(e);
|
||||
e = next;
|
||||
}
|
||||
s_head = NULL;
|
||||
|
||||
s_whitelist_error_count = 0;
|
||||
}
|
||||
|
||||
void cc2564A_advert_no_sleep_wa(void) {
|
||||
}
|
30
tests/fakes/fake_HCIAPI.h
Normal file
30
tests/fakes/fake_HCIAPI.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
|
||||
bool fake_HCIAPI_whitelist_contains(const BTDeviceInternal *device);
|
||||
|
||||
uint32_t fake_HCIAPI_whitelist_count(void);
|
||||
|
||||
uint32_t fake_HCIAPI_whitelist_error_count(void);
|
||||
|
||||
void fake_HCIAPI_deinit(void);
|
109
tests/fakes/fake_accel_service.c
Normal file
109
tests/fakes/fake_accel_service.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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 "fake_accel_service.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "applib/accel_service_private.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
static AccelDataHandler s_handler;
|
||||
static AccelRawDataHandler s_raw_handler;
|
||||
static uint32_t s_samples_per_update;
|
||||
|
||||
#define ACCEL_SESSION_REF ((AccelServiceState *)1)
|
||||
|
||||
|
||||
void accel_data_service_subscribe(uint32_t samples_per_update, AccelDataHandler handler) {
|
||||
PBL_ASSERTN(!s_raw_handler);
|
||||
s_handler = handler;
|
||||
s_samples_per_update = samples_per_update;
|
||||
}
|
||||
|
||||
void accel_raw_data_service_subscribe(uint32_t samples_per_update, AccelRawDataHandler handler) {
|
||||
PBL_ASSERTN(!s_handler);
|
||||
s_raw_handler = handler;
|
||||
s_samples_per_update = samples_per_update;
|
||||
}
|
||||
|
||||
void accel_data_service_unsubscribe(void) {
|
||||
s_handler = NULL;
|
||||
s_raw_handler = NULL;
|
||||
}
|
||||
|
||||
|
||||
int accel_service_set_sampling_rate(AccelSamplingRate rate) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fake_accel_service_invoke_callbacks(AccelData *data, uint32_t num_samples) {
|
||||
if (s_handler) {
|
||||
s_handler(data, num_samples);
|
||||
}
|
||||
|
||||
if (s_raw_handler) {
|
||||
AccelRawData raw_data[num_samples];
|
||||
for (int i = 0; i < num_samples; i++) {
|
||||
raw_data[i].x = data[i].x;
|
||||
raw_data[i].y = data[i].y;
|
||||
raw_data[i].z = data[i].z;
|
||||
}
|
||||
|
||||
uint64_t timestamp = data[0].timestamp;
|
||||
s_raw_handler(raw_data, num_samples, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AccelServiceState * accel_session_create(void) {
|
||||
return ACCEL_SESSION_REF;
|
||||
}
|
||||
|
||||
void accel_session_delete(AccelServiceState *session) {
|
||||
}
|
||||
|
||||
void accel_session_data_subscribe(AccelServiceState *session, uint32_t samples_per_update,
|
||||
AccelDataHandler handler) {
|
||||
s_handler = handler;
|
||||
s_samples_per_update = samples_per_update;
|
||||
}
|
||||
|
||||
void accel_session_raw_data_subscribe(
|
||||
AccelServiceState *session, AccelSamplingRate sampling_rate, uint32_t samples_per_update,
|
||||
AccelRawDataHandler handler) {
|
||||
s_raw_handler = handler;
|
||||
s_samples_per_update = samples_per_update;
|
||||
}
|
||||
|
||||
|
||||
void accel_session_data_unsubscribe(AccelServiceState *session) {
|
||||
s_handler = NULL;
|
||||
s_raw_handler = NULL;
|
||||
}
|
||||
|
||||
int accel_session_set_sampling_rate(AccelServiceState *session, AccelSamplingRate rate) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int accel_session_set_samples_per_update(AccelServiceState *session, uint32_t samples_per_update) {
|
||||
PBL_ASSERTN(session == ACCEL_SESSION_REF);
|
||||
s_samples_per_update = samples_per_update;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
24
tests/fakes/fake_accel_service.h
Normal file
24
tests/fakes/fake_accel_service.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "applib/accel_service.h"
|
||||
|
||||
void fake_accel_service_invoke_callbacks(AccelData *data, uint32_t num_samples);
|
||||
|
57
tests/fakes/fake_accessory.c
Normal file
57
tests/fakes/fake_accessory.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 "fake_accessory.h"
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
#include "services/normal/accessory/smartstrap_comms.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define BUFFER_LENGTH 200
|
||||
|
||||
static uint8_t s_buffer[BUFFER_LENGTH];
|
||||
static int s_buffer_index = 0;
|
||||
static bool s_did_send_byte = false;
|
||||
|
||||
void accessory_disable_input(void) {
|
||||
}
|
||||
|
||||
void accessory_enable_input(void) {
|
||||
}
|
||||
|
||||
void accessory_use_dma(bool use_dma) {
|
||||
}
|
||||
|
||||
void accessory_send_byte(uint8_t data) {
|
||||
cl_assert(s_buffer_index < BUFFER_LENGTH);
|
||||
s_buffer[s_buffer_index++] = data;
|
||||
s_did_send_byte = true;
|
||||
}
|
||||
|
||||
void accessory_send_stream(AccessoryDataStreamCallback callback, void *context) {
|
||||
s_buffer_index = 0;
|
||||
memset(s_buffer, 0, BUFFER_LENGTH);
|
||||
while (callback(context)) {
|
||||
cl_assert(s_did_send_byte);
|
||||
}
|
||||
}
|
||||
|
||||
void fake_accessory_get_buffer(uint8_t **buffer, int *length) {
|
||||
*buffer = s_buffer;
|
||||
*length = s_buffer_index;
|
||||
}
|
32
tests/fakes/fake_accessory.h
Normal file
32
tests/fakes/fake_accessory.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void accessory_enable_input(void);
|
||||
void accessory_disable_input(void);
|
||||
|
||||
void accessory_use_dma(bool use_dma);
|
||||
|
||||
void accessory_send_byte(uint8_t data);
|
||||
|
||||
typedef bool (*AccessoryDataStreamCallback)(void *context);
|
||||
void accessory_send_stream(AccessoryDataStreamCallback callback, void *context);
|
||||
|
||||
void fake_accessory_get_buffer(uint8_t **buffer, int *length);
|
615
tests/fakes/fake_aes.c
Normal file
615
tests/fakes/fake_aes.c
Normal file
|
@ -0,0 +1,615 @@
|
|||
/*
|
||||
* 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 "aes.h"
|
||||
|
||||
//! @file fake_aes.c This file implements the aes_128_encrypt_block() helper in software, which in
|
||||
//! real life uses Dialog's AES hardware block.
|
||||
|
||||
// Public-domain AES software implementation.
|
||||
// From: https://github.com/kokke/tiny-AES128-C
|
||||
|
||||
#define ECB (1)
|
||||
#define CBC (0)
|
||||
|
||||
/*
|
||||
|
||||
|
||||
This is an implementation of the AES128 algorithm, specifically ECB and CBC mode.
|
||||
|
||||
The implementation is verified against the test vectors in:
|
||||
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
|
||||
|
||||
ECB-AES128
|
||||
----------
|
||||
|
||||
plain-text:
|
||||
6bc1bee22e409f96e93d7e117393172a
|
||||
ae2d8a571e03ac9c9eb76fac45af8e51
|
||||
30c81c46a35ce411e5fbc1191a0a52ef
|
||||
f69f2445df4f9b17ad2b417be66c3710
|
||||
|
||||
key:
|
||||
2b7e151628aed2a6abf7158809cf4f3c
|
||||
|
||||
resulting cipher
|
||||
3ad77bb40d7a3660a89ecaf32466ef97
|
||||
f5d3d58503b9699de785895a96fdbaaf
|
||||
43b1cd7f598ece23881b00e3ed030688
|
||||
7b0c785e27e8ad3f8223207104725dd4
|
||||
|
||||
|
||||
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
|
||||
You should pad the end of the string with zeros if this is not the case.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Includes: */
|
||||
/*****************************************************************************/
|
||||
#include <stdint.h>
|
||||
#include <string.h> // CBC mode, for memset
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Defines: */
|
||||
/*****************************************************************************/
|
||||
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
|
||||
#define Nb 4
|
||||
// The number of 32 bit words in a key.
|
||||
#define Nk 4
|
||||
// Key length in bytes [128 bit]
|
||||
#define KEYLEN 16
|
||||
// The number of rounds in AES Cipher.
|
||||
#define Nr 10
|
||||
|
||||
// jcallan@github points out that declaring Multiply as a function
|
||||
// reduces code size considerably with the Keil ARM compiler.
|
||||
// See this link for more information: https://github.com/kokke/tiny-AES128-C/pull/3
|
||||
#ifndef MULTIPLY_AS_A_FUNCTION
|
||||
#define MULTIPLY_AS_A_FUNCTION 0
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Private variables: */
|
||||
/*****************************************************************************/
|
||||
// state - array holding the intermediate results during decryption.
|
||||
typedef uint8_t state_t[4][4];
|
||||
static state_t* state;
|
||||
|
||||
// The array that stores the round keys.
|
||||
static uint8_t RoundKey[176];
|
||||
|
||||
// The Key input to the AES Program
|
||||
static const uint8_t* Key;
|
||||
|
||||
#if defined(CBC) && CBC
|
||||
// Initial Vector used only for CBC mode
|
||||
static uint8_t* Iv;
|
||||
#endif
|
||||
|
||||
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
|
||||
// The numbers below can be computed dynamically trading ROM for RAM -
|
||||
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
|
||||
static const uint8_t sbox[256] = {
|
||||
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
|
||||
|
||||
static const uint8_t rsbox[256] =
|
||||
{ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
||||
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
||||
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
|
||||
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
|
||||
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
||||
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
||||
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
|
||||
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
|
||||
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
||||
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
||||
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
|
||||
|
||||
|
||||
// The round constant word array, Rcon[i], contains the values given by
|
||||
// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
|
||||
// Note that i starts at 1, not 0).
|
||||
static const uint8_t Rcon[255] = {
|
||||
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
|
||||
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
|
||||
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
|
||||
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
|
||||
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
|
||||
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
|
||||
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
|
||||
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
|
||||
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
|
||||
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
|
||||
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
|
||||
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
|
||||
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
|
||||
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
|
||||
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
|
||||
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb };
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Private functions: */
|
||||
/*****************************************************************************/
|
||||
static uint8_t getSBoxValue(uint8_t num)
|
||||
{
|
||||
return sbox[num];
|
||||
}
|
||||
|
||||
static uint8_t getSBoxInvert(uint8_t num)
|
||||
{
|
||||
return rsbox[num];
|
||||
}
|
||||
|
||||
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
|
||||
static void KeyExpansion(void)
|
||||
{
|
||||
uint32_t i, j, k;
|
||||
uint8_t tempa[4]; // Used for the column/row operations
|
||||
|
||||
// The first round key is the key itself.
|
||||
for(i = 0; i < Nk; ++i)
|
||||
{
|
||||
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
|
||||
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
|
||||
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
|
||||
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
|
||||
}
|
||||
|
||||
// All other round keys are found from the previous round keys.
|
||||
for(; (i < (Nb * (Nr + 1))); ++i)
|
||||
{
|
||||
for(j = 0; j < 4; ++j)
|
||||
{
|
||||
tempa[j]=RoundKey[(i-1) * 4 + j];
|
||||
}
|
||||
if (i % Nk == 0)
|
||||
{
|
||||
// This function rotates the 4 bytes in a word to the left once.
|
||||
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
|
||||
|
||||
// Function RotWord()
|
||||
{
|
||||
k = tempa[0];
|
||||
tempa[0] = tempa[1];
|
||||
tempa[1] = tempa[2];
|
||||
tempa[2] = tempa[3];
|
||||
tempa[3] = k;
|
||||
}
|
||||
|
||||
// SubWord() is a function that takes a four-byte input word and
|
||||
// applies the S-box to each of the four bytes to produce an output word.
|
||||
|
||||
// Function Subword()
|
||||
{
|
||||
tempa[0] = getSBoxValue(tempa[0]);
|
||||
tempa[1] = getSBoxValue(tempa[1]);
|
||||
tempa[2] = getSBoxValue(tempa[2]);
|
||||
tempa[3] = getSBoxValue(tempa[3]);
|
||||
}
|
||||
|
||||
tempa[0] = tempa[0] ^ Rcon[i/Nk];
|
||||
}
|
||||
else if (Nk > 6 && i % Nk == 4)
|
||||
{
|
||||
// Function Subword()
|
||||
{
|
||||
tempa[0] = getSBoxValue(tempa[0]);
|
||||
tempa[1] = getSBoxValue(tempa[1]);
|
||||
tempa[2] = getSBoxValue(tempa[2]);
|
||||
tempa[3] = getSBoxValue(tempa[3]);
|
||||
}
|
||||
}
|
||||
RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0];
|
||||
RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1];
|
||||
RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2];
|
||||
RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3];
|
||||
}
|
||||
}
|
||||
|
||||
// This function adds the round key to state.
|
||||
// The round key is added to the state by an XOR function.
|
||||
static void AddRoundKey(uint8_t round)
|
||||
{
|
||||
uint8_t i,j;
|
||||
for(i=0;i<4;++i)
|
||||
{
|
||||
for(j = 0; j < 4; ++j)
|
||||
{
|
||||
(*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The SubBytes Function Substitutes the values in the
|
||||
// state matrix with values in an S-box.
|
||||
static void SubBytes(void)
|
||||
{
|
||||
uint8_t i, j;
|
||||
for(i = 0; i < 4; ++i)
|
||||
{
|
||||
for(j = 0; j < 4; ++j)
|
||||
{
|
||||
(*state)[j][i] = getSBoxValue((*state)[j][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The ShiftRows() function shifts the rows in the state to the left.
|
||||
// Each row is shifted with different offset.
|
||||
// Offset = Row number. So the first row is not shifted.
|
||||
static void ShiftRows(void)
|
||||
{
|
||||
uint8_t temp;
|
||||
|
||||
// Rotate first row 1 columns to left
|
||||
temp = (*state)[0][1];
|
||||
(*state)[0][1] = (*state)[1][1];
|
||||
(*state)[1][1] = (*state)[2][1];
|
||||
(*state)[2][1] = (*state)[3][1];
|
||||
(*state)[3][1] = temp;
|
||||
|
||||
// Rotate second row 2 columns to left
|
||||
temp = (*state)[0][2];
|
||||
(*state)[0][2] = (*state)[2][2];
|
||||
(*state)[2][2] = temp;
|
||||
|
||||
temp = (*state)[1][2];
|
||||
(*state)[1][2] = (*state)[3][2];
|
||||
(*state)[3][2] = temp;
|
||||
|
||||
// Rotate third row 3 columns to left
|
||||
temp = (*state)[0][3];
|
||||
(*state)[0][3] = (*state)[3][3];
|
||||
(*state)[3][3] = (*state)[2][3];
|
||||
(*state)[2][3] = (*state)[1][3];
|
||||
(*state)[1][3] = temp;
|
||||
}
|
||||
|
||||
static uint8_t xtime(uint8_t x)
|
||||
{
|
||||
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
|
||||
}
|
||||
|
||||
// MixColumns function mixes the columns of the state matrix
|
||||
static void MixColumns(void)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t Tmp,Tm,t;
|
||||
for(i = 0; i < 4; ++i)
|
||||
{
|
||||
t = (*state)[i][0];
|
||||
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
|
||||
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
|
||||
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
|
||||
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
|
||||
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
|
||||
}
|
||||
}
|
||||
|
||||
// Multiply is used to multiply numbers in the field GF(2^8)
|
||||
#if MULTIPLY_AS_A_FUNCTION
|
||||
static uint8_t Multiply(uint8_t x, uint8_t y)
|
||||
{
|
||||
return (((y & 1) * x) ^
|
||||
((y>>1 & 1) * xtime(x)) ^
|
||||
((y>>2 & 1) * xtime(xtime(x))) ^
|
||||
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
|
||||
((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))));
|
||||
}
|
||||
#else
|
||||
#define Multiply(x, y) \
|
||||
( ((y & 1) * x) ^ \
|
||||
((y>>1 & 1) * xtime(x)) ^ \
|
||||
((y>>2 & 1) * xtime(xtime(x))) ^ \
|
||||
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
|
||||
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
|
||||
|
||||
#endif
|
||||
|
||||
// MixColumns function mixes the columns of the state matrix.
|
||||
// The method used to multiply may be difficult to understand for the inexperienced.
|
||||
// Please use the references to gain more information.
|
||||
static void InvMixColumns(void)
|
||||
{
|
||||
int i;
|
||||
uint8_t a,b,c,d;
|
||||
for(i=0;i<4;++i)
|
||||
{
|
||||
a = (*state)[i][0];
|
||||
b = (*state)[i][1];
|
||||
c = (*state)[i][2];
|
||||
d = (*state)[i][3];
|
||||
|
||||
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
|
||||
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
|
||||
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
|
||||
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The SubBytes Function Substitutes the values in the
|
||||
// state matrix with values in an S-box.
|
||||
static void InvSubBytes(void)
|
||||
{
|
||||
uint8_t i,j;
|
||||
for(i=0;i<4;++i)
|
||||
{
|
||||
for(j=0;j<4;++j)
|
||||
{
|
||||
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InvShiftRows(void)
|
||||
{
|
||||
uint8_t temp;
|
||||
|
||||
// Rotate first row 1 columns to right
|
||||
temp=(*state)[3][1];
|
||||
(*state)[3][1]=(*state)[2][1];
|
||||
(*state)[2][1]=(*state)[1][1];
|
||||
(*state)[1][1]=(*state)[0][1];
|
||||
(*state)[0][1]=temp;
|
||||
|
||||
// Rotate second row 2 columns to right
|
||||
temp=(*state)[0][2];
|
||||
(*state)[0][2]=(*state)[2][2];
|
||||
(*state)[2][2]=temp;
|
||||
|
||||
temp=(*state)[1][2];
|
||||
(*state)[1][2]=(*state)[3][2];
|
||||
(*state)[3][2]=temp;
|
||||
|
||||
// Rotate third row 3 columns to right
|
||||
temp=(*state)[0][3];
|
||||
(*state)[0][3]=(*state)[1][3];
|
||||
(*state)[1][3]=(*state)[2][3];
|
||||
(*state)[2][3]=(*state)[3][3];
|
||||
(*state)[3][3]=temp;
|
||||
}
|
||||
|
||||
|
||||
// Cipher is the main function that encrypts the PlainText.
|
||||
static void Cipher(void)
|
||||
{
|
||||
uint8_t round = 0;
|
||||
|
||||
// Add the First round key to the state before starting the rounds.
|
||||
AddRoundKey(0);
|
||||
|
||||
// There will be Nr rounds.
|
||||
// The first Nr-1 rounds are identical.
|
||||
// These Nr-1 rounds are executed in the loop below.
|
||||
for(round = 1; round < Nr; ++round)
|
||||
{
|
||||
SubBytes();
|
||||
ShiftRows();
|
||||
MixColumns();
|
||||
AddRoundKey(round);
|
||||
}
|
||||
|
||||
// The last round is given below.
|
||||
// The MixColumns function is not here in the last round.
|
||||
SubBytes();
|
||||
ShiftRows();
|
||||
AddRoundKey(Nr);
|
||||
}
|
||||
|
||||
static void InvCipher(void)
|
||||
{
|
||||
uint8_t round=0;
|
||||
|
||||
// Add the First round key to the state before starting the rounds.
|
||||
AddRoundKey(Nr);
|
||||
|
||||
// There will be Nr rounds.
|
||||
// The first Nr-1 rounds are identical.
|
||||
// These Nr-1 rounds are executed in the loop below.
|
||||
for(round=Nr-1;round>0;round--)
|
||||
{
|
||||
InvShiftRows();
|
||||
InvSubBytes();
|
||||
AddRoundKey(round);
|
||||
InvMixColumns();
|
||||
}
|
||||
|
||||
// The last round is given below.
|
||||
// The MixColumns function is not here in the last round.
|
||||
InvShiftRows();
|
||||
InvSubBytes();
|
||||
AddRoundKey(0);
|
||||
}
|
||||
|
||||
static void BlockCopy(uint8_t* output, const uint8_t* input)
|
||||
{
|
||||
uint8_t i;
|
||||
for (i=0;i<KEYLEN;++i)
|
||||
{
|
||||
output[i] = input[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Public functions: */
|
||||
/*****************************************************************************/
|
||||
#if defined(ECB) && ECB
|
||||
|
||||
|
||||
void AES128_ECB_encrypt(const uint8_t* input, const uint8_t* key, uint8_t* output)
|
||||
{
|
||||
// Copy input to output, and work in-memory on output
|
||||
BlockCopy(output, input);
|
||||
state = (state_t*)output;
|
||||
|
||||
Key = key;
|
||||
KeyExpansion();
|
||||
|
||||
// The next function call encrypts the PlainText with the Key using AES algorithm.
|
||||
Cipher();
|
||||
}
|
||||
|
||||
void AES128_ECB_decrypt(const uint8_t* input, const uint8_t* key, uint8_t *output)
|
||||
{
|
||||
// Copy input to output, and work in-memory on output
|
||||
BlockCopy(output, input);
|
||||
state = (state_t*)output;
|
||||
|
||||
// The KeyExpansion routine must be called before encryption.
|
||||
Key = key;
|
||||
KeyExpansion();
|
||||
|
||||
InvCipher();
|
||||
}
|
||||
|
||||
|
||||
#endif // #if defined(ECB) && ECB
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(CBC) && CBC
|
||||
|
||||
|
||||
static void XorWithIv(uint8_t* buf)
|
||||
{
|
||||
uint8_t i;
|
||||
for(i = 0; i < KEYLEN; ++i)
|
||||
{
|
||||
buf[i] ^= Iv[i];
|
||||
}
|
||||
}
|
||||
|
||||
void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
|
||||
{
|
||||
uintptr_t i;
|
||||
uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */
|
||||
|
||||
BlockCopy(output, input);
|
||||
state = (state_t*)output;
|
||||
|
||||
// Skip the key expansion if key is passed as 0
|
||||
if(0 != key)
|
||||
{
|
||||
Key = key;
|
||||
KeyExpansion();
|
||||
}
|
||||
|
||||
if(iv != 0)
|
||||
{
|
||||
Iv = (uint8_t*)iv;
|
||||
}
|
||||
|
||||
for(i = 0; i < length; i += KEYLEN)
|
||||
{
|
||||
XorWithIv(input);
|
||||
BlockCopy(output, input);
|
||||
state = (state_t*)output;
|
||||
Cipher();
|
||||
Iv = output;
|
||||
input += KEYLEN;
|
||||
output += KEYLEN;
|
||||
}
|
||||
|
||||
if(remainders)
|
||||
{
|
||||
BlockCopy(output, input);
|
||||
memset(output + remainders, 0, KEYLEN - remainders); /* add 0-padding */
|
||||
state = (state_t*)output;
|
||||
Cipher();
|
||||
}
|
||||
}
|
||||
|
||||
void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
|
||||
{
|
||||
uintptr_t i;
|
||||
uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */
|
||||
|
||||
BlockCopy(output, input);
|
||||
state = (state_t*)output;
|
||||
|
||||
// Skip the key expansion if key is passed as 0
|
||||
if(0 != key)
|
||||
{
|
||||
Key = key;
|
||||
KeyExpansion();
|
||||
}
|
||||
|
||||
// If iv is passed as 0, we continue to encrypt without re-setting the Iv
|
||||
if(iv != 0)
|
||||
{
|
||||
Iv = (uint8_t*)iv;
|
||||
}
|
||||
|
||||
for(i = 0; i < length; i += KEYLEN)
|
||||
{
|
||||
BlockCopy(output, input);
|
||||
state = (state_t*)output;
|
||||
InvCipher();
|
||||
XorWithIv(output);
|
||||
Iv = input;
|
||||
input += KEYLEN;
|
||||
output += KEYLEN;
|
||||
}
|
||||
|
||||
if(remainders)
|
||||
{
|
||||
BlockCopy(output, input);
|
||||
memset(output+remainders, 0, KEYLEN - remainders); /* add 0-padding */
|
||||
state = (state_t*)output;
|
||||
InvCipher();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // #if defined(CBC) && CBC
|
||||
|
||||
bool aes_128_encrypt_block(const uint8_t key[AES_128_KEY_SIZE],
|
||||
const uint8_t plain_text_block[AES_128_BLOCK_SIZE],
|
||||
uint8_t cipher_text_block_out[AES_128_BLOCK_SIZE]) {
|
||||
AES128_ECB_encrypt(plain_text_block, key, cipher_text_block_out);
|
||||
return true;
|
||||
}
|
274
tests/fakes/fake_animation.c
Normal file
274
tests/fakes/fake_animation.c
Normal file
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Need to use the real struct because the product code accesses the structure directly. It would
|
||||
// be nice to instead create a dummy with only the fields we need, but oh well.
|
||||
#include "applib/ui/animation_private.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
//! List of all animations that were created in the current test in order of creation.
|
||||
ListNode *s_animations;
|
||||
|
||||
// Fake implementations of the real animation interface.
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
Animation *animation_create(void) {
|
||||
AnimationPrivate *animation = malloc(sizeof(AnimationPrivate));
|
||||
*animation = (AnimationPrivate) {};
|
||||
|
||||
if (!s_animations) {
|
||||
s_animations = (ListNode *)animation;
|
||||
} else {
|
||||
list_append(s_animations, (ListNode *)animation);
|
||||
}
|
||||
|
||||
return (Animation *)animation;
|
||||
}
|
||||
|
||||
static Animation *prv_create_from_array(Animation **animation_array, size_t array_len) {
|
||||
AnimationPrivate *parent = (AnimationPrivate *)animation_create();
|
||||
parent->first_child = (AnimationPrivate *)animation_array[0];
|
||||
for (int i = 0; i < (int)array_len; i++) {
|
||||
AnimationPrivate *child = (AnimationPrivate *)animation_array[i];
|
||||
child->parent = parent;
|
||||
if (i + 1 < (int)array_len) {
|
||||
child->sibling = (AnimationPrivate *)animation_array[i + 1];
|
||||
}
|
||||
}
|
||||
return (Animation *)parent;
|
||||
}
|
||||
|
||||
static Animation *prv_create_from_vararg(Animation *animation_a, Animation *animation_b,
|
||||
Animation *animation_c, va_list args) {
|
||||
Animation *animation_array[ANIMATION_MAX_CREATE_VARGS];
|
||||
size_t array_len = 0;
|
||||
animation_array[array_len++] = animation_a;
|
||||
animation_array[array_len++] = animation_b;
|
||||
if (animation_c) {
|
||||
animation_array[array_len++] = animation_c;
|
||||
while (array_len < ANIMATION_MAX_CREATE_VARGS) {
|
||||
void *arg = va_arg(args, void *);
|
||||
if (arg == NULL) {
|
||||
break;
|
||||
}
|
||||
animation_array[array_len++] = arg;
|
||||
}
|
||||
}
|
||||
return prv_create_from_array(animation_array, array_len);
|
||||
}
|
||||
|
||||
Animation *WEAK animation_sequence_create(Animation *animation_a, Animation *animation_b,
|
||||
Animation *animation_c, ...) {
|
||||
va_list args;
|
||||
va_start(args, animation_c);
|
||||
Animation *animation = prv_create_from_vararg(animation_a, animation_b, animation_c, args);
|
||||
va_end(args);
|
||||
return animation;
|
||||
}
|
||||
|
||||
Animation *WEAK animation_sequence_create_from_array(Animation **animation_array,
|
||||
uint32_t array_len) {
|
||||
return prv_create_from_array(animation_array, array_len);
|
||||
}
|
||||
|
||||
Animation *WEAK animation_spawn_create(Animation *animation_a, Animation *animation_b,
|
||||
Animation *animation_c, ...) {
|
||||
va_list args;
|
||||
va_start(args, animation_c);
|
||||
Animation *animation = prv_create_from_vararg(animation_a, animation_b, animation_c, args);
|
||||
va_end(args);
|
||||
return animation;
|
||||
}
|
||||
|
||||
Animation *WEAK animation_spawn_create_from_array(Animation **animation_array,
|
||||
uint32_t array_len) {
|
||||
return prv_create_from_array(animation_array, array_len);
|
||||
}
|
||||
|
||||
typedef void (*AnimationEachCallback)(AnimationPrivate *animation, uintptr_t context);
|
||||
|
||||
static void prv_each(AnimationPrivate *animation, AnimationEachCallback callback,
|
||||
uintptr_t context) {
|
||||
if (animation->first_child) {
|
||||
prv_each(animation->first_child, callback, context);
|
||||
}
|
||||
if (animation->sibling) {
|
||||
prv_each(animation->sibling, callback, context);
|
||||
}
|
||||
callback(animation, context);
|
||||
}
|
||||
|
||||
static void prv_free(AnimationPrivate *animation, uintptr_t context) {
|
||||
list_remove(&animation->list_node, NULL, NULL);
|
||||
free(animation);
|
||||
}
|
||||
|
||||
bool animation_destroy(Animation *animation) {
|
||||
prv_each((AnimationPrivate *)animation, prv_free, (uintptr_t)NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool animation_set_implementation(Animation *animation_h,
|
||||
const AnimationImplementation *implementation) {
|
||||
AnimationPrivate *animation = (AnimationPrivate *)animation_h;
|
||||
if (!animation) {
|
||||
return false;
|
||||
}
|
||||
animation->implementation = implementation;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool animation_is_scheduled(Animation *animation_h) {
|
||||
AnimationPrivate *animation = (AnimationPrivate *)animation_h;
|
||||
if (!animation) {
|
||||
return false;
|
||||
}
|
||||
return animation->scheduled;
|
||||
}
|
||||
|
||||
uint32_t animation_get_duration(Animation *animation, bool include_delay, bool include_play_count) {
|
||||
if (!animation) {
|
||||
return 0;
|
||||
}
|
||||
return ((AnimationPrivate *)animation)->duration_ms;
|
||||
}
|
||||
|
||||
static void prv_call_started(AnimationPrivate *animation, uintptr_t UNUSED context) {
|
||||
if (animation->implementation && animation->implementation->setup) {
|
||||
animation->implementation->setup((Animation *)animation);
|
||||
}
|
||||
if (animation->handlers.started) {
|
||||
animation->handlers.started((Animation *)animation, animation->context);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_call_scheduled(AnimationPrivate *animation, uintptr_t scheduled) {
|
||||
animation->scheduled = scheduled;
|
||||
}
|
||||
|
||||
bool animation_schedule(Animation *animation_h) {
|
||||
AnimationPrivate *animation = (AnimationPrivate *)animation_h;
|
||||
if (!animation) {
|
||||
return false;
|
||||
}
|
||||
if (!animation->scheduled) {
|
||||
prv_each(animation, prv_call_scheduled, true);
|
||||
// If your test is failing, build out this fake so that this is an async start
|
||||
prv_each(animation, prv_call_started, (uintptr_t)NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool animation_set_elapsed(Animation *animation_h, uint32_t elapsed_ms) {
|
||||
AnimationPrivate *animation = (AnimationPrivate *)animation_h;
|
||||
if (!animation) {
|
||||
return false;
|
||||
}
|
||||
if (animation->duration_ms <= elapsed_ms) {
|
||||
animation->is_completed = true;
|
||||
animation_unschedule(animation_h);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool animation_get_elapsed(Animation *animation_h, int32_t *elapsed_ms) {
|
||||
AnimationPrivate *animation= (AnimationPrivate *)animation_h;
|
||||
if (!animation) {
|
||||
return false;
|
||||
}
|
||||
if (elapsed_ms) {
|
||||
*elapsed_ms = animation->is_completed ? animation->duration_ms : 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool animation_set_handlers(Animation *animation_h, AnimationHandlers callbacks, void *context) {
|
||||
AnimationPrivate *animation = (AnimationPrivate *)animation_h;
|
||||
if (!animation) {
|
||||
return false;
|
||||
}
|
||||
animation->handlers = callbacks;
|
||||
animation->context = context;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void *animation_get_context(Animation *animation_h) {
|
||||
AnimationPrivate *animation = (AnimationPrivate *)animation_h;
|
||||
if (!animation) {
|
||||
return false;
|
||||
}
|
||||
return animation->context;
|
||||
}
|
||||
|
||||
static void prv_call_update(AnimationPrivate *animation, uintptr_t progress) {
|
||||
if (animation->implementation && animation->implementation->update) {
|
||||
animation->implementation->update((Animation *)animation, progress);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_call_stopped(AnimationPrivate *animation, uintptr_t finished) {
|
||||
if (animation->handlers.stopped) {
|
||||
animation->handlers.stopped((Animation *)animation, finished, animation->context);
|
||||
}
|
||||
if (animation->implementation && animation->implementation->teardown) {
|
||||
animation->implementation->teardown((Animation *)animation);
|
||||
}
|
||||
}
|
||||
|
||||
bool animation_unschedule(Animation *animation_h) {
|
||||
AnimationPrivate *animation = (AnimationPrivate *)animation_h;
|
||||
if (!animation) {
|
||||
return false;
|
||||
}
|
||||
if (animation->scheduled) {
|
||||
prv_each(animation, prv_call_scheduled, false);
|
||||
prv_each(animation, prv_call_update, ANIMATION_NORMALIZED_MAX);
|
||||
prv_each(animation, prv_call_stopped, animation->is_completed);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Interface for unit tests to query the fake animation state
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
Animation *fake_animation_get_first_animation(void) {
|
||||
return (Animation *)s_animations;
|
||||
}
|
||||
|
||||
Animation *fake_animation_get_next_animation(Animation *animation) {
|
||||
return (Animation *)((AnimationPrivate *)animation)->list_node.next;
|
||||
}
|
||||
|
||||
void fake_animation_cleanup(void) {
|
||||
ListNode *iter = s_animations;
|
||||
while (iter) {
|
||||
ListNode *current = iter;
|
||||
iter = iter->next;
|
||||
free(current);
|
||||
}
|
||||
|
||||
s_animations = NULL;
|
||||
}
|
||||
|
||||
void fake_animation_complete(Animation *animation) {
|
||||
animation_schedule(animation);
|
||||
const uint32_t duration =
|
||||
animation_get_duration(animation, false /* delay */, true /* play_count */);
|
||||
animation_set_elapsed(animation, duration);
|
||||
animation_unschedule(animation);
|
||||
}
|
39
tests/fakes/fake_animation.h
Normal file
39
tests/fakes/fake_animation.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//! @file fake_animation.h
|
||||
//!
|
||||
//! Simple fake of the Animation code. Not intended to be a complete drop in replacement, but good
|
||||
//! enough for some simple tests.
|
||||
|
||||
#include "applib/ui/animation_private.h"
|
||||
|
||||
//! @return A pointer to the first animation that was created since we last called
|
||||
//! fake_animation_cleanup
|
||||
Animation * fake_animation_get_first_animation(void);
|
||||
|
||||
//! @return The next animation after the supplied animation. Animations form a link list based on
|
||||
//! creation time, and that list can be walked by combining this function with
|
||||
//! fake_animation_get_first_animation
|
||||
Animation * fake_animation_get_next_animation(Animation *animation);
|
||||
|
||||
//! Cleans up all fake animation state. Use between tests to ensure a clean slate.
|
||||
void fake_animation_cleanup(void);
|
||||
|
||||
//! Runs an animation to completion by scheduling it, setting its elapsed to its duration, and then
|
||||
//! unscheduling it.
|
||||
//! @param animation The animation to run to completion.
|
||||
void fake_animation_complete(Animation *animation);
|
25
tests/fakes/fake_app_malloc.h
Normal file
25
tests/fakes/fake_app_malloc.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void* app_malloc(size_t bytes) {
|
||||
return malloc(bytes);
|
||||
}
|
||||
|
||||
void app_free(void* ptr) {
|
||||
free(ptr);
|
||||
}
|
147
tests/fakes/fake_app_manager.h
Normal file
147
tests/fakes/fake_app_manager.h
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "process_management/pebble_process_md.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/list.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define TEST_UUID UuidMake(0xF9, 0xC6, 0xEB, 0xE4, 0x06, 0xCD, 0x46, 0xF1, 0xB1, 0x51, 0x24, 0x08, 0x74, 0xD2, 0x07, 0x73)
|
||||
|
||||
static bool s_is_app_running = true;
|
||||
|
||||
static AppInstallId s_app_install_id = INSTALL_ID_INVALID;
|
||||
|
||||
static PebbleProcessMdSystem s_app_md = {
|
||||
.common.uuid = TEST_UUID,
|
||||
.name = "Test App"
|
||||
};
|
||||
|
||||
const PebbleProcessMd* app_manager_get_current_app_md(void) {
|
||||
if (s_is_app_running) {
|
||||
return (const PebbleProcessMd*)&s_app_md;
|
||||
} else {
|
||||
static PebbleProcessMd s_invalid_md = {
|
||||
.uuid = UUID_INVALID,
|
||||
};
|
||||
return &s_invalid_md;
|
||||
}
|
||||
}
|
||||
|
||||
AppInstallId app_manager_get_current_app_id(void) {
|
||||
return s_is_app_running ? s_app_install_id : INSTALL_ID_INVALID;
|
||||
}
|
||||
|
||||
const PebbleProcessMd* sys_process_manager_get_current_process_md(void) {
|
||||
return app_manager_get_current_app_md();
|
||||
}
|
||||
|
||||
bool sys_process_manager_get_current_process_uuid(Uuid *uuid_out) {
|
||||
if (!s_is_app_running) {
|
||||
return false;
|
||||
}
|
||||
*uuid_out = app_manager_get_current_app_md()->uuid;
|
||||
return true;
|
||||
}
|
||||
|
||||
ResAppNum app_manager_get_current_resource_num(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ProcessContext s_app_task_context;
|
||||
ProcessContext* app_manager_get_task_context(void) {
|
||||
return &s_app_task_context;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
void (*callback)(void *);
|
||||
void *data;
|
||||
} CallbackNode;
|
||||
|
||||
static ListNode *s_app_task_callback_head = NULL;
|
||||
|
||||
void app_task_add_callback(void (*callback)(void *data), void *data) {
|
||||
CallbackNode *node = (CallbackNode *) malloc(sizeof(CallbackNode));
|
||||
list_init(&node->node);
|
||||
node->callback = callback;
|
||||
node->data = data;
|
||||
s_app_task_callback_head = list_prepend(s_app_task_callback_head, &node->node);
|
||||
}
|
||||
|
||||
AppInstallId app_install_get_id_for_uuid(const Uuid *uuid) {
|
||||
return s_app_install_id;
|
||||
}
|
||||
|
||||
void app_install_set_is_communicating(const AppInstallId install_id, const bool is_communicating) {
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// Stub manipulation:
|
||||
//
|
||||
void stub_app_set_js(bool is_js) {
|
||||
((PebbleProcessMd *) &s_app_md)->allow_js = is_js;
|
||||
}
|
||||
|
||||
void stub_app_set_uuid(Uuid uuid) {
|
||||
s_app_md.common.uuid = uuid;
|
||||
}
|
||||
|
||||
void stub_app_set_install_id(AppInstallId install_id) {
|
||||
s_app_install_id = install_id;
|
||||
}
|
||||
|
||||
void stub_app_task_callbacks_invoke_pending(void) {
|
||||
// Start at tail ("oldest" callback):
|
||||
CallbackNode *node = (CallbackNode *) list_get_tail(s_app_task_callback_head);
|
||||
while (node) {
|
||||
CallbackNode *prev = (CallbackNode *) list_get_prev(&node->node);
|
||||
if (node->callback) {
|
||||
node->callback(node->data);
|
||||
}
|
||||
list_remove(&node->node, &s_app_task_callback_head, NULL);
|
||||
free(node);
|
||||
node = prev;
|
||||
}
|
||||
PBL_ASSERTN(s_app_task_callback_head == NULL);
|
||||
}
|
||||
|
||||
void stub_app_task_callbacks_cleanup(void) {
|
||||
CallbackNode *node = (CallbackNode *) s_app_task_callback_head;
|
||||
while (node) {
|
||||
CallbackNode *next = (CallbackNode *) list_get_next(&node->node);
|
||||
list_remove(&node->node, &s_app_task_callback_head, NULL);
|
||||
free(node);
|
||||
node = next;
|
||||
}
|
||||
s_app_install_id = INSTALL_ID_INVALID;
|
||||
PBL_ASSERTN(s_app_task_callback_head == NULL);
|
||||
}
|
||||
|
||||
void stub_app_set_is_running(const bool is_running) {
|
||||
s_is_app_running = is_running;
|
||||
}
|
||||
|
||||
|
||||
void stub_app_init(void) {
|
||||
stub_app_set_uuid(TEST_UUID);
|
||||
stub_app_set_is_running(true);
|
||||
}
|
31
tests/fakes/fake_app_state.h
Normal file
31
tests/fakes/fake_app_state.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 "fake_graphics_context.h"
|
||||
|
||||
#include "applib/ui/content_indicator.h"
|
||||
#include "kernel/ui/kernel_ui.h"
|
||||
#include "stubs_app_state.h"
|
||||
|
||||
void fake_app_state_init(void) {
|
||||
// Setup graphics context
|
||||
fake_graphics_context_init();
|
||||
s_app_state_framebuffer = fake_graphics_context_get_framebuffer();
|
||||
|
||||
// Setup content indicator
|
||||
ContentIndicatorsBuffer *buffer = content_indicator_get_current_buffer();
|
||||
content_indicator_init_buffer(buffer);
|
||||
}
|
162
tests/fakes/fake_app_timer.h
Normal file
162
tests/fakes/fake_app_timer.h
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/app_timer.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct FakeAppTimer {
|
||||
uint32_t timeout_ms;
|
||||
bool repeating;
|
||||
AppTimerCallback callback;
|
||||
void* callback_data;
|
||||
struct FakeAppTimer *next;
|
||||
uint32_t timer_id;
|
||||
} FakeAppTimer;
|
||||
|
||||
static FakeAppTimer *s_fake_app_timer_head;
|
||||
static uint32_t s_fake_app_timer_next_id;
|
||||
|
||||
static FakeAppTimer *prv_find_fake_app_timer_by_timer_id(uint32_t timer_id) {
|
||||
for (FakeAppTimer *node = s_fake_app_timer_head; node != NULL; node = node->next) {
|
||||
if (node->timer_id == timer_id) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void fake_app_timer_init(void) {
|
||||
s_fake_app_timer_head = NULL;
|
||||
s_fake_app_timer_next_id = 0;
|
||||
}
|
||||
|
||||
void fake_app_timer_deinit(void) {
|
||||
FakeAppTimer *timer = s_fake_app_timer_head;
|
||||
while (timer) {
|
||||
FakeAppTimer *next = timer->next;
|
||||
app_timer_cancel((AppTimer *)timer);
|
||||
timer = next;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t fake_app_timer_get_timeout(AppTimer *timer) {
|
||||
FakeAppTimer *fake_timer = prv_find_fake_app_timer_by_timer_id((uintptr_t)timer);
|
||||
if (fake_timer) {
|
||||
return fake_timer->timeout_ms;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool fake_app_timer_is_scheduled(AppTimer *timer) {
|
||||
return (prv_find_fake_app_timer_by_timer_id((uintptr_t)timer) != NULL);
|
||||
}
|
||||
|
||||
AppTimer* app_timer_register(uint32_t timeout_ms, AppTimerCallback callback, void* callback_data) {
|
||||
FakeAppTimer *fake_timer = malloc(sizeof(FakeAppTimer));
|
||||
*fake_timer = (FakeAppTimer) {
|
||||
.timeout_ms = timeout_ms,
|
||||
.callback = callback,
|
||||
.callback_data = callback_data,
|
||||
.next = s_fake_app_timer_head,
|
||||
.timer_id = ++s_fake_app_timer_next_id,
|
||||
};
|
||||
|
||||
s_fake_app_timer_head = fake_timer;
|
||||
return (AppTimer *)(uintptr_t)fake_timer->timer_id;
|
||||
}
|
||||
|
||||
AppTimer* app_timer_register_repeatable(uint32_t timeout_ms,
|
||||
AppTimerCallback callback,
|
||||
void* callback_data,
|
||||
bool repeating) {
|
||||
FakeAppTimer *fake_timer = malloc(sizeof(FakeAppTimer));
|
||||
*fake_timer = (FakeAppTimer) {
|
||||
.timeout_ms = timeout_ms,
|
||||
.repeating = repeating,
|
||||
.callback = callback,
|
||||
.callback_data = callback_data,
|
||||
.next = s_fake_app_timer_head,
|
||||
.timer_id = ++s_fake_app_timer_next_id,
|
||||
};
|
||||
|
||||
s_fake_app_timer_head = fake_timer;
|
||||
return (AppTimer *)(uintptr_t)fake_timer->timer_id;
|
||||
}
|
||||
|
||||
bool app_timer_reschedule(AppTimer *timer, uint32_t new_timeout_ms) {
|
||||
FakeAppTimer *fake_timer = prv_find_fake_app_timer_by_timer_id((uintptr_t)timer);
|
||||
if (fake_timer) {
|
||||
fake_timer->timeout_ms = new_timeout_ms;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void prv_unlink_and_free_timer(FakeAppTimer *timer) {
|
||||
FakeAppTimer *prev_timer = s_fake_app_timer_head;
|
||||
if (timer == s_fake_app_timer_head) {
|
||||
// The timer is the head
|
||||
s_fake_app_timer_head = timer->next;
|
||||
} else {
|
||||
// Not the head, find the previous one:
|
||||
while (prev_timer && prev_timer != timer &&
|
||||
prev_timer->next && prev_timer->next != timer) {
|
||||
prev_timer = prev_timer->next;
|
||||
}
|
||||
if (!prev_timer) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Tried to unlink and free non-existing timer %p", timer);
|
||||
return;
|
||||
}
|
||||
prev_timer->next = timer->next;
|
||||
}
|
||||
free(timer);
|
||||
}
|
||||
|
||||
void app_timer_cancel(AppTimer *timer) {
|
||||
FakeAppTimer *fake_timer = prv_find_fake_app_timer_by_timer_id((uintptr_t)timer);
|
||||
if (fake_timer) {
|
||||
prv_unlink_and_free_timer(fake_timer);
|
||||
}
|
||||
}
|
||||
|
||||
bool app_timer_trigger(AppTimer *timer) {
|
||||
FakeAppTimer *fake_timer = prv_find_fake_app_timer_by_timer_id((uintptr_t)timer);
|
||||
if (fake_timer) {
|
||||
AppTimerCallback callback = fake_timer->callback;
|
||||
void *data = fake_timer->callback_data;
|
||||
if (!fake_timer->repeating) {
|
||||
prv_unlink_and_free_timer(fake_timer);
|
||||
}
|
||||
callback(data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void *app_timer_get_data(AppTimer *timer) {
|
||||
FakeAppTimer *fake_timer = prv_find_fake_app_timer_by_timer_id((uintptr_t)timer);
|
||||
if (fake_timer) {
|
||||
return fake_timer->callback_data;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
50
tests/fakes/fake_applib_resource.c
Normal file
50
tests/fakes/fake_applib_resource.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 "applib/applib_resource_private.h"
|
||||
#include "fake_resource_syscalls.h"
|
||||
|
||||
bool applib_resource_track_mmapped(const void *bytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool applib_resource_is_mmapped(const void *bytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool applib_resource_munmap(const void *bytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool applib_resource_munmap_all() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void applib_resource_munmap_or_free(void *bytes) {
|
||||
free(bytes);
|
||||
}
|
||||
|
||||
void *applib_resource_mmap_or_load(ResAppNum app_num, uint32_t resource_id,
|
||||
size_t offset, size_t num_bytes, bool used_aligned) {
|
||||
uint8_t *result = malloc(num_bytes + (used_aligned ? 7 : 0));
|
||||
if (!result || sys_resource_load_range(app_num, resource_id, offset,
|
||||
result, num_bytes) != num_bytes) {
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
62
tests/fakes/fake_battery.c
Normal file
62
tests/fakes/fake_battery.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 "fake_battery.h"
|
||||
#include "kernel/events.h"
|
||||
|
||||
static int s_millivolts = 0;
|
||||
static bool s_usb_connected = false;
|
||||
static bool s_charging = false;
|
||||
|
||||
void fake_battery_init(int millivolts, bool usb_connected, bool charging) {
|
||||
s_millivolts = millivolts;
|
||||
s_usb_connected = usb_connected;
|
||||
s_charging = charging;
|
||||
}
|
||||
|
||||
void fake_battery_set_millivolts(int millivolts) {
|
||||
s_millivolts = millivolts;
|
||||
}
|
||||
|
||||
void fake_battery_set_connected(bool usb_connected) {
|
||||
s_usb_connected = usb_connected;
|
||||
|
||||
// Trigger a connection event!
|
||||
PebbleEvent event = {
|
||||
.type = PEBBLE_BATTERY_CONNECTION_EVENT,
|
||||
.battery_connection = {
|
||||
.is_connected = usb_connected,
|
||||
}
|
||||
};
|
||||
|
||||
event_put(&event);
|
||||
}
|
||||
|
||||
void fake_battery_set_charging(bool charging) {
|
||||
s_charging = charging;
|
||||
}
|
||||
|
||||
int battery_get_millivolts(void) {
|
||||
return s_millivolts;
|
||||
}
|
||||
|
||||
bool battery_is_usb_connected(void) {
|
||||
return s_usb_connected;
|
||||
}
|
||||
|
||||
bool battery_charge_controller_thinks_we_are_charging(void) {
|
||||
return s_charging;
|
||||
}
|
27
tests/fakes/fake_battery.h
Normal file
27
tests/fakes/fake_battery.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void fake_battery_init(int millivolts, bool usb_connected, bool charging);
|
||||
|
||||
void fake_battery_set_millivolts(int millivolts);
|
||||
|
||||
void fake_battery_set_charging(bool charging);
|
||||
|
||||
void fake_battery_set_connected(bool usb_connected);
|
83
tests/fakes/fake_blobdb.c
Normal file
83
tests/fakes/fake_blobdb.c
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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_asserts.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "test_db.h"
|
||||
#include "services/normal/blob_db/api.h"
|
||||
|
||||
static BlobDBId s_blobdb_id = BlobDBIdTest;
|
||||
|
||||
void fake_blob_db_set_id(BlobDBId id) {
|
||||
s_blobdb_id = id;
|
||||
}
|
||||
|
||||
void blob_db_init_dbs(void) {
|
||||
test_db_init();
|
||||
}
|
||||
|
||||
void blob_db_get_dirty_dbs(uint8_t *ids, uint8_t *num_ids) {
|
||||
bool is_dirty = false;
|
||||
test_db_is_dirty(&is_dirty);
|
||||
if (is_dirty) {
|
||||
ids[0] = s_blobdb_id;
|
||||
*num_ids = 1;
|
||||
} else {
|
||||
*num_ids = 0;
|
||||
}
|
||||
}
|
||||
|
||||
status_t blob_db_insert(BlobDBId db_id,
|
||||
const uint8_t *key, int key_len, const uint8_t *val, int val_len) {
|
||||
cl_assert(db_id == s_blobdb_id);
|
||||
return test_db_insert(key, key_len, val, val_len);
|
||||
}
|
||||
|
||||
int blob_db_get_len(BlobDBId db_id,
|
||||
const uint8_t *key, int key_len) {
|
||||
cl_assert(db_id == s_blobdb_id);
|
||||
return test_db_get_len(key, key_len);
|
||||
}
|
||||
|
||||
status_t blob_db_read(BlobDBId db_id,
|
||||
const uint8_t *key, int key_len, uint8_t *val_out, int val_len) {
|
||||
cl_assert(db_id == s_blobdb_id);
|
||||
return test_db_read(key, key_len, val_out, val_len);
|
||||
}
|
||||
|
||||
status_t blob_db_delete(BlobDBId db_id,
|
||||
const uint8_t *key, int key_len) {
|
||||
cl_assert(db_id == s_blobdb_id);
|
||||
return test_db_delete(key, key_len);
|
||||
}
|
||||
|
||||
status_t blob_db_flush(BlobDBId db_id) {
|
||||
cl_assert(db_id == s_blobdb_id);
|
||||
return test_db_flush();
|
||||
}
|
||||
|
||||
BlobDBDirtyItem *blob_db_get_dirty_list(BlobDBId db_id) {
|
||||
cl_assert(db_id == s_blobdb_id);
|
||||
return test_db_get_dirty_list();
|
||||
}
|
||||
|
||||
status_t blob_db_mark_synced(BlobDBId db_id, uint8_t *key, int key_len) {
|
||||
cl_assert(db_id == s_blobdb_id);
|
||||
return test_db_mark_synced(key, key_len);
|
||||
}
|
21
tests/fakes/fake_blobdb.h
Normal file
21
tests/fakes/fake_blobdb.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "services/normal/blob_db/api.h"
|
||||
|
||||
void fake_blob_db_set_id(BlobDBId id);
|
120
tests/fakes/fake_bluetooth_persistent_storage.c
Normal file
120
tests/fakes/fake_bluetooth_persistent_storage.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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 "fake_bluetooth_persistent_storage.h"
|
||||
|
||||
#include "util/list.h"
|
||||
|
||||
#include <btutil/bt_device.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
BTBondingID id;
|
||||
SMIdentityResolvingKey irk;
|
||||
bool is_public_address;
|
||||
BTDeviceInternal device;
|
||||
char name[BT_DEVICE_NAME_BUFFER_SIZE];
|
||||
bool is_gateway;
|
||||
} FakeBonding;
|
||||
|
||||
static FakeBonding *s_head;
|
||||
static BTBondingID s_next_id = 1;
|
||||
|
||||
bool bt_persistent_storage_is_gateway(const BTBondingID bonding) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static BTBondingID prv_next_id(void) {
|
||||
return s_next_id++;
|
||||
}
|
||||
|
||||
static bool prv_find_by_id(ListNode *found_node, void *data) {
|
||||
BTBondingID bonding_id = (BTBondingID) data;
|
||||
const FakeBonding *bonding = (const FakeBonding *) found_node;
|
||||
return (bonding->id == bonding_id);
|
||||
}
|
||||
|
||||
bool bt_persistent_storage_get_ble_pairing_by_id(BTBondingID id,
|
||||
SMIdentityResolvingKey *IRK_out,
|
||||
BTDeviceInternal *device_out,
|
||||
char name[BT_DEVICE_NAME_BUFFER_SIZE]) {
|
||||
FakeBonding *bonding = (FakeBonding *) list_find(&s_head->node, prv_find_by_id,
|
||||
(void *) (uintptr_t) id);
|
||||
if (!bonding) {
|
||||
return false;
|
||||
}
|
||||
if (IRK_out) {
|
||||
*IRK_out = bonding->irk;
|
||||
}
|
||||
if (device_out) {
|
||||
*device_out = bonding->device;
|
||||
}
|
||||
if (name) {
|
||||
strncpy(name, bonding->name, BT_DEVICE_NAME_BUFFER_SIZE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BTBondingID fake_bt_persistent_storage_add(const SMIdentityResolvingKey *irk,
|
||||
const BTDeviceInternal *device,
|
||||
const char name[BT_DEVICE_NAME_BUFFER_SIZE],
|
||||
bool is_gateway) {
|
||||
FakeBonding *bonding = (FakeBonding *) malloc(sizeof(FakeBonding));
|
||||
*bonding = (const FakeBonding) {
|
||||
.id = prv_next_id(),
|
||||
.irk = *irk,
|
||||
.device = *device,
|
||||
.is_gateway = is_gateway,
|
||||
};
|
||||
strncpy(bonding->name, name, BT_DEVICE_NAME_BUFFER_SIZE);
|
||||
s_head = (FakeBonding *) list_prepend(&s_head->node, &bonding->node);
|
||||
|
||||
return bonding->id;
|
||||
}
|
||||
|
||||
BTBondingID bt_persistent_storage_store_ble_pairing(const SMPairingInfo *pairing_info, bool is_gateway,
|
||||
const char *device_name, bool requires_address_pinning, uint8_t flags) {
|
||||
const SMIdentityResolvingKey *IRK = pairing_info->is_remote_identity_info_valid ?
|
||||
&pairing_info->irk : NULL;
|
||||
const BTDeviceInternal *device = pairing_info->is_remote_identity_info_valid ?
|
||||
&pairing_info->identity : NULL;
|
||||
if (!device_name) {
|
||||
device_name = "Device";
|
||||
}
|
||||
return fake_bt_persistent_storage_add(IRK, device, device_name, is_gateway);
|
||||
}
|
||||
|
||||
void fake_bt_persistent_storage_reset(void) {
|
||||
FakeBonding *bonding = s_head;
|
||||
while (bonding) {
|
||||
FakeBonding *next = (FakeBonding *) bonding->node.next;
|
||||
free(bonding);
|
||||
bonding = next;
|
||||
}
|
||||
s_head = NULL;
|
||||
s_next_id = 1;
|
||||
}
|
||||
|
||||
bool bt_persistent_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void bt_persistent_storage_set_root_keys(SM128BitKey *keys_in) {
|
||||
return;
|
||||
}
|
26
tests/fakes/fake_bluetooth_persistent_storage.h
Normal file
26
tests/fakes/fake_bluetooth_persistent_storage.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
|
||||
|
||||
void fake_bt_persistent_storage_reset(void);
|
||||
|
||||
BTBondingID fake_bt_persistent_storage_add(const SMIdentityResolvingKey *irk,
|
||||
const BTDeviceInternal *device,
|
||||
const char name[BT_DEVICE_NAME_BUFFER_SIZE],
|
||||
bool is_gateway);
|
85
tests/fakes/fake_bonding_sync.h
Normal file
85
tests/fakes/fake_bonding_sync.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/list.h"
|
||||
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <bluetooth/bonding_sync.h>
|
||||
#include <bluetooth/sm_types.h>
|
||||
#include <btutil/sm_util.h>
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
BleBonding bonding;
|
||||
} BLEBondingNode;
|
||||
|
||||
static BLEBondingNode *s_ble_bonding_head;
|
||||
|
||||
void bonding_sync_add_bonding(const BleBonding *bonding) {
|
||||
BLEBondingNode *node = (BLEBondingNode *) kernel_malloc_check(sizeof(BLEBondingNode));
|
||||
*node = (BLEBondingNode) {
|
||||
.bonding = *bonding,
|
||||
};
|
||||
s_ble_bonding_head = (BLEBondingNode *) list_prepend((ListNode *)s_ble_bonding_head,
|
||||
(ListNode *)node);
|
||||
}
|
||||
|
||||
void bt_driver_handle_host_added_bonding(const BleBonding *bonding) {
|
||||
bonding_sync_add_bonding(bonding);
|
||||
}
|
||||
|
||||
static bool prv_list_find_cb(ListNode *found_list_node, void *data) {
|
||||
const BleBonding *bonding = (const BleBonding *)data;
|
||||
const BLEBondingNode *found_node = (const BLEBondingNode *)found_list_node;
|
||||
const BleBonding *found_bonding = &found_node->bonding;
|
||||
return sm_is_pairing_info_equal_identity(&bonding->pairing_info, &found_bonding->pairing_info);
|
||||
}
|
||||
|
||||
static void prv_remove_node(BLEBondingNode *node) {
|
||||
list_remove((ListNode *)node, (ListNode **)&s_ble_bonding_head, NULL);
|
||||
kernel_free(node);
|
||||
}
|
||||
|
||||
bool bonding_sync_contains_pairing_info(const SMPairingInfo *pairing_info, bool is_gateway) {
|
||||
BleBonding bonding = {
|
||||
.is_gateway = is_gateway,
|
||||
.pairing_info = *pairing_info,
|
||||
};
|
||||
BLEBondingNode *found_node = (BLEBondingNode *)list_find((ListNode *)s_ble_bonding_head,
|
||||
prv_list_find_cb, (void *)&bonding);
|
||||
return (found_node != NULL);
|
||||
}
|
||||
|
||||
void bt_driver_handle_host_removed_bonding(const BleBonding *bonding) {
|
||||
BLEBondingNode *found_node = (BLEBondingNode *)list_find((ListNode *)s_ble_bonding_head,
|
||||
prv_list_find_cb, (void *)bonding);
|
||||
PBL_ASSERTN(found_node);
|
||||
prv_remove_node(found_node);
|
||||
}
|
||||
|
||||
void bonding_sync_init(void) {
|
||||
PBL_ASSERTN(!s_ble_bonding_head);
|
||||
}
|
||||
|
||||
void bonding_sync_deinit(void) {
|
||||
while (s_ble_bonding_head) {
|
||||
prv_remove_node(s_ble_bonding_head);
|
||||
}
|
||||
}
|
33
tests/fakes/fake_bootbits.c
Normal file
33
tests/fakes/fake_bootbits.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include "fake_bootbits.h"
|
||||
|
||||
static uint32_t s_bootbits = 0;
|
||||
|
||||
void boot_bit_clear(BootBitValue bit) {
|
||||
s_bootbits &= ~bit;
|
||||
}
|
||||
|
||||
bool boot_bit_test(BootBitValue bit) {
|
||||
return (s_bootbits & bit);
|
||||
}
|
||||
|
||||
void fake_boot_bit_set(BootBitValue bit) {
|
||||
s_bootbits |= bit;
|
||||
}
|
21
tests/fakes/fake_bootbits.h
Normal file
21
tests/fakes/fake_bootbits.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "system/bootbits.h"
|
||||
|
||||
void fake_boot_bit_set(BootBitValue bit);
|
126
tests/fakes/fake_clock.c
Normal file
126
tests/fakes/fake_clock.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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 "services/common/i18n/i18n.h"
|
||||
#include "util/attributes.h"
|
||||
#include "util/math.h"
|
||||
|
||||
#include "stubs_i18n.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <memory.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
WEAK const char *string_strip_leading_whitespace(const char *string) {
|
||||
const char *result_string = string;
|
||||
while (*result_string != '\0') {
|
||||
if (*result_string != ' ' &&
|
||||
*result_string != '\n') {
|
||||
break;
|
||||
}
|
||||
result_string++;
|
||||
}
|
||||
|
||||
return result_string;
|
||||
}
|
||||
|
||||
WEAK int time_util_get_num_hours(int hours, bool is24h) {
|
||||
return is24h ? hours : (hours + 12 - 1) % 12 + 1;
|
||||
}
|
||||
|
||||
WEAK bool clock_is_24h_style() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static size_t prv_format_time(char *buffer, int buf_size, const char *format, time_t timestamp) {
|
||||
struct tm time_tm;
|
||||
localtime_r(×tamp, &time_tm);
|
||||
const size_t ret_val = strftime(buffer, buf_size, i18n_get(format, buffer), &time_tm);
|
||||
i18n_free(format, buffer);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
size_t clock_get_time_number(char *number_buffer, size_t number_buffer_size, time_t timestamp) {
|
||||
const size_t written =
|
||||
prv_format_time(number_buffer, number_buffer_size,
|
||||
(clock_is_24h_style() ? i18n_noop("%R") : i18n_noop("%l:%M")), timestamp);
|
||||
const char *number_buffer_ptr = string_strip_leading_whitespace(number_buffer);
|
||||
memmove(number_buffer,
|
||||
number_buffer_ptr,
|
||||
number_buffer_size - (number_buffer_ptr - number_buffer));
|
||||
return written - (number_buffer_ptr - number_buffer);
|
||||
}
|
||||
|
||||
size_t clock_get_time_word(char *buffer, size_t buffer_size, time_t timestamp) {
|
||||
if (clock_is_24h_style()) {
|
||||
buffer[0] = '\0';
|
||||
return 0;
|
||||
} else {
|
||||
return prv_format_time(buffer, buffer_size, i18n_noop("%p"), timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
size_t clock_format_time(char *buffer, uint8_t size, int16_t hours, int16_t minutes,
|
||||
bool add_space) {
|
||||
if (size == 0 || buffer == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is24h = clock_is_24h_style();
|
||||
const char *format;
|
||||
|
||||
// [INTL] you want to have layout resources that specify time formatting,
|
||||
// and be able to set a default one for each locale.
|
||||
if (is24h) {
|
||||
format = "%u:%02u";
|
||||
} else {
|
||||
if (hours < 12) {
|
||||
format = add_space ? "%u:%02u AM" : "%u:%02uAM";
|
||||
} else {
|
||||
format = add_space ? "%u:%02u PM" : "%u:%02uPM";
|
||||
}
|
||||
}
|
||||
return sniprintf(buffer, size, format, time_util_get_num_hours(hours, is24h), minutes);
|
||||
}
|
||||
|
||||
size_t clock_copy_time_string_timestamp(char *buffer, uint8_t size, time_t timestamp) {
|
||||
struct tm time;
|
||||
localtime_r(×tamp, &time);
|
||||
return clock_format_time(buffer, size, time.tm_hour, time.tm_min, true);
|
||||
}
|
||||
|
||||
void clock_copy_time_string(char *buffer, uint8_t size) {
|
||||
time_t t = 0;
|
||||
clock_copy_time_string_timestamp(buffer, size, t);
|
||||
}
|
||||
|
||||
size_t clock_get_date(char *buffer, int buf_size, time_t timestamp) {
|
||||
return prv_format_time(buffer, buf_size, i18n_noop("%m/%d"), timestamp);
|
||||
}
|
||||
|
||||
size_t clock_get_day_date(char *buffer, int buf_size, time_t timestamp) {
|
||||
return prv_format_time(buffer, buf_size, i18n_noop("%d"), timestamp);
|
||||
}
|
||||
|
||||
void clock_hour_and_minute_add(int *hour, int *minute, int delta_minutes) {
|
||||
const int new_minutes = positive_modulo(*hour * MINUTES_PER_HOUR + *minute + delta_minutes,
|
||||
MINUTES_PER_DAY);
|
||||
*hour = new_minutes / MINUTES_PER_HOUR;
|
||||
*minute = new_minutes % MINUTES_PER_HOUR;
|
||||
}
|
25
tests/fakes/fake_content_indicator.h
Normal file
25
tests/fakes/fake_content_indicator.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/ui/content_indicator_private.h"
|
||||
|
||||
static ContentIndicatorsBuffer s_content_indicators_buffer;
|
||||
|
||||
ContentIndicatorsBuffer *content_indicator_get_current_buffer(void) {
|
||||
return &s_content_indicators_buffer;
|
||||
}
|
41
tests/fakes/fake_cron.h
Normal file
41
tests/fakes/fake_cron.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pebbleos/cron.h>
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
static CronJob *s_job = NULL;
|
||||
|
||||
time_t cron_job_schedule(CronJob *job) {
|
||||
s_job = job;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool cron_job_unschedule(CronJob *job) {
|
||||
s_job = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void fake_cron_job_fire(void) {
|
||||
cl_assert(s_job != NULL);
|
||||
CronJob *job = s_job;
|
||||
s_job = NULL;
|
||||
job->cb(job, job->cb_data);
|
||||
}
|
||||
|
27
tests/fakes/fake_display.c
Normal file
27
tests/fakes/fake_display.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 "fake_display.h"
|
||||
|
||||
void display_init(void) {}
|
||||
|
||||
void display_clear(void) {}
|
||||
|
||||
void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb) {}
|
||||
|
||||
void display_enter_static(void) {}
|
||||
|
||||
void display_pulse_vcom(void) {}
|
54
tests/fakes/fake_display.h
Normal file
54
tests/fakes/fake_display.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DISP_BYTES_LINE DISP_COLS / 8
|
||||
// Bytes_per_line + 1 byte for the line address + 1 byte for a null trailer + 1 optional byte for a write command
|
||||
#define DISP_DMA_BUFFER_SIZE (DISP_BYTES_LINE + 3)
|
||||
|
||||
typedef struct {
|
||||
uint8_t address;
|
||||
uint8_t* data;
|
||||
} DisplayRow;
|
||||
|
||||
typedef bool(*NextRowCallback)(DisplayRow* row);
|
||||
typedef void(*UpdateCompleteCallback)(void);
|
||||
|
||||
typedef enum {
|
||||
DISPLAY_STATE_IDLE,
|
||||
DISPLAY_STATE_WRITING
|
||||
} DisplayState;
|
||||
|
||||
typedef struct {
|
||||
DisplayState state;
|
||||
NextRowCallback get_next_row;
|
||||
UpdateCompleteCallback complete;
|
||||
} DisplayContext;
|
||||
|
||||
|
||||
void display_init(void);
|
||||
|
||||
void display_clear(void);
|
||||
|
||||
void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb);
|
||||
|
||||
void display_enter_static(void);
|
||||
|
||||
void display_pulse_vcom(void);
|
49
tests/fakes/fake_event_service.h
Normal file
49
tests/fakes/fake_event_service.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
#include "applib/event_service_client.h"
|
||||
#include "fake_events.h"
|
||||
|
||||
static EventServiceInfo s_event_handler[PEBBLE_NUM_EVENTS];
|
||||
|
||||
void event_service_client_subscribe(EventServiceInfo *service_info) {
|
||||
cl_assert_equal_p(s_event_handler[service_info->type].handler, NULL);
|
||||
s_event_handler[service_info->type] = *service_info;
|
||||
}
|
||||
|
||||
void event_service_client_unsubscribe(EventServiceInfo *service_info) {
|
||||
s_event_handler[service_info->type] = (EventServiceInfo) {};
|
||||
}
|
||||
|
||||
void fake_event_service_init(void) {
|
||||
memset(s_event_handler, sizeof(s_event_handler), 0);
|
||||
}
|
||||
|
||||
void fake_event_service_handle_last(void) {
|
||||
PebbleEvent event = fake_event_get_last();
|
||||
EventServiceInfo *service_info = &s_event_handler[event.type];
|
||||
cl_assert(service_info->handler);
|
||||
service_info->handler(&event, service_info->context);
|
||||
}
|
||||
|
||||
EventServiceInfo *fake_event_service_get_info(PebbleEventType type) {
|
||||
return &s_event_handler[type];
|
||||
}
|
||||
|
77
tests/fakes/fake_evented_timer.h
Normal file
77
tests/fakes/fake_evented_timer.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "services/common/evented_timer.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t timeout_ms;
|
||||
EventedTimerCallback callback;
|
||||
void *callback_data;
|
||||
bool repeating;
|
||||
} FakeEventedTimer;
|
||||
|
||||
EventedTimerID evented_timer_register(uint32_t timeout_ms, bool repeating,
|
||||
EventedTimerCallback callback, void* callback_data) {
|
||||
FakeEventedTimer *fake_timer = malloc(sizeof(FakeEventedTimer));
|
||||
fake_timer->timeout_ms = timeout_ms;
|
||||
fake_timer->callback = callback;
|
||||
fake_timer->callback_data = callback_data;
|
||||
fake_timer->repeating = repeating;
|
||||
return (EventedTimerID)fake_timer;
|
||||
}
|
||||
|
||||
|
||||
bool evented_timer_reschedule(EventedTimerID timer_id, uint32_t new_timeout_ms) {
|
||||
if (timer_id) {
|
||||
FakeEventedTimer *fake_timer = (FakeEventedTimer*)timer_id;
|
||||
fake_timer->timeout_ms = new_timeout_ms;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EventedTimerID evented_timer_register_or_reschedule(EventedTimerID timer_id, uint32_t timeout_ms,
|
||||
EventedTimerCallback callback, void *data) {
|
||||
if (timer_id && evented_timer_reschedule(timer_id, timeout_ms)) {
|
||||
return timer_id;
|
||||
} else {
|
||||
return evented_timer_register(timeout_ms, false, callback, data);
|
||||
}
|
||||
}
|
||||
|
||||
void evented_timer_cancel(EventedTimerID timer_id) {
|
||||
if (timer_id) {
|
||||
FakeEventedTimer *fake_timer = (FakeEventedTimer*)timer_id;
|
||||
free(fake_timer);
|
||||
}
|
||||
}
|
||||
|
||||
bool fake_evented_timer_trigger(EventedTimerID timer_id) {
|
||||
if (timer_id) {
|
||||
FakeEventedTimer *fake_timer = (FakeEventedTimer*)timer_id;
|
||||
fake_timer->callback(fake_timer->callback_data);
|
||||
if (!fake_timer->repeating) {
|
||||
free(fake_timer);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
91
tests/fakes/fake_events.c
Normal file
91
tests/fakes/fake_events.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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 "fake_events.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
|
||||
#include "freertos_types.h"
|
||||
#include "projdefs.h"
|
||||
|
||||
static PebbleEvent s_last_pebble_event;
|
||||
static uint32_t s_fake_event_count = 0;
|
||||
static FakeEventCallback s_fake_event_cb = NULL;
|
||||
|
||||
WEAK void **fake_event_get_buffer(PebbleEvent *event) {
|
||||
switch (event->type) {
|
||||
case PEBBLE_BLE_GATT_CLIENT_EVENT:
|
||||
if (event->bluetooth.le.gatt_client.subtype == PebbleBLEGATTClientEventTypeServiceChange) {
|
||||
return (void **)(&event->bluetooth.le.gatt_client_service.info);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break; // Nothing to do!
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void event_put(PebbleEvent* event) {
|
||||
fake_event_clear_last();
|
||||
s_last_pebble_event = *event;
|
||||
++s_fake_event_count;
|
||||
if (s_fake_event_cb) {
|
||||
s_fake_event_cb(event);
|
||||
}
|
||||
}
|
||||
|
||||
bool event_put_isr(PebbleEvent* event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QueueHandle_t event_kernel_to_kernel_event_queue(void) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
BaseType_t event_queue_cleanup_and_reset(QueueHandle_t queue) {
|
||||
return pdPASS;
|
||||
}
|
||||
|
||||
void fake_event_init(void) {
|
||||
fake_event_reset_count();
|
||||
fake_event_clear_last();
|
||||
}
|
||||
|
||||
PebbleEvent fake_event_get_last(void) {
|
||||
return s_last_pebble_event;
|
||||
}
|
||||
|
||||
void fake_event_clear_last(void) {
|
||||
void **buf = fake_event_get_buffer(&s_last_pebble_event);
|
||||
if (buf && *buf) {
|
||||
kernel_free(*buf);
|
||||
*buf = NULL;
|
||||
}
|
||||
|
||||
s_last_pebble_event = (PebbleEvent) {};
|
||||
}
|
||||
|
||||
void fake_event_reset_count(void) {
|
||||
s_fake_event_count = 0;
|
||||
}
|
||||
|
||||
uint32_t fake_event_get_count(void) {
|
||||
return s_fake_event_count;
|
||||
}
|
||||
|
||||
void fake_event_set_callback(FakeEventCallback cb) {
|
||||
s_fake_event_cb = cb;
|
||||
}
|
35
tests/fakes/fake_events.h
Normal file
35
tests/fakes/fake_events.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/events.h"
|
||||
|
||||
void fake_event_init(void);
|
||||
|
||||
PebbleEvent fake_event_get_last(void);
|
||||
|
||||
void fake_event_clear_last(void);
|
||||
|
||||
void fake_event_reset_count(void);
|
||||
|
||||
uint32_t fake_event_get_count(void);
|
||||
|
||||
void **fake_event_get_buffer(PebbleEvent *event);
|
||||
|
||||
typedef void (*FakeEventCallback)(PebbleEvent *event);
|
||||
void fake_event_set_callback(FakeEventCallback cb);
|
||||
|
77
tests/fakes/fake_flash_region.c
Normal file
77
tests/fakes/fake_flash_region.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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 "drivers/flash.h"
|
||||
#include "flash_region/flash_region.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static void prv_erase_optimal_range(uint32_t min_start, uint32_t max_start,
|
||||
uint32_t min_end, uint32_t max_end) {
|
||||
// We want to erase the sector that starts immediately below max_start but after min_start. If no sector
|
||||
// boundary exists between the two, we need to start erasing sectors after min_start and backfill with
|
||||
// subsector erases.
|
||||
int32_t sector_start = (max_start & SECTOR_ADDR_MASK);
|
||||
int32_t subsector_start = (max_start & SUBSECTOR_ADDR_MASK);
|
||||
if (sector_start < (int32_t) min_start) {
|
||||
sector_start += SECTOR_SIZE_BYTES;
|
||||
}
|
||||
|
||||
// We want to erase ending after min_end but before max_end. If that ends running past the end of max_end,
|
||||
// we need to erase starting with the sector before and fill in with subsector erases.
|
||||
int32_t sector_end = ((min_end - 1) & SECTOR_ADDR_MASK) + SECTOR_SIZE_BYTES;
|
||||
int32_t subsector_end = ((min_end - 1) & SUBSECTOR_ADDR_MASK) + SUBSECTOR_SIZE_BYTES;
|
||||
if (sector_end > (int32_t) max_end) {
|
||||
sector_end -= SECTOR_SIZE_BYTES;
|
||||
}
|
||||
|
||||
int erase_count = 0;
|
||||
|
||||
// Do the upkeep immediately just in case we've spent awhile running without feeding the
|
||||
// watchdog before doing this erase operation.
|
||||
|
||||
if (sector_start < sector_end) {
|
||||
// Now erase the leading subsectors...
|
||||
for (int32_t i = subsector_start; i < sector_start; i += SUBSECTOR_SIZE_BYTES) {
|
||||
flash_erase_subsector_blocking(i);
|
||||
}
|
||||
|
||||
// Erase the full sectors...
|
||||
for (int32_t i = sector_start; i < sector_end; i += SECTOR_SIZE_BYTES) {
|
||||
flash_erase_sector_blocking(i);
|
||||
}
|
||||
|
||||
// Erase the trailing subsectors
|
||||
for (int32_t i = sector_end; i < subsector_end; i += SUBSECTOR_SIZE_BYTES) {
|
||||
flash_erase_subsector_blocking(i);
|
||||
}
|
||||
} else {
|
||||
// Can't erase any full sectors, just erase subsectors the whole way.
|
||||
for (int32_t i = subsector_start; i < subsector_end; i += SUBSECTOR_SIZE_BYTES) {
|
||||
flash_erase_subsector_blocking(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flash_region_erase_optimal_range(uint32_t min_start, uint32_t max_start,
|
||||
uint32_t min_end, uint32_t max_end) {
|
||||
prv_erase_optimal_range(min_start, max_start, min_end, max_end);
|
||||
}
|
||||
|
||||
void flash_region_erase_optimal_range_no_watchdog(uint32_t min_start, uint32_t max_start,
|
||||
uint32_t min_end, uint32_t max_end) {
|
||||
prv_erase_optimal_range(min_start, max_start, min_end, max_end);
|
||||
}
|
130
tests/fakes/fake_fonts.c
Normal file
130
tests/fakes/fake_fonts.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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 "fake_fonts.h"
|
||||
|
||||
#include "resource/resource_ids.auto.h"
|
||||
#include "font_resource_keys.auto.h"
|
||||
#include "applib/fonts/fonts_private.h"
|
||||
#include "applib/graphics/text_resources.h"
|
||||
#include "util/math.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
typedef struct {
|
||||
const char *key;
|
||||
uint32_t handle;
|
||||
FontInfo font_info;
|
||||
} FontHelper;
|
||||
|
||||
static FontHelper s_font_helpers[] = {
|
||||
{.key = FONT_KEY_GOTHIC_14, .handle = RESOURCE_ID_GOTHIC_14},
|
||||
{.key = FONT_KEY_GOTHIC_14_BOLD, .handle = RESOURCE_ID_GOTHIC_14_BOLD},
|
||||
{.key = FONT_KEY_GOTHIC_18, .handle = RESOURCE_ID_GOTHIC_18},
|
||||
{.key = FONT_KEY_GOTHIC_18_BOLD, .handle = RESOURCE_ID_GOTHIC_18_BOLD},
|
||||
{.key = FONT_KEY_GOTHIC_24_BOLD, .handle = RESOURCE_ID_GOTHIC_24_BOLD},
|
||||
{.key = FONT_KEY_DROID_SERIF_28_BOLD, .handle = RESOURCE_ID_GOTHIC_28_BOLD},
|
||||
{.key = FONT_KEY_LECO_20_BOLD_NUMBERS, .handle = RESOURCE_ID_LECO_20_BOLD_NUMBERS},
|
||||
{.key = FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM, .handle = RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM},
|
||||
{.key = FONT_KEY_LECO_32_BOLD_NUMBERS, .handle = RESOURCE_ID_LECO_32_BOLD_NUMBERS},
|
||||
{.key = FONT_KEY_LECO_36_BOLD_NUMBERS, .handle = RESOURCE_ID_LECO_36_BOLD_NUMBERS},
|
||||
{.key = FONT_KEY_LECO_38_BOLD_NUMBERS, .handle = RESOURCE_ID_LECO_38_BOLD_NUMBERS},
|
||||
{.key = FONT_KEY_GOTHIC_14_EMOJI, .handle = RESOURCE_ID_GOTHIC_14_EMOJI},
|
||||
{.key = FONT_KEY_GOTHIC_18_EMOJI, .handle = RESOURCE_ID_GOTHIC_18_EMOJI},
|
||||
{.key = FONT_KEY_GOTHIC_24, .handle = RESOURCE_ID_GOTHIC_24},
|
||||
{.key = FONT_KEY_GOTHIC_24_EMOJI, .handle = RESOURCE_ID_GOTHIC_24_EMOJI},
|
||||
{.key = FONT_KEY_GOTHIC_28, .handle = RESOURCE_ID_GOTHIC_28},
|
||||
{.key = FONT_KEY_GOTHIC_28_EMOJI, .handle = RESOURCE_ID_GOTHIC_28_EMOJI},
|
||||
{.key = FONT_KEY_GOTHIC_28_BOLD, .handle = RESOURCE_ID_GOTHIC_28_BOLD},
|
||||
{.key = FONT_KEY_GOTHIC_36, .handle = RESOURCE_ID_GOTHIC_36},
|
||||
{.key = FONT_KEY_GOTHIC_36_BOLD, .handle = RESOURCE_ID_GOTHIC_36_BOLD},
|
||||
#if (PLATFORM_SNOWY || PLATFORM_SPALDING)
|
||||
{.key = FONT_KEY_AGENCY_FB_36_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_36_NUMBERS_AM_PM },
|
||||
{.key = FONT_KEY_AGENCY_FB_60_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_60_NUMBERS_AM_PM },
|
||||
{.key = FONT_KEY_AGENCY_FB_60_THIN_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_60_THIN_NUMBERS_AM_PM },
|
||||
#elif PLATFORM_ROBERT
|
||||
{.key = FONT_KEY_AGENCY_FB_46_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_46_NUMBERS_AM_PM },
|
||||
{.key = FONT_KEY_AGENCY_FB_88_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_88_NUMBERS_AM_PM },
|
||||
{.key = FONT_KEY_AGENCY_FB_88_THIN_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_88_THIN_NUMBERS_AM_PM },
|
||||
#endif
|
||||
// add more here as we need more fonts from this module
|
||||
};
|
||||
|
||||
static FontHelper *prv_font_helper_from_font_key(const char *font_key) {
|
||||
for (int i = 0; i < ARRAY_LENGTH(s_font_helpers); i++) {
|
||||
if (!strcmp(font_key, s_font_helpers[i].key)) {
|
||||
return &s_font_helpers[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GFont prv_get_font(const char *font_key) {
|
||||
FontHelper *font_helper = prv_font_helper_from_font_key(font_key);
|
||||
cl_assert_(font_helper, font_key);
|
||||
FontInfo *result = &font_helper->font_info;
|
||||
if (!result->loaded) {
|
||||
bool init_result = text_resources_init_font(0, font_helper->handle, 0, result);
|
||||
cl_assert_(init_result, font_key);
|
||||
}
|
||||
|
||||
return (GFont)result;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char *key_name;
|
||||
uint8_t min_height;
|
||||
} s_emoji_fonts[] = {
|
||||
// Keep this sorted in descending order
|
||||
{ FONT_KEY_GOTHIC_28_EMOJI, 28 },
|
||||
{ FONT_KEY_GOTHIC_24_EMOJI, 24 },
|
||||
{ FONT_KEY_GOTHIC_18_EMOJI, 18 },
|
||||
{ FONT_KEY_GOTHIC_14_EMOJI, 14 },
|
||||
};
|
||||
|
||||
FontInfo *fonts_get_system_emoji_font_for_size(unsigned int font_height) {
|
||||
for (uint32_t i = 0; i < ARRAY_LENGTH(s_emoji_fonts); i++) {
|
||||
if (font_height >= s_emoji_fonts[i].min_height) {
|
||||
return prv_get_font(s_emoji_fonts[i].key_name);
|
||||
}
|
||||
}
|
||||
// Didn't find a suitable emoji font
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GFont fonts_get_system_font(const char *font_key) {
|
||||
return prv_get_font(font_key);
|
||||
}
|
||||
|
||||
GFont system_resource_get_font(const char *font_key) {
|
||||
return prv_get_font(font_key);
|
||||
}
|
||||
|
||||
uint8_t fonts_get_font_height(GFont font) {
|
||||
return font->max_height;
|
||||
}
|
||||
|
||||
int16_t fonts_get_font_cap_offset(GFont font) {
|
||||
if (!font) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// FIXME PBL-25709: Actually use font-specific caps and also provide function for baseline offsets
|
||||
return (int16_t)(((int16_t)font->max_height) * 22 / 100);
|
||||
}
|
27
tests/fakes/fake_fonts.h
Normal file
27
tests/fakes/fake_fonts.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/fonts/fonts.h"
|
||||
|
||||
GFont fonts_get_system_font(const char *font_key);
|
||||
|
||||
uint8_t fonts_get_font_height(GFont font);
|
||||
|
||||
GFont system_resource_get_font(const char *font_key);
|
||||
|
||||
FontInfo *fonts_get_system_emoji_font_for_size(unsigned int font_height);
|
125
tests/fakes/fake_framebuffer.c
Normal file
125
tests/fakes/fake_framebuffer.c
Normal 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 "framebuffer.h"
|
||||
|
||||
#include "drivers/display.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/bitset.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
const int FrameBuffer_MaxX = DISP_COLS;
|
||||
const int FrameBuffer_MaxY = DISP_ROWS;
|
||||
|
||||
static FrameBuffer* s_current_framebuffer;
|
||||
static uint8_t s_current_flush_line;
|
||||
|
||||
static void flush_complete(void);
|
||||
static bool flush_get_next_line(DisplayRow* row);
|
||||
static void framebuffer_reset_dirty_lines(FrameBuffer* f);
|
||||
|
||||
uint32_t* framebuffer_get_line(FrameBuffer* f, uint8_t y) {
|
||||
PBL_ASSERT(y < FrameBuffer_MaxY, "Y coordinate is outside of framebuffer dimensions");
|
||||
|
||||
return f->buffer + (y * FRAMEBUFFER_WORDS_PER_ROW);
|
||||
}
|
||||
|
||||
void framebuffer_clear(FrameBuffer* f) {
|
||||
memset(f->buffer, 0xffffffff, FRAMEBUFFER_SIZE_BYTES);
|
||||
memset(f->dirty_lines, 0xff, DISP_ROWS / 8);
|
||||
f->is_dirty = true;
|
||||
f->is_cleared = false;
|
||||
}
|
||||
|
||||
void framebuffer_clear_line(FrameBuffer* f, uint8_t y) {
|
||||
uint32_t* line = framebuffer_get_line(f, y);
|
||||
memset(line, 0xffffffff, FRAMEBUFFER_SIZE_BYTES);
|
||||
bitset8_set(f->dirty_lines, y);
|
||||
|
||||
f->is_dirty = true;
|
||||
}
|
||||
|
||||
void framebuffer_mark_dirty_rect(FrameBuffer* f, GRect rect) {
|
||||
const uint16_t y_start = rect.origin.y;
|
||||
const uint16_t y_end = y_start + rect.size.h;
|
||||
for (uint16_t y = y_start; y < y_end; ++y) {
|
||||
bitset8_update(f->dirty_lines, y, is_dirty);
|
||||
}
|
||||
|
||||
f->is_dirty = true;
|
||||
}
|
||||
|
||||
void framebuffer_set_line(FrameBuffer* f, uint8_t y, const uint32_t* buffer) {
|
||||
memcpy(framebuffer_get_line(f, y), buffer, FRAMEBUFFER_WORDS_PER_ROW);
|
||||
bitset8_set(f->dirty_lines, y);
|
||||
|
||||
f->is_dirty = true;
|
||||
}
|
||||
|
||||
void framebuffer_set_lines(FrameBuffer* f, uint8_t y, const uint8_t num_lines, const uint32_t* buffer) {
|
||||
uint32_t* line = framebuffer_get_line(f, y);
|
||||
memcpy(line, buffer, num_lines * FRAMEBUFFER_WORDS_PER_ROW);
|
||||
const GRect dirty_rect = GRect(0, y, DISP_COLS, num_lines);
|
||||
framebuffer_mark_dirty_rect(f, dirty_rect);
|
||||
}
|
||||
|
||||
void framebuffer_flush(FrameBuffer* f) {
|
||||
// If the framebuffer hasn't been cleared but it is dirty, issue a
|
||||
// display command to blank the screen.
|
||||
if (f->is_cleared) {
|
||||
display_clear();
|
||||
f->is_cleared = false;
|
||||
}
|
||||
|
||||
if (!f->is_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_current_framebuffer = f;
|
||||
s_current_flush_line = 0;
|
||||
display_update(&flush_get_next_line, &flush_complete);
|
||||
}
|
||||
|
||||
static void flush_complete(void) {
|
||||
s_current_flush_line = 0;
|
||||
framebuffer_reset_dirty_lines(s_current_framebuffer);
|
||||
}
|
||||
|
||||
static bool flush_get_next_line(DisplayRow* row) {
|
||||
while (s_current_flush_line < DISP_ROWS) {
|
||||
if (bitset8_get(s_current_framebuffer->dirty_lines, s_current_flush_line)) {
|
||||
row->address = s_current_flush_line;
|
||||
row->data = framebuffer_get_line(s_current_framebuffer, s_current_flush_line);
|
||||
s_current_flush_line++;
|
||||
return true;
|
||||
}
|
||||
|
||||
s_current_flush_line++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void framebuffer_reset_dirty(FrameBuffer* f) {
|
||||
memset(f->dirty_lines, 0x00, DISP_ROWS / 8);
|
||||
f->is_dirty = false;
|
||||
f->is_cleared = false;
|
||||
}
|
||||
|
||||
|
53
tests/fakes/fake_gap_le.c
Normal file
53
tests/fakes/fake_gap_le.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 "fake_gap_le.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
bool gap_le_start_scan(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gap_le_stop_scan(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool s_gap_le_is_initialized = false;
|
||||
|
||||
void gap_le_init(uint32_t stack_id) {
|
||||
s_gap_le_is_initialized = true;
|
||||
}
|
||||
|
||||
bool gap_le_deinit(uint32_t stack_id) {
|
||||
s_gap_le_is_initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void gap_le_connect_params_request_low_power(unsigned int stack_id, const BD_ADDR_t* addr) {
|
||||
|
||||
}
|
||||
|
||||
bool fake_gap_le_is_initialized(void) {
|
||||
return s_gap_le_is_initialized;
|
||||
}
|
||||
|
||||
void sm_set_pairable(uint32_t stack_id, bool is_pairable) {
|
||||
}
|
||||
|
||||
void gap_le_request_power_saving_connection_params(unsigned int stack_id, const BD_ADDR_t* addr) {
|
||||
}
|
||||
|
21
tests/fakes/fake_gap_le.h
Normal file
21
tests/fakes/fake_gap_le.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <stdbool.h>
|
||||
|
||||
bool fake_gap_le_is_initialized(void);
|
62
tests/fakes/fake_gap_le_connect_params.c
Normal file
62
tests/fakes/fake_gap_le_connect_params.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 "fake_gap_le_connect_params.h"
|
||||
|
||||
#include "GAPAPI.h"
|
||||
|
||||
static ResponseTimeState s_last_requested_desired_state;
|
||||
|
||||
void gap_le_connect_params_request(GAPLEConnection *connection,
|
||||
ResponseTimeState desired_state) {
|
||||
s_last_requested_desired_state = desired_state;
|
||||
}
|
||||
|
||||
void gap_le_connect_params_setup_connection(GAPLEConnection *connection) {
|
||||
}
|
||||
|
||||
void gap_le_connect_params_cleanup_by_connection(GAPLEConnection *connection) {
|
||||
}
|
||||
|
||||
void gap_le_connect_params_handle_update(unsigned int stack_id,
|
||||
const GAP_LE_Connection_Parameter_Updated_Event_Data_t *event) {
|
||||
}
|
||||
|
||||
void gap_le_connect_params_handle_connection_parameter_update_response(
|
||||
const GAP_LE_Connection_Parameter_Update_Response_Event_Data_t *event_data) {
|
||||
}
|
||||
|
||||
static ResponseTimeState s_actual_state;
|
||||
ResponseTimeState gap_le_connect_params_get_actual_state(GAPLEConnection *connection) {
|
||||
return s_actual_state;
|
||||
}
|
||||
|
||||
void fake_gap_le_connect_params_init(void) {
|
||||
s_last_requested_desired_state = ResponseTimeInvalid;
|
||||
s_actual_state = ResponseTimeInvalid;
|
||||
}
|
||||
|
||||
ResponseTimeState fake_gap_le_connect_params_get_last_requested(void) {
|
||||
return s_last_requested_desired_state;
|
||||
}
|
||||
|
||||
void fake_gap_le_connect_params_reset_last_requested(void) {
|
||||
s_last_requested_desired_state = ResponseTimeInvalid;
|
||||
}
|
||||
|
||||
void fake_gap_le_connect_params_set_actual_state(ResponseTimeState actual_state) {
|
||||
s_actual_state = actual_state;
|
||||
}
|
27
tests/fakes/fake_gap_le_connect_params.h
Normal file
27
tests/fakes/fake_gap_le_connect_params.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "comm/ble/gap_le_connect_params.h"
|
||||
|
||||
void fake_gap_le_connect_params_init(void);
|
||||
|
||||
ResponseTimeState fake_gap_le_connect_params_get_last_requested(void);
|
||||
|
||||
void fake_gap_le_connect_params_reset_last_requested(void);
|
||||
|
||||
void fake_gap_le_connect_params_set_actual_state(ResponseTimeState actual_state);
|
24
tests/fakes/fake_gatt_client_discovery.c
Normal file
24
tests/fakes/fake_gatt_client_discovery.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 "comm/ble/gatt_client_discovery.h"
|
||||
|
||||
#include "comm/ble/gap_le_connection.h"
|
||||
|
||||
void gatt_client_discovery_cleanup_by_connection(GAPLEConnection *connection) { }
|
||||
|
||||
void gatt_client_subscription_cleanup_by_att_handle_range(
|
||||
struct GAPLEConnection *connection, ATTHandleRange *range) { }
|
220
tests/fakes/fake_gatt_client_operations.c
Normal file
220
tests/fakes/fake_gatt_client_operations.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* 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 "fake_gatt_client_operations.h"
|
||||
|
||||
#include "comm/ble/gatt_client_operations.h"
|
||||
|
||||
#include "util/list.h"
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
BLECharacteristic characteristic;
|
||||
GAPLEClient client;
|
||||
} Read;
|
||||
|
||||
static Read *s_read_head;
|
||||
|
||||
static BTErrno s_read_return_value;
|
||||
|
||||
BTErrno gatt_client_op_read(BLECharacteristic characteristic,
|
||||
GAPLEClient client) {
|
||||
if (s_read_return_value != BTErrnoOK) {
|
||||
return s_read_return_value;
|
||||
}
|
||||
Read *read = malloc(sizeof(Read));
|
||||
*read = (const Read) {
|
||||
.characteristic = characteristic,
|
||||
.client = client,
|
||||
};
|
||||
if (s_read_head) {
|
||||
list_append((ListNode *)s_read_head, &read->node);
|
||||
} else {
|
||||
s_read_head = read;
|
||||
}
|
||||
return BTErrnoOK;
|
||||
}
|
||||
|
||||
void gatt_client_consume_read_response(uintptr_t object_ref,
|
||||
uint8_t value_out[],
|
||||
uint16_t value_length,
|
||||
GAPLEClient client) {
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
BLECharacteristic characteristic;
|
||||
GAPLEClient client;
|
||||
uint8_t *value;
|
||||
size_t value_length;
|
||||
bool is_response_required;
|
||||
} Write;
|
||||
|
||||
static Write *s_write_head;
|
||||
|
||||
static BTErrno s_write_return_value;
|
||||
|
||||
static BTErrno fake_gatt_client_write(BLECharacteristic characteristic,
|
||||
const uint8_t *value,
|
||||
size_t value_length,
|
||||
GAPLEClient client,
|
||||
bool is_response_required) {
|
||||
if (s_write_return_value != BTErrnoOK) {
|
||||
return s_write_return_value;
|
||||
}
|
||||
Write *write = malloc(sizeof(Write));
|
||||
uint8_t *buffer;
|
||||
if (value_length) {
|
||||
cl_assert(value);
|
||||
buffer = malloc(value_length);
|
||||
memcpy(buffer, value, value_length);
|
||||
} else {
|
||||
cl_assert_equal_p(value, NULL);
|
||||
buffer = NULL;
|
||||
}
|
||||
*write = (const Write) {
|
||||
.characteristic = characteristic,
|
||||
.client = client,
|
||||
.is_response_required = is_response_required,
|
||||
.value = buffer,
|
||||
.value_length = value_length,
|
||||
};
|
||||
if (s_write_head) {
|
||||
list_append((ListNode *)s_write_head, &write->node);
|
||||
} else {
|
||||
s_write_head = write;
|
||||
}
|
||||
return BTErrnoOK;
|
||||
}
|
||||
|
||||
BTErrno gatt_client_op_write(BLECharacteristic characteristic,
|
||||
const uint8_t *value,
|
||||
size_t value_length,
|
||||
GAPLEClient client) {
|
||||
return fake_gatt_client_write(characteristic, value, value_length, client,
|
||||
true /* is_response_required */);
|
||||
}
|
||||
|
||||
BTErrno gatt_client_op_write_without_response(BLECharacteristic characteristic,
|
||||
const uint8_t *value,
|
||||
size_t value_length,
|
||||
GAPLEClient client) {
|
||||
return fake_gatt_client_write(characteristic, value, value_length, client,
|
||||
false /* is_response_required */);
|
||||
}
|
||||
|
||||
BTErrno gatt_client_op_write_descriptor(BLEDescriptor descriptor,
|
||||
const uint8_t *value,
|
||||
size_t value_length,
|
||||
GAPLEClient client) {
|
||||
return BTErrnoOK;
|
||||
}
|
||||
|
||||
BTErrno gatt_client_op_read_descriptor(BLEDescriptor descriptor,
|
||||
GAPLEClient client) {
|
||||
return BTErrnoOK;
|
||||
}
|
||||
|
||||
BTErrno gatt_client_op_write_descriptor_cccd(BLEDescriptor cccd, const uint16_t *value) {
|
||||
return BTErrnoOK;
|
||||
}
|
||||
|
||||
void gatt_client_op_cleanup(GAPLEClient client) {
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Fake Manipulation
|
||||
|
||||
void fake_gatt_client_op_init(void) {
|
||||
s_read_return_value = BTErrnoOK;
|
||||
s_write_return_value = BTErrnoOK;
|
||||
}
|
||||
|
||||
void fake_gatt_client_op_deinit(void) {
|
||||
Read *read = s_read_head;
|
||||
while (read) {
|
||||
Read *next = (Read *) read->node.next;
|
||||
free(read);
|
||||
read = next;
|
||||
}
|
||||
s_read_head = NULL;
|
||||
|
||||
fake_gatt_client_op_clear_write_list();
|
||||
}
|
||||
|
||||
void fake_gatt_client_op_set_read_return_value(BTErrno e) {
|
||||
s_read_return_value = e;
|
||||
}
|
||||
|
||||
void fake_gatt_client_op_assert_read(BLECharacteristic characteristic,
|
||||
GAPLEClient client) {
|
||||
if (s_read_head) {
|
||||
cl_assert_equal_i(characteristic, s_read_head->characteristic);
|
||||
cl_assert_equal_i(client, s_read_head->client);
|
||||
} else {
|
||||
cl_assert_(false, "No gatt_client_op_read() has happened at all");
|
||||
}
|
||||
Read *old_head = s_read_head;
|
||||
s_read_head = (Read *) list_pop_head(&s_read_head->node);
|
||||
free(old_head);
|
||||
}
|
||||
|
||||
void fake_gatt_client_op_set_write_return_value(BTErrno e) {
|
||||
s_write_return_value = e;
|
||||
}
|
||||
|
||||
void fake_gatt_client_op_clear_write_list(void) {
|
||||
Write *write = s_write_head;
|
||||
while (write) {
|
||||
Write *next = (Write *) write->node.next;
|
||||
free(write->value);
|
||||
free(write);
|
||||
write = next;
|
||||
}
|
||||
s_write_head = NULL;
|
||||
}
|
||||
|
||||
void fake_gatt_client_op_assert_no_write(void) {
|
||||
cl_assert_equal_p(s_write_head, NULL);
|
||||
}
|
||||
|
||||
static void fake_gatt_client_op_assert_write_failed(void) {
|
||||
cl_assert_(false, "No gatt_client_op_write() or "
|
||||
"gatt_client_op_write_without_response() has happened at all");
|
||||
}
|
||||
|
||||
void fake_gatt_client_op_assert_write(BLECharacteristic characteristic,
|
||||
const uint8_t *value, size_t value_length,
|
||||
GAPLEClient client, bool is_response_required) {
|
||||
if (s_write_head) {
|
||||
cl_assert_equal_i(characteristic, s_write_head->characteristic);
|
||||
cl_assert_equal_i(s_write_head->value_length, value_length);
|
||||
cl_assert_equal_i(memcmp(value, s_write_head->value, value_length), 0);
|
||||
cl_assert_equal_i(client, s_write_head->client);
|
||||
cl_assert_equal_b(is_response_required, s_write_head->is_response_required);
|
||||
} else {
|
||||
fake_gatt_client_op_assert_write_failed();
|
||||
}
|
||||
Write *old_write = s_write_head;
|
||||
s_write_head = (Write *) list_pop_head(&s_write_head->node);
|
||||
free(old_write->value);
|
||||
free(old_write);
|
||||
}
|
40
tests/fakes/fake_gatt_client_operations.h
Normal file
40
tests/fakes/fake_gatt_client_operations.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/bluetooth/ble_client.h"
|
||||
|
||||
#include "comm/ble/gap_le_task.h"
|
||||
|
||||
void fake_gatt_client_op_init(void);
|
||||
|
||||
void fake_gatt_client_op_deinit(void);
|
||||
|
||||
void fake_gatt_client_op_set_read_return_value(BTErrno e);
|
||||
|
||||
void fake_gatt_client_op_assert_read(BLECharacteristic characteristic,
|
||||
GAPLEClient client);
|
||||
|
||||
void fake_gatt_client_op_set_write_return_value(BTErrno e);
|
||||
|
||||
void fake_gatt_client_op_clear_write_list(void);
|
||||
|
||||
void fake_gatt_client_op_assert_no_write(void);
|
||||
|
||||
void fake_gatt_client_op_assert_write(BLECharacteristic characteristic,
|
||||
const uint8_t *value, size_t value_length,
|
||||
GAPLEClient client, bool is_response_required);
|
102
tests/fakes/fake_gatt_client_subscriptions.c
Normal file
102
tests/fakes/fake_gatt_client_subscriptions.c
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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 "fake_gatt_client_subscriptions.h"
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
BLECharacteristic characteristic;
|
||||
BLESubscription subscription_type;
|
||||
GAPLEClient client;
|
||||
} Subscribe;
|
||||
|
||||
static Subscribe *s_subscribe_head;
|
||||
|
||||
static BTErrno s_subscribe_return_value;
|
||||
|
||||
BTErrno gatt_client_subscriptions_subscribe(BLECharacteristic characteristic,
|
||||
BLESubscription subscription_type,
|
||||
GAPLEClient client) {
|
||||
Subscribe *subscribe = malloc(sizeof(Subscribe));
|
||||
*subscribe = (const Subscribe) {
|
||||
.characteristic = characteristic,
|
||||
.subscription_type = subscription_type,
|
||||
.client = client,
|
||||
};
|
||||
if (s_subscribe_head) {
|
||||
list_append((ListNode *)s_subscribe_head, &subscribe->node);
|
||||
} else {
|
||||
s_subscribe_head = subscribe;
|
||||
}
|
||||
return s_subscribe_return_value;
|
||||
}
|
||||
|
||||
bool gatt_client_subscriptions_get_notification_header(GAPLEClient client,
|
||||
GATTBufferedNotificationHeader *header_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t gatt_client_subscriptions_consume_notification(BLECharacteristic *characteristic_ref_out,
|
||||
uint8_t *value_out,
|
||||
uint16_t *value_length_in_out,
|
||||
GAPLEClient client, bool *has_more_out) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gatt_client_subscriptions_cleanup_by_client(GAPLEClient client) {
|
||||
|
||||
}
|
||||
|
||||
void gatt_client_subscriptions_cleanup_by_connection(struct GAPLEConnection *connection,
|
||||
bool should_unsubscribe) {
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Fake Manipulation
|
||||
|
||||
void fake_gatt_client_subscriptions_init(void) {
|
||||
s_subscribe_return_value = BTErrnoOK;
|
||||
}
|
||||
|
||||
void fake_gatt_client_subscriptions_deinit(void) {
|
||||
Subscribe *subscribe = s_subscribe_head;
|
||||
while (subscribe) {
|
||||
Subscribe *next = (Subscribe *) subscribe->node.next;
|
||||
free(subscribe);
|
||||
subscribe = next;
|
||||
}
|
||||
s_subscribe_head = NULL;
|
||||
}
|
||||
|
||||
void fake_gatt_client_subscriptions_set_subscribe_return_value(BTErrno e) {
|
||||
s_subscribe_return_value = e;
|
||||
}
|
||||
|
||||
void fake_gatt_client_subscriptions_assert_subscribe(BLECharacteristic characteristic,
|
||||
BLESubscription subscription_type,
|
||||
GAPLEClient client) {
|
||||
if (s_subscribe_head) {
|
||||
cl_assert_equal_i(characteristic, s_subscribe_head->characteristic);
|
||||
cl_assert_equal_i(subscription_type, s_subscribe_head->subscription_type);
|
||||
cl_assert_equal_i(client, s_subscribe_head->client);
|
||||
} else {
|
||||
cl_assert_(false, "No gatt_client_subscriptions_subscribe() has happened at all");
|
||||
}
|
||||
s_subscribe_head = (Subscribe *) list_pop_head(&s_subscribe_head->node);
|
||||
}
|
29
tests/fakes/fake_gatt_client_subscriptions.h
Normal file
29
tests/fakes/fake_gatt_client_subscriptions.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "comm/ble/gatt_client_subscriptions.h"
|
||||
|
||||
void fake_gatt_client_subscriptions_init(void);
|
||||
|
||||
void fake_gatt_client_subscriptions_deinit(void);
|
||||
|
||||
void fake_gatt_client_subscriptions_set_subscribe_return_value(BTErrno e);
|
||||
|
||||
void fake_gatt_client_subscriptions_assert_subscribe(BLECharacteristic characteristic,
|
||||
BLESubscription subscription_type,
|
||||
GAPLEClient client);
|
46
tests/fakes/fake_gbitmap_get_data_row.c
Normal file
46
tests/fakes/fake_gbitmap_get_data_row.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 "fake_gbitmap_get_data_row.h"
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
bool s_fake_data_row_handling = false;
|
||||
bool s_fake_data_row_handling_disable_vertical_flip = false;
|
||||
|
||||
extern GBitmapDataRowInfo prv_gbitmap_get_data_row_info(const GBitmap *bitmap, uint16_t y);
|
||||
|
||||
// Overrides the same function in gbitmap.c
|
||||
GBitmapDataRowInfo gbitmap_get_data_row_info(const GBitmap *bitmap, uint16_t y) {
|
||||
// If fake data row handling is enabled, clip the row to a diamond mask
|
||||
if (s_fake_data_row_handling) {
|
||||
const int16_t diamond_offset =
|
||||
ABS((bitmap->bounds.size.w / 2) - (y * bitmap->bounds.size.w / bitmap->bounds.size.h));
|
||||
const int16_t min_x = bitmap->bounds.origin.x + diamond_offset;
|
||||
// vertically flip unless disabled
|
||||
if (!s_fake_data_row_handling_disable_vertical_flip) {
|
||||
y = bitmap->bounds.size.h - y - 1;
|
||||
}
|
||||
return (GBitmapDataRowInfo){
|
||||
.data = prv_gbitmap_get_data_row_info(bitmap, y).data,
|
||||
.min_x = min_x,
|
||||
.max_x = grect_get_max_x(&bitmap->bounds) - diamond_offset - 1,
|
||||
};
|
||||
}
|
||||
return prv_gbitmap_get_data_row_info(bitmap, y);
|
||||
}
|
22
tests/fakes/fake_gbitmap_get_data_row.h
Normal file
22
tests/fakes/fake_gbitmap_get_data_row.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
extern bool s_fake_data_row_handling;
|
||||
extern bool s_fake_data_row_handling_disable_vertical_flip;
|
36
tests/fakes/fake_gbitmap_png.c
Normal file
36
tests/fakes/fake_gbitmap_png.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 "applib/graphics/gtypes.h"
|
||||
#include "upng.h"
|
||||
|
||||
bool gbitmap_png_data_is_png(uint8_t *data, size_t data_size) {return false;}
|
||||
|
||||
GBitmap* gbitmap_create_from_png_data(const uint8_t *png_data, size_t png_data_size) {return NULL;}
|
||||
|
||||
bool gbitmap_init_with_png_data(GBitmap *bitmap, const uint8_t *data, size_t data_size) {return true;}
|
||||
|
||||
uint8_t gbitmap_png_load_palette(upng_t *upng, GColor8 **palette) {return 0;}
|
||||
|
||||
bool gbitmap_png_is_format_supported(upng_t *upng) {return true;}
|
||||
|
||||
int32_t gbitmap_png_get_transparent_gray_value(upng_t *upng) {return -1;}
|
||||
|
||||
int32_t png_seek_chunk_in_resource(uint32_t resource_id, uint32_t offset,
|
||||
bool seek_framedata, bool *found_actl) {return 0;}
|
||||
|
||||
int32_t png_seek_chunk_in_resource_system(ResAppNum app_num, uint32_t resource_id, uint32_t offset,
|
||||
bool seek_framedata, bool *found_actl) {return 0;}
|
46
tests/fakes/fake_graphics_context.c
Normal file
46
tests/fakes/fake_graphics_context.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define FAKE_GRAPHICS_CONTEXT_C (1)
|
||||
|
||||
#include "fake_graphics_context.h"
|
||||
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
extern GContext *s_app_state_get_graphics_context;
|
||||
|
||||
static GContext s_ctx;
|
||||
static FrameBuffer s_fb;
|
||||
|
||||
GContext *graphics_context_get_current_context(void) {
|
||||
return &s_ctx;
|
||||
}
|
||||
|
||||
GContext *fake_graphics_context_get_context(void) {
|
||||
return &s_ctx;
|
||||
}
|
||||
|
||||
FrameBuffer *fake_graphics_context_get_framebuffer(void) {
|
||||
return &s_fb;
|
||||
}
|
||||
|
||||
void fake_graphics_context_init(void) {
|
||||
framebuffer_init(&s_fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
framebuffer_clear(&s_fb);
|
||||
graphics_context_init(&s_ctx, &s_fb, GContextInitializationMode_App);
|
||||
s_app_state_get_graphics_context = &s_ctx;
|
||||
}
|
39
tests/fakes/fake_graphics_context.h
Normal file
39
tests/fakes/fake_graphics_context.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#if !FAKE_GRAPHICS_CONTEXT_C
|
||||
#include "fw/graphics/util.h"
|
||||
#include "stubs_app_state.h"
|
||||
#endif
|
||||
|
||||
GContext *fake_graphics_context_get_context(void);
|
||||
|
||||
FrameBuffer *fake_graphics_context_get_framebuffer(void);
|
||||
|
||||
void fake_graphics_context_init(void);
|
||||
|
||||
#define FAKE_GRAPHICS_CONTEXT_CHECK_DEST_BITMAP(name) ({ \
|
||||
cl_check(gbitmap_pbi_eq(&fake_graphics_context_get_context()->dest_bitmap, name)); \
|
||||
})
|
||||
|
||||
#define FAKE_GRAPHICS_CONTEXT_CHECK_DEST_BITMAP_FILE() ({ \
|
||||
cl_check(gbitmap_pbi_eq(&fake_graphics_context_get_context()->dest_bitmap, TEST_PBI_FILE)); \
|
||||
})
|
192
tests/fakes/fake_kernel_malloc.h
Normal file
192
tests/fakes/fake_kernel_malloc.h
Normal file
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/list.h"
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
// Simple pass-through implementation of kernel_malloc/free that attempts to
|
||||
// protect against buffer overruns in tested code by adding a canary value to
|
||||
// the beginning and end of the allocated block, and verifying the value on
|
||||
// freeing of the block. It won't catch *all* memory errors of course, like
|
||||
// writing way outside of your bounds, or use-after-free, or neglecting to free.
|
||||
// But it should catch some of the simpler cases.
|
||||
|
||||
static const uint32_t s_malloc_canary = 0x54761F34;
|
||||
|
||||
static uint64_t s_largest_free_block_bytes = ~0;
|
||||
|
||||
static uint64_t s_heap_mark;
|
||||
|
||||
static bool s_stats_enabled = false;
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
size_t size;
|
||||
void *ptr;
|
||||
} Allocation;
|
||||
|
||||
Allocation *s_head;
|
||||
|
||||
void* kernel_malloc(size_t bytes) {
|
||||
if (bytes > s_largest_free_block_bytes) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* memory = malloc(bytes + 12);
|
||||
|
||||
memcpy(memory, &bytes, 4);
|
||||
memcpy(memory + 4, &s_malloc_canary, 4);
|
||||
memcpy(memory + bytes + 8, &s_malloc_canary, 4);
|
||||
|
||||
void* ptr = memory + 8;
|
||||
|
||||
if (s_stats_enabled) {
|
||||
Allocation *a = (Allocation *) malloc(sizeof(Allocation));
|
||||
*a = (const Allocation) {
|
||||
.size = bytes,
|
||||
.ptr = ptr,
|
||||
};
|
||||
s_head = (Allocation *) list_prepend((ListNode *) s_head, &a->node);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* kernel_zalloc(size_t bytes) {
|
||||
void *ptr = kernel_malloc(bytes);
|
||||
if (ptr) {
|
||||
memset(ptr, 0, bytes);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* kernel_zalloc_check(size_t bytes) {
|
||||
return kernel_zalloc(bytes);
|
||||
}
|
||||
|
||||
void* kernel_malloc_check(size_t bytes) {
|
||||
return kernel_malloc(bytes);
|
||||
}
|
||||
|
||||
char* kernel_strdup(const char* s) {
|
||||
char *r = kernel_malloc_check(strlen(s) + 1);
|
||||
if (!r) {
|
||||
return NULL;
|
||||
}
|
||||
strcpy(r, s);
|
||||
return r;
|
||||
}
|
||||
|
||||
char* kernel_strdup_check(const char* s) {
|
||||
return kernel_strdup(s);
|
||||
}
|
||||
|
||||
static bool prv_find_allocation_filter_cb(ListNode *found_node, void *data) {
|
||||
Allocation *a = (Allocation *) found_node;
|
||||
return (a->ptr == data);
|
||||
}
|
||||
|
||||
// Split into its own function to make it easy to set a breakpoint on it when debugging
|
||||
// using `./waf test --debug_test`
|
||||
static void prv_double_free_assert(Allocation *a) {
|
||||
cl_assert_(a != NULL, "Couldn't find allocation! Double free?");
|
||||
}
|
||||
|
||||
void kernel_free(void* ptr) {
|
||||
if (ptr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_stats_enabled) {
|
||||
Allocation *a = (Allocation *) list_find((ListNode *) s_head,
|
||||
prv_find_allocation_filter_cb, ptr);
|
||||
prv_double_free_assert(a);
|
||||
list_remove(&a->node, (ListNode **) &s_head, NULL);
|
||||
free(a);
|
||||
}
|
||||
|
||||
char* memory = (char*)ptr - 8;
|
||||
|
||||
uint32_t canary_start = -1;
|
||||
uint32_t canary_end = -1;
|
||||
uint32_t canary_length = -1;
|
||||
|
||||
memcpy(&canary_length, memory, 4);
|
||||
memcpy(&canary_start, memory + 4, 4);
|
||||
memcpy(&canary_end, memory + canary_length + 8, 4);
|
||||
|
||||
cl_assert(canary_start == s_malloc_canary);
|
||||
cl_assert(canary_length != -1);
|
||||
cl_assert(canary_end == s_malloc_canary);
|
||||
|
||||
free(memory);
|
||||
}
|
||||
|
||||
//! Enables or disables the tracking of allocations
|
||||
void fake_kernel_malloc_enable_stats(bool enable) {
|
||||
s_stats_enabled = enable;
|
||||
}
|
||||
|
||||
//! Returns the number of bytes allocated on the kernel heap.
|
||||
//! @note Call fake_kernel_malloc_enable_stats(true) before using this.
|
||||
uint64_t fake_kernel_malloc_get_total_bytes_allocated(void) {
|
||||
uint64_t bytes_allocated = 0;
|
||||
Allocation *a = s_head;
|
||||
while (a) {
|
||||
bytes_allocated += a->size;
|
||||
a = (Allocation *) a->node.next;
|
||||
}
|
||||
return bytes_allocated;
|
||||
}
|
||||
|
||||
//! Makes successive kernel_malloc() fail for sizes above the number of bytes specified.
|
||||
void fake_kernel_malloc_set_largest_free_block(uint64_t bytes) {
|
||||
s_largest_free_block_bytes = bytes;
|
||||
}
|
||||
|
||||
//! Marks the current, total bytes allocated.
|
||||
//! @see fake_kernel_malloc_mark_assert_equal
|
||||
void fake_kernel_malloc_mark(void) {
|
||||
s_heap_mark = fake_kernel_malloc_get_total_bytes_allocated();
|
||||
}
|
||||
|
||||
//! Asserts that the total bytes allocated is the same as the last time fake_kernel_malloc_mark()
|
||||
//! was called.
|
||||
void fake_kernel_malloc_mark_assert_equal(void) {
|
||||
cl_assert_equal_i(s_heap_mark, fake_kernel_malloc_get_total_bytes_allocated());
|
||||
}
|
||||
|
||||
void fake_kernel_malloc_init(void) {
|
||||
s_largest_free_block_bytes = ~0;
|
||||
s_heap_mark = 0;
|
||||
s_head = NULL;
|
||||
}
|
||||
|
||||
void fake_kernel_malloc_deinit(void) {
|
||||
Allocation *a = s_head;
|
||||
while (a) {
|
||||
Allocation *next = (Allocation *) a->node.next;
|
||||
free(a);
|
||||
a = next;
|
||||
}
|
||||
s_head = NULL;
|
||||
}
|
58
tests/fakes/fake_kernel_services_notifications.c
Normal file
58
tests/fakes/fake_kernel_services_notifications.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 "fake_kernel_services_notifications.h"
|
||||
|
||||
#include "services/normal/notifications/notification_storage.h"
|
||||
|
||||
static uint32_t s_ancs_count = 0;
|
||||
static uint32_t s_acted_upon_count = 0;
|
||||
|
||||
void notifications_handle_notification_added(Uuid *id) {
|
||||
++s_ancs_count;
|
||||
}
|
||||
|
||||
void notifications_handle_notification_removed(Uuid *id) {
|
||||
--s_ancs_count;
|
||||
}
|
||||
|
||||
void notifications_handle_notification_acted_upon(Uuid *id) {
|
||||
++s_acted_upon_count;
|
||||
return;
|
||||
}
|
||||
|
||||
void notifications_handle_notification_action_result() {
|
||||
}
|
||||
|
||||
void notifications_add_notification(TimelineItem *notification) {
|
||||
notification_storage_store(notification);
|
||||
++s_ancs_count;
|
||||
}
|
||||
|
||||
void fake_kernel_services_notifications_reset(void) {
|
||||
s_ancs_count = 0;
|
||||
s_acted_upon_count = 0;
|
||||
}
|
||||
|
||||
uint32_t fake_kernel_services_notifications_ancs_notifications_count(void) {
|
||||
return s_ancs_count;
|
||||
}
|
||||
|
||||
uint32_t fake_kernel_services_notifications_acted_upon_count(void) {
|
||||
return s_acted_upon_count;
|
||||
}
|
||||
|
||||
|
39
tests/fakes/fake_kernel_services_notifications.h
Normal file
39
tests/fakes/fake_kernel_services_notifications.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "services/normal/timeline/item.h"
|
||||
|
||||
void notifications_handle_ancs_message(TimelineItem *notification);
|
||||
|
||||
void notifications_handle_ancs_notification_removed(uint32_t ancs_uid);
|
||||
|
||||
void notifications_add_notification(TimelineItem *notification);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Fake manipulation:
|
||||
|
||||
//! Resets the fake (i.e. ANCS count)
|
||||
void fake_kernel_services_notifications_reset(void);
|
||||
|
||||
//! @return Number of times notifications_handle_ancs_message() was called.
|
||||
uint32_t fake_kernel_services_notifications_ancs_notifications_count(void);
|
||||
|
||||
//! @return Number of times notifications_handle_notification_acted_upon() was called.
|
||||
uint32_t fake_kernel_services_notifications_acted_upon_count(void);
|
18
tests/fakes/fake_light.c
Normal file
18
tests/fakes/fake_light.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
void light_enable_interaction(void) {
|
||||
}
|
81
tests/fakes/fake_logging.h
Normal file
81
tests/fakes/fake_logging.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <clar.h>
|
||||
|
||||
#include <regex.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <util/string.h>
|
||||
|
||||
// make sure we can unit-test log output
|
||||
#define CUSTOM_LOG_INTERNAL
|
||||
|
||||
const char **s_log_internal__expected;
|
||||
const char **s_log_internal__expected_regex;
|
||||
|
||||
static void log_internal(uint8_t log_level, const char* src_filename, int src_line_number,
|
||||
const char* fmt, va_list args) {
|
||||
// this implementation for log_internal constructs the logged string inside of a static buffer
|
||||
// so we can compare it against some test expectation
|
||||
|
||||
printf("%s:%d> ", GET_FILE_NAME(src_filename), src_line_number);
|
||||
|
||||
static char buffer[256];
|
||||
const int written = vsprintf(buffer, fmt, args);
|
||||
cl_assert(written < sizeof(buffer)); // there's no vsnprintf()
|
||||
buffer[written] = '\0';
|
||||
|
||||
// compare log message against array of expectations
|
||||
if (s_log_internal__expected) {
|
||||
if (*s_log_internal__expected == NULL) {
|
||||
cl_assert_equal_s("Did not expect another logged string, but got", buffer);
|
||||
cl_fail("Should only happen if the log statement exactly matches the message above.");
|
||||
}
|
||||
cl_assert_equal_s(*s_log_internal__expected, buffer);
|
||||
s_log_internal__expected++;
|
||||
} else if (s_log_internal__expected_regex) {
|
||||
if (*s_log_internal__expected_regex == NULL) {
|
||||
cl_assert_equal_s("Did not expect another logged string, but got", buffer);
|
||||
cl_fail("Should only happen if the log statement exactly matches the message above.");
|
||||
}
|
||||
regex_t regex = {};
|
||||
|
||||
// Compile regex:
|
||||
cl_assert_equal_i(0, regcomp(®ex, *s_log_internal__expected_regex, REG_EXTENDED));
|
||||
|
||||
// Match regex:
|
||||
const int rv = regexec(®ex, buffer, 0, NULL, 0);
|
||||
if (rv) {
|
||||
// Check REG_... #defines in regex.h for what these values mean.
|
||||
char msgbuf[256];
|
||||
char regexerr[128];
|
||||
regerror(rv, ®ex, regexerr, sizeof(regexerr));
|
||||
sprintf(msgbuf, "Regex match failed (rv=%i): %s\n \"%s\" didn't match pattern \"%s\"",
|
||||
rv, regexerr, buffer, *s_log_internal__expected_regex);
|
||||
cl_fail(msgbuf);
|
||||
}
|
||||
regfree(®ex);
|
||||
|
||||
s_log_internal__expected_regex++;
|
||||
}
|
||||
|
||||
printf("%s", buffer);
|
||||
printf("\n");
|
||||
}
|
34
tests/fakes/fake_memory_layout.h
Normal file
34
tests/fakes/fake_memory_layout.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 "drivers/mpu.h"
|
||||
|
||||
// Everything but NULL + 256
|
||||
const MpuRegion s_fake_app_region = { .region_num = 9, .enabled = true, .base_address = 256,
|
||||
.size = 0xFFFFFFFF - (1 << 8), .priv_read = true, .priv_write = true, .user_read = true, .user_write = true };
|
||||
|
||||
const MpuRegion* memory_layout_get_app_region(void) {
|
||||
return &s_fake_app_region;
|
||||
}
|
||||
|
||||
bool memory_layout_is_pointer_in_region(const MpuRegion *region, const void *ptr) {
|
||||
uintptr_t p = (uintptr_t) ptr;
|
||||
return (p >= region->base_address && p < (region->base_address + region->size));
|
||||
}
|
||||
|
||||
bool memory_layout_is_buffer_in_region(const MpuRegion *region, const void *buf, size_t length) {
|
||||
return memory_layout_is_pointer_in_region(region, buf) && memory_layout_is_pointer_in_region(region, buf + length);
|
||||
}
|
195
tests/fakes/fake_mutex.h
Normal file
195
tests/fakes/fake_mutex.h
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "system/passert.h"
|
||||
#include "util/list.h"
|
||||
|
||||
#include <os/mutex.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct FakePebbleMutex {
|
||||
ListNode node;
|
||||
uint8_t lock_count;
|
||||
bool recursive;
|
||||
} FakePebbleMutex;
|
||||
|
||||
static FakePebbleMutex *s_mutex_list;
|
||||
static bool s_asserts_disabled;
|
||||
static bool s_assert_triggered;
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
static void *prv_mutex_create(bool is_recursive) {
|
||||
FakePebbleMutex *mutex = malloc(sizeof(FakePebbleMutex));
|
||||
*mutex = (FakePebbleMutex) {
|
||||
.recursive = is_recursive,
|
||||
};
|
||||
s_mutex_list = (FakePebbleMutex *)list_prepend((ListNode *)s_mutex_list, (ListNode *)mutex);
|
||||
return mutex;
|
||||
}
|
||||
|
||||
static bool prv_list_foreach_assert_unlocked(ListNode *node, void *context) {
|
||||
FakePebbleMutex *mutex = (FakePebbleMutex *)node;
|
||||
bool *failed = context;
|
||||
if (mutex->lock_count != 0) {
|
||||
// If this is failing, set your breakpoint here to find out which mutex
|
||||
printf("Mutex (0x%lx) was not unlocked when fake_mutex_assert_all_unlocked called\n",
|
||||
(unsigned long)mutex);
|
||||
s_assert_triggered = true;
|
||||
cl_assert(s_asserts_disabled);
|
||||
*failed = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Fake Mutex API
|
||||
//
|
||||
|
||||
void fake_mutex_reset(bool assert_all_unlocked) {
|
||||
ListNode *iter = (ListNode *)s_mutex_list;
|
||||
while (iter) {
|
||||
ListNode *next = iter->next;
|
||||
if (assert_all_unlocked) {
|
||||
FakePebbleMutex *mutex = (FakePebbleMutex *)iter;
|
||||
cl_assert_equal_i(0, mutex->lock_count);
|
||||
}
|
||||
free(iter);
|
||||
iter = next;
|
||||
}
|
||||
s_mutex_list = NULL;
|
||||
|
||||
s_asserts_disabled = false;
|
||||
s_assert_triggered = false;
|
||||
}
|
||||
|
||||
void fake_mutex_assert_all_unlocked(void) {
|
||||
bool failed;
|
||||
list_foreach((ListNode *)s_mutex_list, prv_list_foreach_assert_unlocked, &failed);
|
||||
}
|
||||
|
||||
//
|
||||
// Mutex API
|
||||
//
|
||||
|
||||
PebbleMutex *mutex_create(void) {
|
||||
const bool is_recursive = false;
|
||||
return prv_mutex_create(is_recursive);
|
||||
}
|
||||
|
||||
void mutex_destroy(PebbleMutex *handle) {
|
||||
FakePebbleMutex *mutex = (FakePebbleMutex *)handle;
|
||||
list_remove((ListNode *)mutex, (ListNode **)&s_mutex_list, NULL);
|
||||
}
|
||||
|
||||
void mutex_lock(PebbleMutex *handle) {
|
||||
FakePebbleMutex *mutex = (FakePebbleMutex *)handle;
|
||||
if (mutex->lock_count != 0) {
|
||||
s_assert_triggered = true;
|
||||
cl_assert_(s_asserts_disabled, "mutex_lock called with mutex that was already locked");
|
||||
}
|
||||
mutex->lock_count++;
|
||||
}
|
||||
|
||||
bool mutex_lock_with_timeout(PebbleMutex *handle, uint32_t timeout_ms) {
|
||||
mutex_lock(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mutex_lock_with_lr(PebbleMutex *handle, uint32_t myLR) {
|
||||
mutex_lock(handle);
|
||||
}
|
||||
|
||||
void mutex_unlock(PebbleMutex *handle) {
|
||||
FakePebbleMutex *mutex = (FakePebbleMutex *)handle;
|
||||
if (mutex->lock_count != 1) {
|
||||
s_assert_triggered = true;
|
||||
cl_assert_(s_asserts_disabled, "mutex_unlock called with mutex that was not locked");
|
||||
}
|
||||
mutex->lock_count--;
|
||||
}
|
||||
|
||||
PebbleRecursiveMutex * mutex_create_recursive(void) {
|
||||
const bool is_recursive = true;
|
||||
return prv_mutex_create(is_recursive);
|
||||
}
|
||||
|
||||
void mutex_lock_recursive(PebbleRecursiveMutex *handle) {
|
||||
FakePebbleMutex *mutex = (FakePebbleMutex *)handle;
|
||||
mutex->lock_count++;
|
||||
}
|
||||
|
||||
bool mutex_lock_recursive_with_timeout(PebbleRecursiveMutex *handle, uint32_t timeout_ms) {
|
||||
mutex_lock_recursive(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mutex_lock_recursive_with_timeout_and_lr(PebbleRecursiveMutex *handle,
|
||||
uint32_t timeout_ms,
|
||||
uint32_t LR) {
|
||||
mutex_lock_recursive(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Tests if a given mutex is owned by the current task. Useful for
|
||||
//! ensuring locks are held when they should be.
|
||||
bool mutex_is_owned_recursive(PebbleRecursiveMutex *handle) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void mutex_unlock_recursive(PebbleRecursiveMutex *handle) {
|
||||
FakePebbleMutex *mutex = (FakePebbleMutex *)handle;
|
||||
if (mutex->lock_count == 0) {
|
||||
s_assert_triggered = true;
|
||||
cl_assert_(s_asserts_disabled,
|
||||
"mutex_unlock_recursive called when lock count not greater than 0");
|
||||
}
|
||||
mutex->lock_count--;
|
||||
}
|
||||
|
||||
//! @return true if the calling task owns the mutex
|
||||
void mutex_assert_held_by_curr_task(PebbleMutex *handle, bool is_held) {
|
||||
return;
|
||||
}
|
||||
|
||||
//! @return true if the calling task owns the mutex
|
||||
void mutex_assert_recursive_held_by_curr_task(PebbleRecursiveMutex *handle, bool is_held) {
|
||||
return;
|
||||
}
|
||||
|
||||
// PRIVATE: Only used for testing this module
|
||||
void fake_mutex_set_should_assert(bool should) {
|
||||
s_asserts_disabled = !should;
|
||||
}
|
||||
|
||||
bool fake_mutex_get_assert_triggered(void) {
|
||||
return s_assert_triggered;
|
||||
}
|
||||
|
||||
bool fake_mutex_all_unlocked(void) {
|
||||
bool failed;
|
||||
s_asserts_disabled = true;
|
||||
list_foreach((ListNode *)s_mutex_list, prv_list_foreach_assert_unlocked, &failed);
|
||||
s_asserts_disabled = false;
|
||||
return !failed;
|
||||
}
|
320
tests/fakes/fake_new_timer.h
Normal file
320
tests/fakes/fake_new_timer.h
Normal file
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fake_pbl_malloc.h"
|
||||
|
||||
#include "services/common/new_timer/new_timer.h"
|
||||
#include "util/list.h"
|
||||
#include "drivers/rtc.h"
|
||||
#include "system/passert.h"
|
||||
#include <stdio.h>
|
||||
|
||||
// Structure of a timer
|
||||
typedef struct StubTimer {
|
||||
//! Entry into either the running timers (s_running_timers) list or the idle timers
|
||||
//! (s_idle_timers)
|
||||
ListNode list_node;
|
||||
|
||||
TimerID id; //<! ID assigned to this timer
|
||||
|
||||
//! client provided callback function and argument
|
||||
NewTimerCallback cb;
|
||||
void* cb_data;
|
||||
|
||||
//! The tick value when this timer will expire (in milliseconds). If the timer isn't currently
|
||||
//! running (scheduled) this value will be zero.
|
||||
uint32_t timeout_ms;
|
||||
|
||||
//! True if this timer should automatically be rescheduled for period_ms from now
|
||||
bool repeating;
|
||||
uint32_t period_ms;
|
||||
|
||||
//! True if this timer is currently having its callback executed.
|
||||
bool executing;
|
||||
|
||||
//! Set by the delete function of client tries to delete a timer currently executing it's callback
|
||||
bool defer_delete;
|
||||
|
||||
} StubTimer;
|
||||
|
||||
|
||||
|
||||
// =============================================================================================
|
||||
// Stubs
|
||||
static ListNode *s_running_timers = NULL;
|
||||
static ListNode *s_idle_timers = NULL;
|
||||
|
||||
// Call counters
|
||||
static int s_num_new_timer_create_calls = 0;
|
||||
static int s_num_new_timer_start_calls = 0;
|
||||
static int s_num_new_timer_stop_calls = 0;
|
||||
static int s_num_new_timer_delete_calls = 0;
|
||||
static int s_num_new_timer_schedule_calls = 0;
|
||||
|
||||
// Last parameters
|
||||
static TimerID s_new_timer_start_param_timer_id;
|
||||
static uint32_t s_new_timer_start_param_timeout_ms;
|
||||
static NewTimerCallback s_new_timer_start_param_cb;
|
||||
static void * s_new_timer_start_param_cb_data;
|
||||
|
||||
// Debug utility
|
||||
/*
|
||||
static void prv_print_idle_list(char* title) {
|
||||
printf("%s IDLE LIST:\n", title);
|
||||
StubTimer *node = (StubTimer *) s_idle_timers;
|
||||
while (node) {
|
||||
StubTimer *next = (StubTimer *) list_get_next(&node->list_node);
|
||||
printf(" %p\n", node);
|
||||
if (node == next) {
|
||||
printf("RECURSIVE!!!\n");
|
||||
return;
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
static bool prv_id_list_filter(ListNode* node, void* data) {
|
||||
StubTimer* timer = (StubTimer*)node;
|
||||
return timer->id == (uint32_t) data;
|
||||
}
|
||||
|
||||
static StubTimer* prv_find_timer(TimerID timer_id)
|
||||
{
|
||||
// Look for this timer in either the running or idle list
|
||||
ListNode* node = list_find(s_running_timers, prv_id_list_filter, (void*)(intptr_t)timer_id);
|
||||
if (!node) {
|
||||
node = list_find(s_idle_timers, prv_id_list_filter, (void*)(intptr_t)timer_id);
|
||||
}
|
||||
return (StubTimer *)node;
|
||||
}
|
||||
|
||||
static int prv_timer_expire_compare_func(void* a, void* b) {
|
||||
return (((StubTimer*)b)->timeout_ms - ((StubTimer*)a)->timeout_ms);
|
||||
}
|
||||
|
||||
|
||||
static int stub_new_timer_create(void) {
|
||||
StubTimer *timer = (StubTimer *) kernel_malloc(sizeof(StubTimer));
|
||||
static int s_next_timer_id = 1;
|
||||
*timer = (StubTimer) {
|
||||
.id = s_next_timer_id++,
|
||||
};
|
||||
s_idle_timers = list_insert_before(s_idle_timers, &timer->list_node);
|
||||
return timer->id;
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
// Stub manipulation:
|
||||
//
|
||||
bool stub_new_timer_start(TimerID timer_id, uint32_t timeout_ms, NewTimerCallback cb, void *cb_data,
|
||||
uint32_t flags) {
|
||||
StubTimer* timer = prv_find_timer(timer_id);
|
||||
|
||||
// Remove it from its current list
|
||||
if (list_contains(s_running_timers, &timer->list_node)) {
|
||||
list_remove(&timer->list_node, &s_running_timers /* &head */, NULL /* &tail */);
|
||||
} else {
|
||||
list_remove(&timer->list_node, &s_idle_timers /* &head */, NULL /* &tail */);
|
||||
}
|
||||
|
||||
// Set timer variables
|
||||
timer->cb = cb;
|
||||
timer->cb_data = cb_data;
|
||||
timer->timeout_ms = timeout_ms;
|
||||
timer->repeating = flags & TIMER_START_FLAG_REPEATING;
|
||||
timer->period_ms = timeout_ms;
|
||||
|
||||
// Insert into sorted order in the running list
|
||||
s_running_timers = list_sorted_add(s_running_timers, &timer->list_node,
|
||||
prv_timer_expire_compare_func, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stub_new_timer_stop(TimerID timer_id) {
|
||||
StubTimer* timer = prv_find_timer(timer_id);
|
||||
|
||||
// Move it to the idle list if it's currently running
|
||||
if (list_contains(s_running_timers, &timer->list_node)) {
|
||||
list_remove(&timer->list_node, &s_running_timers /* &head */, NULL /* &tail */);
|
||||
s_idle_timers = list_insert_before(s_idle_timers, &timer->list_node);
|
||||
}
|
||||
|
||||
// Clear the repeating flag so that if they call this method from a callback it won't get
|
||||
// rescheduled
|
||||
timer->repeating = false;
|
||||
timer->timeout_ms = 0;
|
||||
return !timer->executing;
|
||||
}
|
||||
|
||||
void stub_new_timer_delete(TimerID timer_id) {
|
||||
StubTimer* timer = prv_find_timer(timer_id);
|
||||
|
||||
// Automatically stop it if it it's not stopped already
|
||||
if (list_contains(s_running_timers, &timer->list_node)) {
|
||||
timer->timeout_ms = 0;
|
||||
list_remove(&timer->list_node, &s_running_timers /* &head */, NULL /* &tail */);
|
||||
s_idle_timers = list_insert_before(s_idle_timers, &timer->list_node);
|
||||
}
|
||||
timer->repeating = false; // In case it's currently executing, make sure we don't reschedule it
|
||||
|
||||
if (!timer->executing) {
|
||||
list_remove(&timer->list_node, &s_idle_timers /* &head */, NULL /* &tail */);
|
||||
kernel_free(timer);
|
||||
} else {
|
||||
timer->defer_delete = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool stub_new_timer_is_scheduled(TimerID timer_id) {
|
||||
StubTimer* timer = prv_find_timer(timer_id);
|
||||
if (timer == NULL) {
|
||||
return false;
|
||||
}
|
||||
return list_contains(s_running_timers, &timer->list_node);
|
||||
}
|
||||
|
||||
uint32_t stub_new_timer_timeout(TimerID timer_id) {
|
||||
StubTimer* timer = prv_find_timer(timer_id);
|
||||
if (timer == NULL) {
|
||||
return false;
|
||||
}
|
||||
return timer->timeout_ms;
|
||||
}
|
||||
|
||||
// Mark the timer as executing. This prevents it from getting deleted. In the real implementation,
|
||||
// it would get deleted after it's callback returned
|
||||
void stub_new_timer_set_executing(TimerID timer_id, bool set) {
|
||||
StubTimer* timer = prv_find_timer(timer_id);
|
||||
PBL_ASSERTN(timer != NULL);
|
||||
timer->executing = true;
|
||||
}
|
||||
|
||||
void * stub_new_timer_callback_data(TimerID timer_id) {
|
||||
StubTimer* timer = prv_find_timer(timer_id);
|
||||
if (timer == NULL) {
|
||||
return false;
|
||||
}
|
||||
return timer->cb_data;
|
||||
}
|
||||
|
||||
bool stub_new_timer_fire(TimerID timer_id) {
|
||||
StubTimer* timer = prv_find_timer(timer_id);
|
||||
if (timer == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (list_contains(s_running_timers, &timer->list_node)) {
|
||||
list_remove(&timer->list_node, &s_running_timers /* &head */, NULL /* &tail */);
|
||||
s_idle_timers = list_insert_before(s_idle_timers, &timer->list_node);
|
||||
} else {
|
||||
printf("WARNING: Attempted to fire a non-running timer\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
timer->timeout_ms = 0;
|
||||
timer->executing = true;
|
||||
timer->cb(timer->cb_data);
|
||||
timer->executing = false;
|
||||
|
||||
if (timer->defer_delete) {
|
||||
// Timer was deleted from the callback, clean it up now:
|
||||
list_remove(&timer->list_node, &s_idle_timers /* &head */, NULL /* &tail */);
|
||||
kernel_free(timer);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (timer->repeating && timer->timeout_ms == 0) {
|
||||
stub_new_timer_start(timer_id, timer->period_ms, timer->cb, timer->cb_data,
|
||||
TIMER_START_FLAG_REPEATING);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void stub_new_timer_cleanup(void) {
|
||||
StubTimer *node = (StubTimer *) s_running_timers;
|
||||
while (node) {
|
||||
StubTimer *next = (StubTimer *) list_get_next(&node->list_node);
|
||||
list_remove(&node->list_node, &s_running_timers, NULL);
|
||||
kernel_free(node);
|
||||
node = next;
|
||||
}
|
||||
PBL_ASSERTN(s_running_timers == NULL);
|
||||
|
||||
node = (StubTimer *) s_idle_timers;
|
||||
while (node) {
|
||||
StubTimer *next = (StubTimer *) list_get_next(&node->list_node);
|
||||
list_remove(&node->list_node, &s_idle_timers, NULL);
|
||||
kernel_free(node);
|
||||
node = next;
|
||||
}
|
||||
PBL_ASSERTN(s_idle_timers == NULL);
|
||||
}
|
||||
|
||||
TimerID stub_new_timer_get_next(void) {
|
||||
StubTimer *timer = (StubTimer *) list_get_head(s_running_timers);
|
||||
return timer ? timer->id : TIMER_INVALID_ID;
|
||||
}
|
||||
|
||||
void stub_new_timer_invoke(int num_to_invoke) {
|
||||
TimerID timer = stub_new_timer_get_next();
|
||||
while (timer != TIMER_INVALID_ID && num_to_invoke--) {
|
||||
stub_new_timer_fire(timer);
|
||||
timer = stub_new_timer_get_next();
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================================
|
||||
// Fakes
|
||||
|
||||
// Create a new timer
|
||||
TimerID new_timer_create(void) {
|
||||
s_num_new_timer_create_calls++;
|
||||
return stub_new_timer_create();
|
||||
}
|
||||
|
||||
// Start a timer
|
||||
bool new_timer_start(TimerID timer_id, uint32_t timeout_ms, NewTimerCallback cb, void *cb_data,
|
||||
uint32_t flags) {
|
||||
s_num_new_timer_start_calls++;
|
||||
s_new_timer_start_param_timer_id = timer_id;
|
||||
s_new_timer_start_param_timeout_ms = timeout_ms;
|
||||
s_new_timer_start_param_cb = cb;
|
||||
s_new_timer_start_param_cb_data = cb_data;
|
||||
return stub_new_timer_start(timer_id, timeout_ms, cb, cb_data, flags);
|
||||
}
|
||||
|
||||
// Stop a timer
|
||||
bool new_timer_stop(TimerID timer_id) {
|
||||
s_num_new_timer_stop_calls++;
|
||||
return stub_new_timer_stop(timer_id);
|
||||
}
|
||||
|
||||
// Delete a timer
|
||||
void new_timer_delete(TimerID timer_id) {
|
||||
s_num_new_timer_delete_calls++;
|
||||
stub_new_timer_delete(timer_id);
|
||||
}
|
||||
|
||||
bool new_timer_scheduled(TimerID timer, uint32_t *expire_ms_p) {
|
||||
s_num_new_timer_schedule_calls++;
|
||||
return stub_new_timer_is_scheduled(timer);
|
||||
}
|
||||
|
125
tests/fakes/fake_notification_storage.h
Normal file
125
tests/fakes/fake_notification_storage.h
Normal 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 "services/normal/timeline/item.h"
|
||||
|
||||
static TimelineItem s_last_stored_notification = {};
|
||||
static int s_notification_store_count = 0;
|
||||
static int s_notification_remove_count = 0;
|
||||
static TimelineItem s_existing_ancs_notification = {
|
||||
.header = (CommonTimelineItemHeader) {
|
||||
.id = UUID_INVALID,
|
||||
.ancs_uid = 0
|
||||
}
|
||||
};
|
||||
|
||||
extern T_STATIC bool prv_deep_copy_attributes_actions(AttributeList *attr_list,
|
||||
TimelineItemActionGroup *action_group,
|
||||
TimelineItem *item_out);
|
||||
|
||||
void fake_notification_storage_reset(void) {
|
||||
s_notification_store_count = 0;
|
||||
s_notification_remove_count = 0;
|
||||
s_existing_ancs_notification = (TimelineItem) {
|
||||
.header = (CommonTimelineItemHeader) {
|
||||
.id = UUID_INVALID,
|
||||
.ancs_uid = 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TimelineItem *fake_notification_storage_get_last_notification(void) {
|
||||
return &s_last_stored_notification;
|
||||
}
|
||||
|
||||
int fake_notification_storage_get_store_count(void) {
|
||||
return s_notification_store_count;
|
||||
}
|
||||
|
||||
int fake_notification_storage_get_remove_count(void) {
|
||||
return s_notification_remove_count;
|
||||
}
|
||||
|
||||
void fake_notification_storage_set_existing_ancs_notification(Uuid *uuid, uint32_t ancs_uid) {
|
||||
s_existing_ancs_notification = (TimelineItem) {
|
||||
.header = (CommonTimelineItemHeader) {
|
||||
.id = *uuid,
|
||||
.ancs_uid = ancs_uid
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void notification_storage_init(void) {
|
||||
}
|
||||
|
||||
void notification_storage_lock(void) {
|
||||
}
|
||||
|
||||
void notification_storage_unlock(void) {
|
||||
}
|
||||
|
||||
void notification_storage_store(TimelineItem *notification) {
|
||||
++s_notification_store_count;
|
||||
|
||||
// Copy notification into our last stored buffer
|
||||
timeline_item_free_allocated_buffer(&s_last_stored_notification);
|
||||
s_last_stored_notification = *notification;
|
||||
if (!prv_deep_copy_attributes_actions(¬ification->attr_list, ¬ification->action_group,
|
||||
&s_last_stored_notification)) {
|
||||
s_last_stored_notification = (TimelineItem){};
|
||||
}
|
||||
}
|
||||
|
||||
bool notification_storage_notification_exists(const Uuid *id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t notification_storage_get_len(const Uuid *uuid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool notification_storage_get(const Uuid *id, TimelineItem *item_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void notification_storage_set_status(const Uuid *id, uint8_t status) {
|
||||
}
|
||||
|
||||
bool notification_storage_get_status(const Uuid *id, uint8_t *status) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void notification_storage_remove(const Uuid *id) {
|
||||
++s_notification_remove_count;
|
||||
}
|
||||
|
||||
bool notification_storage_find_ancs_notification_id(uint32_t ancs_uid, Uuid *uuid_out) {
|
||||
*uuid_out = s_existing_ancs_notification.header.id;
|
||||
return (s_existing_ancs_notification.header.ancs_uid == ancs_uid);
|
||||
}
|
||||
|
||||
bool notification_storage_find_ancs_notification_by_timestamp(
|
||||
TimelineItem *notification, CommonTimelineItemHeader *header_out) {
|
||||
if (uuid_is_invalid(&s_existing_ancs_notification.header.id)) {
|
||||
return false;
|
||||
}
|
||||
*header_out = s_existing_ancs_notification.header;
|
||||
return true;
|
||||
}
|
||||
|
||||
void notification_storage_rewrite(void (*iter_callback)(TimelineItem *notification,
|
||||
SerializedTimelineItemHeader *header, void *data), void *data) {
|
||||
}
|
62
tests/fakes/fake_otp.c
Normal file
62
tests/fakes/fake_otp.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 "fake_otp.h"
|
||||
|
||||
#include "drivers/otp.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
char s_otp_buffer[512];
|
||||
uint8_t s_otp_locks[16];
|
||||
|
||||
void fake_otp_reset(void) {
|
||||
memset(s_otp_buffer, 0xff, sizeof(s_otp_buffer));
|
||||
memset(s_otp_locks, 0xff, sizeof(s_otp_locks));
|
||||
}
|
||||
|
||||
char * otp_get_slot(const uint8_t index) {
|
||||
PBL_ASSERTN(index < NUM_OTP_SLOTS);
|
||||
return (char * const) (s_otp_buffer + (32 * index));
|
||||
}
|
||||
|
||||
uint8_t * otp_get_lock(const uint8_t index) {
|
||||
PBL_ASSERTN(index < NUM_OTP_SLOTS);
|
||||
return (uint8_t * const) (s_otp_locks + index);
|
||||
}
|
||||
|
||||
bool otp_is_locked(const uint8_t index) {
|
||||
return (*otp_get_lock(index) == 0);
|
||||
}
|
||||
|
||||
OtpWriteResult otp_write_slot(const uint8_t index, const char *value) {
|
||||
if (otp_is_locked(index)) {
|
||||
return OtpWriteFailAlreadyWritten;
|
||||
}
|
||||
|
||||
// Write the value
|
||||
char *slot = otp_get_slot(index);
|
||||
strcpy(slot, value);
|
||||
|
||||
// Lock the OTP sector
|
||||
uint8_t *lock = otp_get_lock(index);
|
||||
*lock = 0;
|
||||
|
||||
return OtpWriteSuccess;
|
||||
}
|
19
tests/fakes/fake_otp.h
Normal file
19
tests/fakes/fake_otp.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void fake_otp_reset(void);
|
238
tests/fakes/fake_pbl_malloc.h
Normal file
238
tests/fakes/fake_pbl_malloc.h
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <util/heap.h>
|
||||
#include "util/list.h"
|
||||
#include "util/math.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
ListNode list_node;
|
||||
size_t bytes;
|
||||
void *ptr;
|
||||
void *lr;
|
||||
} PointerListNode;
|
||||
|
||||
static PointerListNode *s_pointer_list = NULL;
|
||||
|
||||
static bool prv_pointer_list_filter(ListNode *node, void *ptr) {
|
||||
return ((PointerListNode *)node)->ptr == ptr;
|
||||
}
|
||||
|
||||
static void prv_pointer_list_add(void *ptr, size_t bytes, void *lr) {
|
||||
PointerListNode *node = malloc(sizeof(PointerListNode));
|
||||
list_init(&node->list_node);
|
||||
node->ptr = ptr;
|
||||
node->bytes = bytes;
|
||||
node->lr = lr;
|
||||
s_pointer_list = (PointerListNode *)list_prepend((ListNode *)s_pointer_list, &node->list_node);
|
||||
}
|
||||
|
||||
static void prv_pointer_list_remove(void *ptr) {
|
||||
ListNode *node = list_find((ListNode *)s_pointer_list, prv_pointer_list_filter, ptr);
|
||||
if (!node && ptr) {
|
||||
printf("*** INVALID FREE: %p\n", ptr);
|
||||
cl_fail("Pointer has not been alloc'd (maybe a double free?)");
|
||||
}
|
||||
|
||||
list_remove(node, (ListNode **)&s_pointer_list, NULL);
|
||||
free(node);
|
||||
}
|
||||
|
||||
static size_t s_max_size_allowed = ~0;
|
||||
|
||||
static Heap s_heap;
|
||||
Heap *task_heap_get_for_current_task(void) {
|
||||
return &s_heap;
|
||||
}
|
||||
|
||||
static void *malloc_and_track(size_t bytes, void *lr) {
|
||||
if (bytes >= s_max_size_allowed) {
|
||||
return NULL;
|
||||
}
|
||||
void *rt = malloc(bytes);
|
||||
prv_pointer_list_add(rt, bytes, lr);
|
||||
return rt;
|
||||
}
|
||||
|
||||
static void *calloc_and_track(int n, size_t bytes, void *lr) {
|
||||
if ((bytes * n) >= s_max_size_allowed) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *rt = calloc(n, bytes);
|
||||
prv_pointer_list_add(rt, bytes, lr);
|
||||
return rt;
|
||||
}
|
||||
|
||||
void fake_malloc_set_largest_free_block(size_t bytes) {
|
||||
s_max_size_allowed = bytes;
|
||||
}
|
||||
|
||||
static void free_and_track(void *ptr) {
|
||||
prv_pointer_list_remove(ptr);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void *realloc_and_track(void *ptr, size_t bytes, void *lr) {
|
||||
void *new_ptr = malloc_and_track(bytes, lr);
|
||||
if (new_ptr && ptr) {
|
||||
ListNode *node = list_find((ListNode *)s_pointer_list, prv_pointer_list_filter, ptr);
|
||||
cl_assert(node);
|
||||
memcpy(new_ptr, ptr, MIN(((PointerListNode*)node)->bytes, bytes));
|
||||
free_and_track(ptr);
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
int fake_pbl_malloc_num_net_allocs(void) {
|
||||
return list_count((ListNode *)s_pointer_list);
|
||||
}
|
||||
|
||||
void fake_pbl_malloc_check_net_allocs(void) {
|
||||
if (fake_pbl_malloc_num_net_allocs() > 0) {
|
||||
ListNode *node = (ListNode *)s_pointer_list;
|
||||
while (node) {
|
||||
PointerListNode *ptr_node = (PointerListNode *)node;
|
||||
printf("Still allocated: %p (%zu bytes, lr %p)\n",
|
||||
ptr_node->ptr, ptr_node->bytes, ptr_node->lr);
|
||||
node = list_get_next(node);
|
||||
}
|
||||
}
|
||||
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0);
|
||||
}
|
||||
|
||||
void fake_pbl_malloc_clear_tracking(void) {
|
||||
while (s_pointer_list) {
|
||||
ListNode *new_head = list_pop_head((ListNode *)s_pointer_list);
|
||||
free(s_pointer_list);
|
||||
s_pointer_list = (PointerListNode *)new_head;
|
||||
}
|
||||
s_max_size_allowed = ~0;
|
||||
}
|
||||
|
||||
void *task_malloc(size_t bytes) {
|
||||
return malloc_and_track(bytes, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void *task_malloc_check(size_t bytes) {
|
||||
return malloc_and_track(bytes, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void *task_realloc(void *ptr, size_t bytes) {
|
||||
return realloc_and_track(ptr, bytes, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void *task_zalloc(size_t bytes) {
|
||||
void *ptr = task_malloc(bytes);
|
||||
if (ptr) {
|
||||
memset(ptr, 0, bytes);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *task_zalloc_check(size_t bytes) {
|
||||
void *ptr = task_malloc_check(bytes);
|
||||
memset(ptr, 0, bytes);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *task_calloc(size_t count, size_t size) {
|
||||
return calloc_and_track(count, size, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void *task_calloc_check(size_t count, size_t size) {
|
||||
return calloc_and_track(count, size, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void task_free(void *ptr) {
|
||||
free_and_track(ptr);
|
||||
}
|
||||
|
||||
void *applib_zalloc(size_t bytes) {
|
||||
return calloc_and_track(1, bytes, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void applib_free(void *ptr) {
|
||||
free_and_track(ptr);
|
||||
}
|
||||
|
||||
void *app_malloc(size_t bytes) {
|
||||
return malloc_and_track(bytes, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void *app_malloc_check(size_t bytes) {
|
||||
return malloc_and_track(bytes, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void app_free(void *ptr) {
|
||||
free_and_track(ptr);
|
||||
}
|
||||
|
||||
void *kernel_malloc(size_t bytes) {
|
||||
return malloc_and_track(bytes, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void *kernel_zalloc(size_t bytes) {
|
||||
return calloc_and_track(1, bytes, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void *kernel_zalloc_check(size_t bytes) {
|
||||
return kernel_zalloc(bytes);
|
||||
}
|
||||
|
||||
void *kernel_malloc_check(size_t bytes) {
|
||||
return malloc_and_track(bytes, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void *kernel_realloc(void *ptr, size_t bytes) {
|
||||
return realloc_and_track(ptr, bytes, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void kernel_free(void *ptr) {
|
||||
free_and_track(ptr);
|
||||
}
|
||||
|
||||
void* kernel_calloc(size_t count, size_t size) {
|
||||
return calloc_and_track(count, size, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
char* kernel_strdup(const char* s) {
|
||||
char *r = malloc_and_track(strlen(s) + 1, __builtin_return_address(0));
|
||||
if (!r) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(r, s);
|
||||
return r;
|
||||
}
|
||||
|
||||
char* kernel_strdup_check(const char* s) {
|
||||
return kernel_strdup(s);
|
||||
}
|
||||
|
||||
char* task_strdup(const char* s) {
|
||||
return kernel_strdup(s);
|
||||
}
|
||||
|
||||
void smart_free(void *ptr) {
|
||||
free_and_track(ptr);
|
||||
}
|
36
tests/fakes/fake_pbl_std.h
Normal file
36
tests/fakes/fake_pbl_std.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "util/time/time.h"
|
||||
|
||||
|
||||
struct tm *pbl_override_localtime(const time_t *timep) {
|
||||
static struct tm local_tm;
|
||||
localtime_r(timep, &local_tm);
|
||||
return &local_tm;
|
||||
}
|
||||
|
||||
struct tm *pbl_override_gmtime(const time_t *timep) {
|
||||
static struct tm local_tm;
|
||||
gmtime_r(timep, &local_tm);
|
||||
return &local_tm;
|
||||
}
|
||||
|
||||
time_t pbl_override_mktime(struct tm *tb) {
|
||||
return mktime(tb);
|
||||
}
|
||||
|
39
tests/fakes/fake_pebble_tasks.h
Normal file
39
tests/fakes/fake_pebble_tasks.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stubs_worker_manager.h"
|
||||
|
||||
#include "kernel/pebble_tasks.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
static PebbleTask s_current_task = PebbleTask_KernelMain;
|
||||
|
||||
PebbleTask pebble_task_get_current(void) {
|
||||
return s_current_task;
|
||||
}
|
||||
|
||||
void stub_pebble_tasks_set_current(PebbleTask task) {
|
||||
s_current_task = task;
|
||||
}
|
||||
|
||||
const char* pebble_task_get_name(PebbleTask task) {
|
||||
return "App <Stub>";
|
||||
}
|
||||
|
42
tests/fakes/fake_property_animation.c
Normal file
42
tests/fakes/fake_property_animation.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 "applib/ui/property_animation_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
static const PropertyAnimationImplementation s_frame_layer_implementation = {
|
||||
.accessors = {
|
||||
.setter.grect = (const GRectSetter)layer_set_frame_by_value,
|
||||
.getter.grect = (const GRectGetter)layer_get_frame_by_value,
|
||||
},
|
||||
};
|
||||
|
||||
PropertyAnimation *WEAK property_animation_create_layer_frame(
|
||||
struct Layer *layer, GRect *from_frame, GRect *to_frame) {
|
||||
PropertyAnimationPrivate *animation = (PropertyAnimationPrivate *)
|
||||
property_animation_create(&s_frame_layer_implementation, layer, from_frame, to_frame);
|
||||
if (from_frame) {
|
||||
animation->values.from.grect = *from_frame;
|
||||
PropertyAnimationImplementation *impl =
|
||||
(PropertyAnimationImplementation *)animation->animation.implementation;
|
||||
impl->accessors.setter.grect(animation->subject, animation->values.from.grect);
|
||||
}
|
||||
if (to_frame) {
|
||||
animation->values.to.grect = *to_frame;
|
||||
}
|
||||
return (PropertyAnimation *)animation;
|
||||
}
|
||||
|
141
tests/fakes/fake_put_bytes_storage_mem.c
Normal file
141
tests/fakes/fake_put_bytes_storage_mem.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* 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 "fake_put_bytes_storage_mem.h"
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "services/common/put_bytes/put_bytes_storage_internal.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#define FAKE_STORAGE_MAX_SIZE (512 * 1024)
|
||||
|
||||
typedef struct FakePutBytesStorageData {
|
||||
PutBytesStorageInfo *info;
|
||||
bool last_is_success;
|
||||
uint32_t crc;
|
||||
uint32_t total_size;
|
||||
uint8_t buffer[FAKE_STORAGE_MAX_SIZE];
|
||||
} FakePutBytesStorageData;
|
||||
|
||||
FakePutBytesStorageData s_storage_data;
|
||||
|
||||
bool pb_storage_raw_get_status(PutBytesObjectType obj_type, PbInstallStatus *status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool fake_pb_storage_mem_init(PutBytesStorage *storage, PutBytesObjectType object_type,
|
||||
uint32_t total_size, PutBytesStorageInfo *info,
|
||||
uint32_t append_offset) {
|
||||
// This fake only supports one put bytes storage to be init'd at a time.
|
||||
PBL_ASSERTN(!s_storage_data.total_size);
|
||||
size_t buffer_size = total_size + sizeof(FirmwareDescription);
|
||||
memset(s_storage_data.buffer, 0, sizeof(s_storage_data.buffer));
|
||||
s_storage_data.total_size = buffer_size;
|
||||
PutBytesStorageInfo *info_copy = NULL;
|
||||
if (info) {
|
||||
info_copy = (PutBytesStorageInfo *)kernel_malloc_check(sizeof(PutBytesStorageInfo));
|
||||
*info_copy = *info;
|
||||
}
|
||||
s_storage_data.info = info_copy;
|
||||
storage->impl_data = &s_storage_data;
|
||||
|
||||
// put_bytes_storage_raw.c is weird, it reserves space at the beginning for FirmwareDescription:
|
||||
storage->current_offset = sizeof(FirmwareDescription);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t fake_pb_storage_mem_get_max_size(PutBytesObjectType object_type) {
|
||||
return FAKE_STORAGE_MAX_SIZE;
|
||||
}
|
||||
|
||||
static void(*s_do_before_write)(void) = NULL;
|
||||
static void fake_pb_storage_mem_write(PutBytesStorage *storage, uint32_t offset,
|
||||
const uint8_t *buffer, uint32_t length) {
|
||||
PBL_ASSERTN(s_storage_data.total_size);
|
||||
PBL_ASSERTN(offset + length <= s_storage_data.total_size);
|
||||
|
||||
if (s_do_before_write) {
|
||||
s_do_before_write();
|
||||
s_do_before_write = NULL;
|
||||
}
|
||||
|
||||
memcpy(s_storage_data.buffer + offset, buffer, length);
|
||||
}
|
||||
|
||||
static uint32_t fake_pb_storage_mem_calculate_crc(PutBytesStorage *storage, PutBytesCrcType crc_type) {
|
||||
PBL_ASSERTN(storage->impl_data == &s_storage_data);
|
||||
return s_storage_data.crc;
|
||||
}
|
||||
|
||||
static void prv_cleanup(void) {
|
||||
kernel_free(s_storage_data.info);
|
||||
s_storage_data.info = NULL;
|
||||
s_storage_data.total_size = 0;
|
||||
}
|
||||
|
||||
static void fake_pb_storage_mem_deinit(PutBytesStorage *storage, bool is_success) {
|
||||
PBL_ASSERTN(storage->impl_data == &s_storage_data);
|
||||
prv_cleanup();
|
||||
s_storage_data.last_is_success = is_success;
|
||||
}
|
||||
|
||||
const PutBytesStorageImplementation s_raw_implementation = {
|
||||
.init = fake_pb_storage_mem_init,
|
||||
.get_max_size = fake_pb_storage_mem_get_max_size,
|
||||
.write = fake_pb_storage_mem_write,
|
||||
.calculate_crc = fake_pb_storage_mem_calculate_crc,
|
||||
.deinit = fake_pb_storage_mem_deinit,
|
||||
};
|
||||
|
||||
const PutBytesStorageImplementation s_file_implementation = {
|
||||
.init = fake_pb_storage_mem_init,
|
||||
.get_max_size = fake_pb_storage_mem_get_max_size,
|
||||
.write = fake_pb_storage_mem_write,
|
||||
.calculate_crc = fake_pb_storage_mem_calculate_crc,
|
||||
.deinit = fake_pb_storage_mem_deinit,
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Fake manipulation
|
||||
|
||||
void fake_pb_storage_register_cb_before_write(void (*cb_before_write)(void)) {
|
||||
s_do_before_write = cb_before_write;
|
||||
}
|
||||
|
||||
void fake_pb_storage_mem_reset(void) {
|
||||
prv_cleanup();
|
||||
s_storage_data = (FakePutBytesStorageData) {};
|
||||
}
|
||||
|
||||
void fake_pb_storage_mem_set_crc(uint32_t crc) {
|
||||
s_storage_data.crc = crc;
|
||||
}
|
||||
|
||||
bool fake_pb_storage_mem_get_last_success(void) {
|
||||
return s_storage_data.last_is_success;
|
||||
}
|
||||
|
||||
void fake_pb_storage_mem_assert_contents_written(const uint8_t contents[], size_t size) {
|
||||
cl_assert_equal_m(contents, s_storage_data.buffer + sizeof(FirmwareDescription), size);
|
||||
}
|
||||
|
||||
void fake_pb_storage_mem_assert_fw_description_written(const FirmwareDescription *fw_descr) {
|
||||
cl_assert_equal_m(fw_descr, s_storage_data.buffer, sizeof(*fw_descr));
|
||||
}
|
||||
|
||||
#undef FAKE_STORAGE_MAX_SIZE
|
37
tests/fakes/fake_put_bytes_storage_mem.h
Normal file
37
tests/fakes/fake_put_bytes_storage_mem.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "services/common/put_bytes/put_bytes.h"
|
||||
#include "system/firmware_storage.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t fake_pb_storage_mem_get_max_size(PutBytesObjectType object_type);
|
||||
|
||||
void fake_pb_storage_mem_reset(void);
|
||||
|
||||
void fake_pb_storage_mem_set_crc(uint32_t crc);
|
||||
|
||||
bool fake_pb_storage_mem_get_last_success(void);
|
||||
|
||||
void fake_pb_storage_mem_assert_contents_written(const uint8_t contents[], size_t size);
|
||||
|
||||
void fake_pb_storage_mem_assert_fw_description_written(const FirmwareDescription *fw_descr);
|
||||
|
||||
void fake_pb_storage_register_cb_before_write(void (*cb_before_write)(void));
|
127
tests/fakes/fake_queue.c
Normal file
127
tests/fakes/fake_queue.c
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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 "fake_queue.h"
|
||||
|
||||
#include "util/circular_buffer.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "queue.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
uint16_t item_size;
|
||||
bool is_semph;
|
||||
|
||||
//! @return ticks spent executing the callback
|
||||
TickType_t (*yield_cb)(QueueHandle_t);
|
||||
CircularBuffer circular_buffer;
|
||||
uint8_t storage[];
|
||||
} FakeQueue;
|
||||
|
||||
signed portBASE_TYPE xQueueGenericReceive(QueueHandle_t xQueue, void * const pvBuffer,
|
||||
TickType_t xTicksToWait, portBASE_TYPE xJustPeeking) {
|
||||
FakeQueue *q = (FakeQueue *) xQueue;
|
||||
TickType_t ticks_waited = 0;
|
||||
while (true) {
|
||||
uint16_t read_space = circular_buffer_get_read_space_remaining(&q->circular_buffer);
|
||||
if (read_space >= q->item_size) {
|
||||
circular_buffer_consume(&q->circular_buffer, q->item_size);
|
||||
return pdTRUE;
|
||||
} else {
|
||||
if (!xTicksToWait || !q->yield_cb) {
|
||||
return pdFALSE;
|
||||
} else {
|
||||
ticks_waited += q->yield_cb(xQueue);
|
||||
if (ticks_waited >= xTicksToWait) {
|
||||
return pdFALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signed portBASE_TYPE xQueueGenericSend(QueueHandle_t xQueue, const void * const pvItemToQueue,
|
||||
TickType_t xTicksToWait, portBASE_TYPE xCopyPosition) {
|
||||
FakeQueue *q = (FakeQueue *) xQueue;
|
||||
TickType_t ticks_waited = 0;
|
||||
while (true) {
|
||||
uint16_t write_space = circular_buffer_get_write_space_remaining(&q->circular_buffer);
|
||||
if (write_space >= q->item_size) {
|
||||
// Just write a zero for a semaphore
|
||||
const uint8_t semph_data = 0;
|
||||
const uint8_t *data = q->is_semph ? &semph_data : (const uint8_t *) pvItemToQueue;
|
||||
circular_buffer_write(&q->circular_buffer, data, q->item_size);
|
||||
return pdTRUE;
|
||||
} else {
|
||||
if (!xTicksToWait || !q->yield_cb) {
|
||||
return pdFALSE;
|
||||
} else {
|
||||
ticks_waited += q->yield_cb(xQueue);
|
||||
if (ticks_waited >= xTicksToWait) {
|
||||
return pdFALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QueueHandle_t xQueueGenericCreate(unsigned portBASE_TYPE uxQueueLength,
|
||||
unsigned portBASE_TYPE uxItemSize, unsigned char ucQueueType) {
|
||||
uint16_t item_size;
|
||||
const bool is_semph = (ucQueueType == queueQUEUE_TYPE_BINARY_SEMAPHORE);
|
||||
if (is_semph) {
|
||||
item_size = 1;
|
||||
} else {
|
||||
item_size = uxItemSize;
|
||||
}
|
||||
const uint16_t storage_size = (item_size * uxQueueLength);
|
||||
uint8_t *buffer = malloc(sizeof(FakeQueue) + storage_size);
|
||||
FakeQueue *q = (FakeQueue *) buffer;
|
||||
*q = (const FakeQueue) {
|
||||
.item_size = item_size,
|
||||
.is_semph = is_semph,
|
||||
};
|
||||
circular_buffer_init(&q->circular_buffer, q->storage, storage_size);
|
||||
return q;
|
||||
}
|
||||
|
||||
void vQueueDelete(QueueHandle_t xQueue) {
|
||||
free(xQueue);
|
||||
}
|
||||
|
||||
QueueHandle_t xQueueCreateMutex(unsigned char ucQueueType) {
|
||||
return (QueueHandle_t)1;
|
||||
}
|
||||
|
||||
portBASE_TYPE xQueueTakeMutexRecursive(QueueHandle_t pxMutex, TickType_t xBlockTime) {
|
||||
return pdTRUE;
|
||||
}
|
||||
|
||||
portBASE_TYPE xQueueGiveMutexRecursive(QueueHandle_t xMutex) {
|
||||
return pdTRUE;
|
||||
}
|
||||
|
||||
BaseType_t xQueueGenericReset(QueueHandle_t xQueue, BaseType_t xNewQueue) {
|
||||
return pdTRUE;
|
||||
}
|
||||
|
||||
void fake_queue_set_yield_callback(QueueHandle_t queue,
|
||||
TickType_t (*yield_cb)(QueueHandle_t)) {
|
||||
FakeQueue *fake_queue = (FakeQueue *) queue;
|
||||
fake_queue->yield_cb = yield_cb;
|
||||
}
|
25
tests/fakes/fake_queue.h
Normal file
25
tests/fakes/fake_queue.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "queue.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void fake_queue_set_yield_callback(QueueHandle_t queue,
|
||||
TickType_t (*yield_cb)(QueueHandle_t));
|
73
tests/fakes/fake_regular_timer.h
Normal file
73
tests/fakes/fake_regular_timer.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "services/common/regular_timer.h"
|
||||
|
||||
static ListNode s_seconds_callbacks;
|
||||
static ListNode s_minutes_callbacks;
|
||||
|
||||
static bool prv_callback_registered_filter(ListNode *found_node, void *data) {
|
||||
return (found_node == (ListNode *)data);
|
||||
}
|
||||
|
||||
void regular_timer_add_multisecond_callback(RegularTimerInfo* cb, uint16_t seconds) {
|
||||
if (!list_find(&s_seconds_callbacks, prv_callback_registered_filter, &cb->list_node)) {
|
||||
list_append(&s_seconds_callbacks, &cb->list_node);
|
||||
}
|
||||
}
|
||||
|
||||
void regular_timer_add_seconds_callback(RegularTimerInfo* cb) {
|
||||
regular_timer_add_multisecond_callback(cb, 1);
|
||||
}
|
||||
|
||||
void regular_timer_add_multiminute_callback(RegularTimerInfo* cb, uint16_t minutes) {
|
||||
if (!list_find(&s_minutes_callbacks, prv_callback_registered_filter, &cb->list_node)) {
|
||||
list_append(&s_minutes_callbacks, &cb->list_node);
|
||||
}
|
||||
}
|
||||
|
||||
void regular_timer_add_minutes_callback(RegularTimerInfo* cb) {
|
||||
regular_timer_add_multiminute_callback(cb, 1);
|
||||
}
|
||||
|
||||
bool regular_timer_is_scheduled(RegularTimerInfo *cb) {
|
||||
return (list_find(&s_seconds_callbacks, prv_callback_registered_filter, &cb->list_node) ||
|
||||
list_find(&s_minutes_callbacks, prv_callback_registered_filter, &cb->list_node));
|
||||
}
|
||||
|
||||
bool regular_timer_pending_deletion(RegularTimerInfo *cb) {
|
||||
return cb->pending_delete;
|
||||
}
|
||||
|
||||
bool regular_timer_remove_callback(RegularTimerInfo* cb) {
|
||||
bool timer_removed = false;
|
||||
if (regular_timer_is_scheduled(cb)) {
|
||||
list_remove(&cb->list_node, NULL, NULL);
|
||||
timer_removed = true;
|
||||
}
|
||||
|
||||
return timer_removed;
|
||||
}
|
||||
|
||||
void fake_regular_timer_trigger(RegularTimerInfo *timer) {
|
||||
if (regular_timer_is_scheduled(timer)) {
|
||||
timer->cb(timer->cb_data);
|
||||
}
|
||||
}
|
26
tests/fakes/fake_reminder_db.h
Normal file
26
tests/fakes/fake_reminder_db.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
bool s_empty = true;
|
||||
|
||||
void fake_reminder_db_set_empty(bool empty) {
|
||||
s_empty = empty;
|
||||
}
|
||||
|
||||
bool reminder_db_is_empty() {
|
||||
return s_empty;
|
||||
}
|
||||
|
41
tests/fakes/fake_resource_storage.c
Normal file
41
tests/fakes/fake_resource_storage.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 "resource/resource_storage_flash.h"
|
||||
#include "resource/resource_storage_impl.h"
|
||||
#include "resource/resource.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "flash_region/flash_region.h"
|
||||
#include "util/string.h"
|
||||
|
||||
void resource_storage_get_file_name(char *name, size_t buf_length, ResAppNum resource_bank) {
|
||||
concat_str_int("res_bank", resource_bank, name, buf_length);
|
||||
}
|
||||
|
||||
void resource_storage_clear(ResAppNum app_num) {
|
||||
return;
|
||||
}
|
||||
|
||||
const SystemResourceBank * resource_storage_flash_get_unused_bank(void) {
|
||||
static const SystemResourceBank unused_bank = {
|
||||
.begin = FLASH_REGION_SYSTEM_RESOURCES_BANK_1_BEGIN,
|
||||
.end = FLASH_REGION_SYSTEM_RESOURCES_BANK_1_END,
|
||||
};
|
||||
return &unused_bank;
|
||||
}
|
19
tests/fakes/fake_resource_storage.h
Normal file
19
tests/fakes/fake_resource_storage.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void resource_storage_get_file_name(char *name, size_t buf_length, ResAppNum resource_bank);
|
96
tests/fakes/fake_resource_syscalls.c
Normal file
96
tests/fakes/fake_resource_syscalls.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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 "fake_resource_syscalls.h"
|
||||
|
||||
#define PATH_STRING_LENGTH 512
|
||||
|
||||
#define MAX_OPEN_FILES 512
|
||||
|
||||
static FILE* resource_files[MAX_OPEN_FILES] = {NULL};
|
||||
static const uint32_t resource_start_index = 1; // must start at 1 so font resources work
|
||||
static uint32_t resource_index = resource_start_index;
|
||||
|
||||
ResAppNum sys_get_current_resource_num(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t sys_resource_load_file_as_resource(const char *filepath, const char *filename) {
|
||||
uint32_t resource_id = UINT32_MAX;
|
||||
char full_path[PATH_STRING_LENGTH];
|
||||
if (filepath) {
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", filepath, filename);
|
||||
} else {
|
||||
snprintf(full_path, sizeof(full_path), "%s", filename);
|
||||
}
|
||||
FILE* resource_file = fopen(full_path, "r");
|
||||
if (resource_file) {
|
||||
resource_files[resource_index] = resource_file;
|
||||
resource_id = resource_index;
|
||||
resource_index++; // Increment to next slot
|
||||
}
|
||||
return resource_id;
|
||||
}
|
||||
|
||||
size_t sys_resource_size(ResAppNum app_num, uint32_t handle) {
|
||||
if (handle < UINT32_MAX) {
|
||||
FILE* resource_file = resource_files[handle];
|
||||
fseek(resource_file, 0, SEEK_END);
|
||||
size_t resource_size = ftell(resource_file);
|
||||
fseek(resource_file, 0, SEEK_SET);
|
||||
return resource_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t sys_resource_load_range(ResAppNum app_num, uint32_t id, uint32_t start_bytes,
|
||||
uint8_t *buffer, size_t num_bytes) {
|
||||
if (buffer && id < UINT32_MAX) {
|
||||
FILE* resource_file = resource_files[id];
|
||||
fseek(resource_file, start_bytes, SEEK_SET);
|
||||
return fread(buffer, 1, num_bytes, resource_file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool sys_resource_bytes_are_readonly(void *bytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t *sys_resource_read_only_bytes(ResAppNum app_num, uint32_t resource_id,
|
||||
size_t *num_bytes_out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t sys_resource_get_and_cache(ResAppNum app_num, uint32_t resource_id) {
|
||||
return resource_id;
|
||||
}
|
||||
|
||||
bool sys_resource_is_valid(ResAppNum app_num, uint32_t resource_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void fake_resource_syscalls_cleanup(void) {
|
||||
for (int i = resource_start_index; i <= resource_index; i++) {
|
||||
FILE *resource_file = resource_files[i];
|
||||
if (resource_file) {
|
||||
fclose(resource_file);
|
||||
resource_files[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
resource_index = resource_start_index;
|
||||
}
|
41
tests/fakes/fake_resource_syscalls.h
Normal file
41
tests/fakes/fake_resource_syscalls.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "resource/resource.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
ResAppNum sys_get_current_resource_num(void);
|
||||
|
||||
uint32_t sys_resource_load_file_as_resource(const char *filepath, const char *filename);
|
||||
|
||||
size_t sys_resource_size(ResAppNum app_num, uint32_t handle);
|
||||
|
||||
size_t sys_resource_load_range(ResAppNum app_num, uint32_t id, uint32_t start_bytes, uint8_t *buffer, size_t num_bytes);
|
||||
|
||||
const uint8_t * sys_resource_read_only_bytes(ResAppNum app_num, uint32_t resource_id,
|
||||
size_t *num_bytes_out);
|
||||
|
||||
uint32_t sys_resource_get_and_cache(ResAppNum app_num, uint32_t resource_id);
|
||||
|
||||
bool sys_resource_is_valid(ResAppNum app_num, uint32_t resource_id);
|
||||
|
||||
void fake_resource_syscalls_cleanup(void);
|
30
tests/fakes/fake_rng.h
Normal file
30
tests/fakes/fake_rng.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static bool s_is_seeded = false;
|
||||
|
||||
bool rng_rand(uint32_t *rand_out) {
|
||||
if (!s_is_seeded) {
|
||||
srand(0); // Seed with constant, to make unit tests less random :)
|
||||
s_is_seeded = true;
|
||||
}
|
||||
*rand_out = rand();
|
||||
return true;
|
||||
}
|
156
tests/fakes/fake_rtc.c
Normal file
156
tests/fakes/fake_rtc.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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 "fake_rtc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
static RtcTicks s_rtc_tick_count;
|
||||
static RtcTicks s_rtc_auto_increment = 0;
|
||||
|
||||
// Defined identically to the static variables in rtc.c
|
||||
static time_t s_time_base = 0;
|
||||
static int16_t s_time_ms_base = 0;
|
||||
static int64_t s_time_tick_base = 0;
|
||||
static TimezoneInfo s_tzinfo = { {0} };
|
||||
|
||||
/*
|
||||
// TODO: Unused right now
|
||||
void rtc_init(void);
|
||||
void rtc_init_timers(void);
|
||||
void rtc_set_time_tm(struct tm* time_tm);
|
||||
|
||||
|
||||
void rtc_next_tick_alarm_init(void);
|
||||
void rtc_set_next_tick_alarm(RtcTicks tick);
|
||||
void rtc_disable_next_tick_alarm(void);
|
||||
bool rtc_is_tick_alarm_initialized(void);
|
||||
|
||||
bool rtc_is_lse_started(void);
|
||||
|
||||
//! @param buffer Buffer used to write the string into. Must be at least TIME_STRING_BUFFER_SIZE
|
||||
const char* time_t_to_string(char* buffer, time_t t);
|
||||
*/
|
||||
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
//! @param buffer Buffer used to write the string into. Must be at least TIME_STRING_BUFFER_SIZE
|
||||
const char* rtc_get_time_string(char* buffer) {return NULL;}
|
||||
|
||||
void rtc_get_time_tm(struct tm* time_tm) {
|
||||
if (time_tm) {
|
||||
time_t temp = rtc_get_time();
|
||||
gmtime_r(&temp, time_tm);
|
||||
}
|
||||
}
|
||||
|
||||
void rtc_set_time(time_t time) {
|
||||
s_time_base = time;
|
||||
}
|
||||
|
||||
void rtc_set_timezone(TimezoneInfo *tzinfo) {
|
||||
s_tzinfo = *tzinfo;
|
||||
}
|
||||
|
||||
bool rtc_is_timezone_set(void) {
|
||||
// The actual driver checks for the first 4 chars as a uint32_t being 0
|
||||
return memcmp(s_tzinfo.tm_zone, "\0\0\0\0", 4) != 0;
|
||||
}
|
||||
|
||||
void rtc_get_timezone(TimezoneInfo *tzinfo) {
|
||||
*tzinfo = s_tzinfo;
|
||||
}
|
||||
|
||||
void rtc_timezone_clear(void) {
|
||||
memset(&s_tzinfo, 0, sizeof(s_tzinfo));
|
||||
}
|
||||
|
||||
bool rtc_sanitize_struct_tm(struct tm *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rtc_sanitize_time_t(time_t *t) {
|
||||
return rtc_sanitize_struct_tm(NULL);
|
||||
}
|
||||
|
||||
uint16_t rtc_get_timezone_id(void) {
|
||||
if (rtc_is_timezone_set()) {
|
||||
return s_tzinfo.timezone_id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! @return millisecond port of the current second.
|
||||
void rtc_get_time_ms(time_t* out_seconds, uint16_t* out_ms) {
|
||||
*out_ms = s_time_ms_base;
|
||||
*out_seconds = s_time_base;
|
||||
}
|
||||
|
||||
// @return a unix timestamp from the host machine
|
||||
time_t rtc_get_time(void) {
|
||||
return s_time_base;
|
||||
}
|
||||
|
||||
time_t sys_get_time(void) {
|
||||
return rtc_get_time();
|
||||
}
|
||||
|
||||
//! @return Absolute number of ticks since system start.
|
||||
RtcTicks rtc_get_ticks(void) {
|
||||
RtcTicks result = s_rtc_tick_count;
|
||||
s_rtc_tick_count += s_rtc_auto_increment;
|
||||
return result;
|
||||
}
|
||||
|
||||
RtcTicks sys_get_ticks(void) {
|
||||
return rtc_get_ticks();
|
||||
}
|
||||
|
||||
//
|
||||
// Fake Functions!
|
||||
//
|
||||
void fake_rtc_init(RtcTicks initial_ticks, time_t initial_time) {
|
||||
s_rtc_tick_count = initial_ticks;
|
||||
s_time_tick_base = initial_ticks;
|
||||
s_time_base = initial_time;
|
||||
}
|
||||
|
||||
void fake_rtc_increment_time(time_t inc) {
|
||||
s_time_base += inc;
|
||||
}
|
||||
|
||||
void fake_rtc_increment_time_ms(uint32_t inc) {
|
||||
const uint32_t new_ms = s_time_ms_base + inc;
|
||||
if (new_ms >= 1000) {
|
||||
s_time_base += new_ms / 1000;
|
||||
}
|
||||
s_time_ms_base = new_ms % 1000;
|
||||
}
|
||||
|
||||
void fake_rtc_set_ticks(RtcTicks new_ticks) {
|
||||
s_rtc_tick_count = new_ticks;
|
||||
}
|
||||
|
||||
void fake_rtc_increment_ticks(RtcTicks tick_increment) {
|
||||
s_rtc_tick_count += tick_increment;
|
||||
}
|
||||
|
||||
void fake_rtc_auto_increment_ticks(RtcTicks auto_increment) {
|
||||
s_rtc_auto_increment = auto_increment;
|
||||
}
|
33
tests/fakes/fake_rtc.h
Normal file
33
tests/fakes/fake_rtc.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "drivers/rtc.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
void fake_rtc_init(RtcTicks initial_ticks, time_t initial_time);
|
||||
|
||||
void fake_rtc_increment_time(time_t inc);
|
||||
void fake_rtc_increment_time_ms(uint32_t inc);
|
||||
void fake_rtc_set_ticks(RtcTicks new_ticks);
|
||||
void fake_rtc_increment_ticks(RtcTicks tick_increment);
|
||||
void fake_rtc_auto_increment_ticks(RtcTicks tick_increment);
|
||||
|
||||
// TODO: there is a lot of stuff missing.
|
502
tests/fakes/fake_session.c
Normal file
502
tests/fakes/fake_session.c
Normal file
|
@ -0,0 +1,502 @@
|
|||
/*
|
||||
* 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 "fake_session.h"
|
||||
|
||||
#include "comm/bt_lock.h"
|
||||
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "system/logging.h"
|
||||
#include "services/common/comm_session/protocol.h"
|
||||
#include "services/common/comm_session/session_send_buffer.h"
|
||||
#include "services/common/system_task.h"
|
||||
#include "util/circular_buffer.h"
|
||||
#include "system/hexdump.h"
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
#include "util/list.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern void fake_system_task_callbacks_invoke_pending(void);
|
||||
|
||||
typedef struct CommSession {
|
||||
ListNode node;
|
||||
Transport *transport;
|
||||
const TransportImplementation *transport_imp;
|
||||
bool is_send_next_call_pending;
|
||||
TransportDestination destination;
|
||||
uint8_t *temp_write_buffer;
|
||||
uint16_t endpoint_id;
|
||||
uint16_t bytes_written;
|
||||
uint16_t max_out_payload_length;
|
||||
CircularBuffer send_buffer;
|
||||
uint8_t storage[1024];
|
||||
} CommSession;
|
||||
|
||||
static CommSession *s_session_head;
|
||||
|
||||
static int s_session_close_call_count;
|
||||
static int s_session_open_call_count;
|
||||
|
||||
bool comm_session_is_valid(const CommSession *session) {
|
||||
return list_contains((ListNode *) s_session_head, &session->node);
|
||||
}
|
||||
|
||||
static bool prv_find_session_is_system_filter(ListNode *found_node, void *data) {
|
||||
const CommSessionType requested_type = (const bool) (uintptr_t) data;
|
||||
const TransportDestination destination = ((const CommSession *) found_node)->destination;
|
||||
switch (requested_type) {
|
||||
case CommSessionTypeApp:
|
||||
return destination == TransportDestinationApp || destination == TransportDestinationHybrid;
|
||||
case CommSessionTypeSystem:
|
||||
return destination == TransportDestinationSystem || destination == TransportDestinationHybrid;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool comm_session_has_capability(CommSession *session, CommSessionCapability capability){
|
||||
return true;
|
||||
}
|
||||
|
||||
CommSession * comm_session_get_by_type(CommSessionType type) {
|
||||
// TODO: This is not going to fly with multiple app sessions
|
||||
CommSession *session;
|
||||
bt_lock();
|
||||
{
|
||||
session = (CommSession *) list_find((ListNode *) s_session_head,
|
||||
prv_find_session_is_system_filter,
|
||||
(void *) (uintptr_t) type);
|
||||
}
|
||||
bt_unlock();
|
||||
return session;
|
||||
}
|
||||
|
||||
CommSession* comm_session_get_system_session(void) {
|
||||
// TODO: What if Pebble App is connected via iSPP *and* PPoGATT ?
|
||||
return comm_session_get_by_type(CommSessionTypeSystem);
|
||||
}
|
||||
|
||||
CommSession* comm_session_get_current_app_session(void) {
|
||||
// TODO: What if App is connected via iSPP *and* PPoGATT ?
|
||||
return comm_session_get_by_type(CommSessionTypeApp);
|
||||
}
|
||||
|
||||
void comm_session_close(CommSession *session, CommSessionCloseReason reason) {
|
||||
cl_assert(list_contains((const ListNode *) s_session_head, &session->node));
|
||||
if (session->temp_write_buffer) {
|
||||
kernel_free(session->temp_write_buffer);
|
||||
}
|
||||
list_remove(&session->node, (ListNode **) &s_session_head, NULL);
|
||||
kernel_free(session);
|
||||
++s_session_close_call_count;
|
||||
}
|
||||
|
||||
void comm_session_receive_router_write(CommSession *session,
|
||||
const uint8_t *received_data,
|
||||
size_t num_bytes_to_copy) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Received Data:");
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, received_data, num_bytes_to_copy);
|
||||
}
|
||||
|
||||
bool comm_session_send_data(CommSession *session, uint16_t endpoint_id,
|
||||
const uint8_t *data, size_t length, uint32_t timeout_ms) {
|
||||
SendBuffer *sb = comm_session_send_buffer_begin_write(session, endpoint_id, length, timeout_ms);
|
||||
if (!sb) {
|
||||
return false;
|
||||
}
|
||||
comm_session_send_buffer_write(sb, data, length);
|
||||
comm_session_send_buffer_end_write(sb);
|
||||
return true;
|
||||
}
|
||||
|
||||
CommSession * comm_session_open(Transport *transport, const TransportImplementation *implementation,
|
||||
TransportDestination destination) {
|
||||
++s_session_open_call_count;
|
||||
|
||||
CommSession *session = kernel_malloc(sizeof(CommSession));
|
||||
memset(session, 0, sizeof(*session));
|
||||
*session = (const CommSession) {
|
||||
.transport = transport,
|
||||
.transport_imp = implementation,
|
||||
.destination = destination,
|
||||
.max_out_payload_length = COMM_MAX_OUTBOUND_PAYLOAD_SIZE,
|
||||
};
|
||||
|
||||
const size_t max_pp_msg_size = session->max_out_payload_length + sizeof(PebbleProtocolHeader);
|
||||
// If this fails, you need to bump up the size of the storage[] array in the fake CommSession
|
||||
cl_assert(sizeof(session->storage) >= max_pp_msg_size);
|
||||
circular_buffer_init(&session->send_buffer, session->storage, max_pp_msg_size);
|
||||
|
||||
s_session_head = (CommSession *) list_prepend((ListNode *) s_session_head, &session->node);
|
||||
return session;
|
||||
}
|
||||
|
||||
size_t comm_session_send_queue_get_length(const CommSession *session) {
|
||||
cl_assert(list_contains((const ListNode *) s_session_head, &session->node));
|
||||
return circular_buffer_get_read_space_remaining(&session->send_buffer);
|
||||
}
|
||||
|
||||
size_t comm_session_send_queue_copy(CommSession *session, uint32_t start_off, size_t length,
|
||||
uint8_t *data_out) {
|
||||
cl_assert(data_out);
|
||||
cl_assert(list_contains((const ListNode *) s_session_head, &session->node));
|
||||
return circular_buffer_copy_offset(&session->send_buffer, start_off, data_out, length);
|
||||
}
|
||||
|
||||
void comm_session_send_queue_consume(CommSession *session, size_t length) {
|
||||
circular_buffer_consume(&session->send_buffer, length);
|
||||
}
|
||||
|
||||
static void prv_send_next_kernel_bg_cb(void *data) {
|
||||
CommSession *session = (CommSession *) data;
|
||||
if (!list_contains((const ListNode *) s_session_head, (const ListNode *) session)) {
|
||||
// Session closed in the mean time
|
||||
return;
|
||||
}
|
||||
// Flip the flag before the send_next callback, so it can schedule again if needed.
|
||||
session->is_send_next_call_pending = false;
|
||||
|
||||
// Kick the transport to send out the next bytes from the send buffer
|
||||
const size_t read_space = comm_session_send_queue_get_length(session);
|
||||
if (read_space) {
|
||||
session->transport_imp->send_next(session->transport);
|
||||
}
|
||||
}
|
||||
|
||||
void comm_session_send_next(CommSession *session) {
|
||||
if (session->is_send_next_call_pending) {
|
||||
return;
|
||||
}
|
||||
system_task_add_callback(prv_send_next_kernel_bg_cb, session);
|
||||
session->is_send_next_call_pending = true;
|
||||
}
|
||||
|
||||
bool prv_filter_by_transport_callback(ListNode *node, void *data) {
|
||||
return (((CommSession *) node)->transport == data);
|
||||
}
|
||||
|
||||
CommSession * prv_find_session_by_transport(Transport *transport) {
|
||||
return (CommSession *) list_find((ListNode *) s_session_head,
|
||||
prv_filter_by_transport_callback, transport);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Send buffer fakes
|
||||
|
||||
size_t comm_session_send_buffer_get_max_payload_length(const CommSession *session) {
|
||||
size_t max_length = 0;
|
||||
if (comm_session_is_valid(session)) {
|
||||
max_length = session->send_buffer.buffer_size - sizeof(PebbleProtocolHeader);
|
||||
}
|
||||
return max_length;
|
||||
}
|
||||
|
||||
SendBuffer * comm_session_send_buffer_begin_write(CommSession *session, uint16_t endpoint_id,
|
||||
size_t required_free_length,
|
||||
uint32_t timeout_ms) {
|
||||
if (!session) {
|
||||
return NULL;
|
||||
}
|
||||
if (!comm_session_is_valid(session)) {
|
||||
return NULL;
|
||||
}
|
||||
if (required_free_length + sizeof(PebbleProtocolHeader) >
|
||||
circular_buffer_get_write_space_remaining(&session->send_buffer)) {
|
||||
return NULL;
|
||||
}
|
||||
if (session->temp_write_buffer) {
|
||||
// Already writing, fake doesn't support multiple tasks trying to write at the same time
|
||||
return NULL;
|
||||
}
|
||||
session->temp_write_buffer = (uint8_t *) kernel_malloc(session->max_out_payload_length);
|
||||
session->bytes_written = 0;
|
||||
session->endpoint_id = endpoint_id;
|
||||
return (SendBuffer *) session;
|
||||
}
|
||||
|
||||
bool comm_session_send_buffer_write(SendBuffer *sb, const uint8_t *data, size_t length) {
|
||||
CommSession *session = (CommSession *) sb;
|
||||
cl_assert(session);
|
||||
cl_assert(session->temp_write_buffer);
|
||||
cl_assert(length + session->bytes_written <= session->max_out_payload_length);
|
||||
|
||||
memcpy(session->temp_write_buffer + session->bytes_written, data, length);
|
||||
session->bytes_written += length;
|
||||
return true;
|
||||
}
|
||||
|
||||
void comm_session_send_buffer_end_write(SendBuffer *sb) {
|
||||
CommSession *session = (CommSession *) sb;
|
||||
cl_assert(session);
|
||||
cl_assert(session->temp_write_buffer);
|
||||
|
||||
const PebbleProtocolHeader pp_header = {
|
||||
.length = session->bytes_written,
|
||||
.endpoint_id = session->endpoint_id,
|
||||
};
|
||||
|
||||
circular_buffer_write(&session->send_buffer, (const uint8_t *) &pp_header, sizeof(pp_header));
|
||||
circular_buffer_write(&session->send_buffer, session->temp_write_buffer, session->bytes_written);
|
||||
|
||||
kernel_free(session->temp_write_buffer);
|
||||
session->temp_write_buffer = NULL;
|
||||
session->endpoint_id = ~0;
|
||||
session->bytes_written = 0;
|
||||
}
|
||||
|
||||
static uint32_t s_responsiveness_max_period_s;
|
||||
static bool s_responsiveness_latency_is_reduced;
|
||||
static ResponsivenessGrantedHandler s_last_responsiveness_granted_handler;
|
||||
|
||||
void comm_session_set_responsiveness(
|
||||
CommSession *session, BtConsumer consumer, ResponseTimeState state, uint16_t max_period_secs) {
|
||||
comm_session_set_responsiveness_ext(session, consumer, state, max_period_secs, NULL);
|
||||
}
|
||||
|
||||
void comm_session_set_responsiveness_ext(CommSession *session, BtConsumer consumer,
|
||||
ResponseTimeState state, uint16_t max_period_secs,
|
||||
ResponsivenessGrantedHandler granted_handler) {
|
||||
|
||||
s_responsiveness_max_period_s = max_period_secs;
|
||||
|
||||
if (state == ResponseTimeMiddle) {
|
||||
s_responsiveness_latency_is_reduced = true;
|
||||
} else if (state == ResponseTimeMax) {
|
||||
s_responsiveness_latency_is_reduced = false;
|
||||
}
|
||||
|
||||
s_last_responsiveness_granted_handler = granted_handler;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Session related functions
|
||||
|
||||
ResponsivenessGrantedHandler fake_comm_session_get_last_responsiveness_granted_handler(void) {
|
||||
return s_last_responsiveness_granted_handler;
|
||||
}
|
||||
|
||||
int fake_comm_session_open_call_count(void) {
|
||||
return s_session_open_call_count;
|
||||
}
|
||||
|
||||
int fake_comm_session_close_call_count(void) {
|
||||
return s_session_close_call_count;
|
||||
}
|
||||
|
||||
void fake_comm_session_process_send_next(void) {
|
||||
CommSession *session = s_session_head;
|
||||
while (session) {
|
||||
CommSession *next = (CommSession *) session->node.next;
|
||||
comm_session_send_next(session);
|
||||
session = next;
|
||||
}
|
||||
fake_system_task_callbacks_invoke_pending();
|
||||
}
|
||||
|
||||
uint32_t fake_comm_session_get_responsiveness_max_period(void) {
|
||||
return s_responsiveness_max_period_s;
|
||||
}
|
||||
|
||||
uint32_t fake_comm_session_is_latency_reduced(void) {
|
||||
return s_responsiveness_latency_is_reduced;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Transport mock
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
uint16_t endpoint_id;
|
||||
size_t length;
|
||||
uint8_t data[];
|
||||
} DataNode;
|
||||
|
||||
typedef struct {
|
||||
ListNode node;
|
||||
TransportDestination destination;
|
||||
FakeTransportSentCallback sent_cb;
|
||||
Uuid app_uuid;
|
||||
CommSession *session;
|
||||
|
||||
//! When no sent_cb is used, data is appended to this list
|
||||
DataNode *sent_data;
|
||||
} FakeTransport;
|
||||
|
||||
static FakeTransport *s_fake_transport_head;
|
||||
|
||||
static void prv_fake_transport_send_next(Transport *transport) {
|
||||
FakeTransport *fake_transport = (FakeTransport *) transport;
|
||||
cl_assert_equal_b(list_contains((const ListNode *) s_fake_transport_head,
|
||||
(const ListNode *) fake_transport), true);
|
||||
CommSession *session = fake_transport->session;
|
||||
PebbleProtocolHeader pp_header;
|
||||
uint8_t *buffer = kernel_malloc(1024);
|
||||
while (circular_buffer_copy(&session->send_buffer,
|
||||
(uint8_t *) &pp_header, sizeof(pp_header)) == sizeof(pp_header)) {
|
||||
circular_buffer_copy_offset(&session->send_buffer, sizeof(pp_header), buffer, pp_header.length);
|
||||
if (fake_transport->sent_cb) {
|
||||
fake_transport->sent_cb(pp_header.endpoint_id, buffer, pp_header.length);
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Sending Data to PP endpoint %u (0x%x):",
|
||||
pp_header.endpoint_id, pp_header.endpoint_id);
|
||||
PBL_HEXDUMP(LOG_LEVEL_DEBUG, buffer, pp_header.length);
|
||||
|
||||
DataNode *data_node = kernel_malloc(sizeof(DataNode) + pp_header.length);
|
||||
list_init(&data_node->node);
|
||||
data_node->endpoint_id = pp_header.endpoint_id;
|
||||
data_node->length = pp_header.length;
|
||||
memcpy(data_node->data, buffer, pp_header.length);
|
||||
fake_transport->sent_data = (DataNode *) list_prepend(&fake_transport->sent_data->node,
|
||||
&data_node->node);
|
||||
}
|
||||
circular_buffer_consume(&session->send_buffer, sizeof(pp_header) + pp_header.length);
|
||||
}
|
||||
kernel_free(buffer);
|
||||
}
|
||||
|
||||
static void prv_fake_transport_reset(Transport *transport) {
|
||||
cl_assert_(false, "Not implemented: prv_fake_transport_reset");
|
||||
}
|
||||
|
||||
static const TransportImplementation s_fake_transport_implementation = {
|
||||
.send_next = prv_fake_transport_send_next,
|
||||
.reset = prv_fake_transport_reset,
|
||||
};
|
||||
|
||||
Transport *fake_transport_create(TransportDestination destination,
|
||||
const Uuid *app_uuid,
|
||||
FakeTransportSentCallback sent_cb) {
|
||||
if (app_uuid == NULL) {
|
||||
cl_assert_(TransportDestinationSystem == destination ||
|
||||
TransportDestinationHybrid == TransportDestinationSystem,
|
||||
"When passing NULL app_uuid, the destination can only be System or Hybrid");
|
||||
} else {
|
||||
cl_assert_(TransportDestinationSystem == destination ||
|
||||
TransportDestinationHybrid == TransportDestinationSystem,
|
||||
"When passing an app_uuid, the destination can only be App or Hybrid");
|
||||
}
|
||||
FakeTransport *transport = (FakeTransport *) kernel_malloc(sizeof(FakeTransport));
|
||||
*transport = (const FakeTransport) {
|
||||
.destination = destination,
|
||||
.sent_cb = sent_cb,
|
||||
};
|
||||
if (app_uuid) {
|
||||
transport->app_uuid = *app_uuid;
|
||||
}
|
||||
s_fake_transport_head = (FakeTransport *) list_prepend((ListNode *) s_fake_transport_head,
|
||||
&transport->node);
|
||||
return (Transport *) transport;
|
||||
}
|
||||
|
||||
CommSession *fake_transport_set_connected(Transport *transport, bool connected) {
|
||||
FakeTransport *fake_transport = (FakeTransport *) transport;
|
||||
if (connected) {
|
||||
cl_assert_equal_p(fake_transport->session, NULL);
|
||||
fake_transport->session = comm_session_open(transport, &s_fake_transport_implementation,
|
||||
fake_transport->destination);
|
||||
return fake_transport->session;
|
||||
} else {
|
||||
cl_assert(fake_transport->session);
|
||||
comm_session_close(fake_transport->session, 0);
|
||||
fake_transport->session = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void fake_transport_set_sent_cb(Transport *transport, FakeTransportSentCallback sent_cb) {
|
||||
cl_assert(transport);
|
||||
FakeTransport *fake_transport = (FakeTransport *) transport;
|
||||
fake_transport->sent_cb = sent_cb;
|
||||
}
|
||||
|
||||
void fake_transport_assert_sent(Transport *transport, uint16_t index, uint16_t endpoint_id,
|
||||
const uint8_t data[], size_t length) {
|
||||
cl_assert(transport);
|
||||
FakeTransport *fake_transport = (FakeTransport *)transport;
|
||||
DataNode *data_node = fake_transport->sent_data;
|
||||
for (uint16_t i = 0; i <= index; ++i) {
|
||||
cl_assert_(data_node, "Sent out too few packets");
|
||||
|
||||
if (i == index) {
|
||||
cl_assert_equal_i(data_node->endpoint_id, endpoint_id);
|
||||
cl_assert_equal_i(data_node->length, length);
|
||||
cl_assert_equal_m(data_node->data, data, length);
|
||||
}
|
||||
|
||||
data_node = (DataNode *) data_node->node.next;
|
||||
}
|
||||
}
|
||||
|
||||
void fake_transport_assert_nothing_sent(Transport *transport) {
|
||||
cl_assert(transport);
|
||||
FakeTransport *fake_transport = (FakeTransport *)transport;
|
||||
DataNode *data_node = fake_transport->sent_data;
|
||||
cl_assert_equal_p(data_node, NULL);
|
||||
}
|
||||
|
||||
void fake_transport_destroy(Transport *transport) {
|
||||
FakeTransport *fake_transport = (FakeTransport *) transport;
|
||||
cl_assert(transport);
|
||||
cl_assert_equal_b(list_contains((const ListNode *)s_fake_transport_head,
|
||||
(const ListNode *)fake_transport), true);
|
||||
if (fake_transport->session) {
|
||||
// Causes clean up of CommSession:
|
||||
fake_transport_set_connected((Transport *)fake_transport, false /* connected */);
|
||||
}
|
||||
list_remove((ListNode *) fake_transport, (ListNode **) &s_fake_transport_head, NULL);
|
||||
DataNode *data_node = fake_transport->sent_data;
|
||||
while (data_node) {
|
||||
DataNode *next_data_node = (DataNode *)data_node->node.next;
|
||||
kernel_free(data_node);
|
||||
data_node = next_data_node;
|
||||
}
|
||||
kernel_free(fake_transport);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Transport helper functions
|
||||
|
||||
bool fake_comm_session_send_buffer_write_raw_by_transport(Transport *transport,
|
||||
const uint8_t *data, size_t length) {
|
||||
CommSession *session = prv_find_session_by_transport(transport);
|
||||
cl_assert(session);
|
||||
return circular_buffer_write(&session->send_buffer, data, length);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Fake life cycle
|
||||
|
||||
void fake_comm_session_init(void) {
|
||||
cl_assert_(s_fake_transport_head == NULL,
|
||||
"Didn't clean up the fake transports? \
|
||||
Call fake_comm_session_cleanup() if you don't want to clean them up manually.");
|
||||
|
||||
s_session_close_call_count = 0;
|
||||
s_session_open_call_count = 0;
|
||||
s_last_responsiveness_granted_handler = NULL;
|
||||
}
|
||||
|
||||
void fake_comm_session_cleanup(void) {
|
||||
FakeTransport *fake_transport = s_fake_transport_head;
|
||||
while (fake_transport) {
|
||||
FakeTransport *next = (FakeTransport *) fake_transport->node.next;
|
||||
fake_transport_destroy((Transport *) fake_transport);
|
||||
fake_transport = next;
|
||||
}
|
||||
}
|
113
tests/fakes/fake_session.h
Normal file
113
tests/fakes/fake_session.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "services/common/comm_session/session_transport.h"
|
||||
|
||||
//
|
||||
// Usage hints:
|
||||
// ------------
|
||||
//
|
||||
// Typically, you'll want to do something like:
|
||||
//
|
||||
// 1. Connect a fake transport for the system CommSession:
|
||||
//
|
||||
// Transport *transport = fake_transport_create(TransportDestinationSystem, NULL, NULL);
|
||||
// fake_transport_set_connected(transport, true /* connected */);
|
||||
//
|
||||
// 2. Simulate receiving some data by calling your module's endpoint handler:
|
||||
//
|
||||
// put_bytes_protocol_msg_callback(comm_session_get_system_session(), msg, sizeof(msg));
|
||||
//
|
||||
// 3. Process the outbound data that has been queued up by your endpoint implementation:
|
||||
//
|
||||
// fake_comm_session_process_send_next();
|
||||
//
|
||||
// 4. Assert the sent data is what you expect:
|
||||
//
|
||||
// const uint8_t expected_payload[] = { 0x01, 0x02, 0x03 };
|
||||
// fake_transport_assert_sent(transport, 0, endpoint_id,
|
||||
// expected_payload, sizeof(expected_payload));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Session related functions
|
||||
|
||||
ResponsivenessGrantedHandler fake_comm_session_get_last_responsiveness_granted_handler(void);
|
||||
|
||||
int fake_comm_session_open_call_count(void);
|
||||
int fake_comm_session_close_call_count(void);
|
||||
|
||||
void fake_comm_session_process_send_next(void);
|
||||
|
||||
uint32_t fake_comm_session_get_responsiveness_max_period(void);
|
||||
uint32_t fake_comm_session_is_latency_reduced(void);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Transport mock
|
||||
|
||||
//! Pointer to function handling data that is sent out by the session
|
||||
typedef void (*FakeTransportSentCallback)(uint16_t endpoint_id,
|
||||
const uint8_t* data, unsigned int data_length);
|
||||
|
||||
//! Creates a mock transport
|
||||
//! @param destination The destination type this transport is connected to. See comments with the
|
||||
//! TransportDestination struct for more info.
|
||||
//! @param app_uuid The UUID of the app that this transport is connected to.
|
||||
//! Pass NULL if this information is not known or irrelevant.
|
||||
//! @param sent_cb The callback that needs to be called whenever data is sent out using this mock
|
||||
//! transport. Note that data will only be sent out when fake_comm_session_process_send_next() is
|
||||
//! called. It's recommended to leave this NULL and use fake_transport_assert_sent instead.
|
||||
Transport *fake_transport_create(TransportDestination destination,
|
||||
const Uuid *app_uuid,
|
||||
FakeTransportSentCallback sent_cb);
|
||||
|
||||
//! Simulating (dis)connecting the transport.
|
||||
//! @return When connected, returns the opened CommSession. Returns NULL when disconnected.
|
||||
CommSession *fake_transport_set_connected(Transport *transport, bool connected);
|
||||
|
||||
//! Asserts the data of sent packets.
|
||||
//! @note This function can only be used when fake_transport_set_sent_cb is not used. They are
|
||||
//! mutually exclusive.
|
||||
//! @param index Packet index. Zero-based, newest packet first, oldest last.
|
||||
void fake_transport_assert_sent(Transport *transport, uint16_t index, uint16_t endpoint_id,
|
||||
const uint8_t data[], size_t length);
|
||||
|
||||
//! Asserts no data has been sent out.
|
||||
void fake_transport_assert_nothing_sent(Transport *transport);
|
||||
|
||||
//! Assigns a new callback that needs to be called whenever data is sent out using this mock
|
||||
//! transport. Note that data will only be sent out when fake_comm_session_process_send_next() is
|
||||
//! called. It's recommended to use fake_transport_assert_sent, because it results in tests that
|
||||
//! are much easier to read.
|
||||
void fake_transport_set_sent_cb(Transport *transport, FakeTransportSentCallback sent_cb);
|
||||
|
||||
//! Destroys the mock transport
|
||||
void fake_transport_destroy(Transport *transport);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Transport helper functions
|
||||
|
||||
//! Writes data into the fake send buffer, skipping Pebble Protocol
|
||||
//! @return false if there's insufficient space.
|
||||
bool fake_comm_session_send_buffer_write_raw_by_transport(Transport *transport,
|
||||
const uint8_t *data, size_t length);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Fake life cycle
|
||||
|
||||
void fake_comm_session_init(void);
|
||||
void fake_comm_session_cleanup(void);
|
69
tests/fakes/fake_session_send_buffer.c
Normal file
69
tests/fakes/fake_session_send_buffer.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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 "fake_session_send_buffer.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static SendBuffer *s_stub_send_buffer = (SendBuffer *) ~0;
|
||||
|
||||
SendBuffer * comm_session_send_buffer_begin_write(CommSession *session, uint16_t endpoint_id,
|
||||
size_t required_free_length,
|
||||
uint32_t timeout_ms) {
|
||||
if (!session) {
|
||||
return NULL;
|
||||
}
|
||||
return s_stub_send_buffer;
|
||||
}
|
||||
|
||||
bool comm_session_send_buffer_write(SendBuffer *send_buffer, const uint8_t *data, size_t length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void comm_session_send_buffer_end_write(SendBuffer *send_buffer) {
|
||||
}
|
||||
|
||||
static int s_send_buffer_create_count;
|
||||
static bool s_send_buffer_create_simulate_oom;
|
||||
|
||||
SendBuffer * comm_session_send_buffer_create(bool is_system) {
|
||||
++s_send_buffer_create_count;
|
||||
if (s_send_buffer_create_simulate_oom) {
|
||||
return NULL;
|
||||
} else {
|
||||
return (SendBuffer *) ~0;
|
||||
}
|
||||
}
|
||||
|
||||
static int s_send_buffer_destroy_count;
|
||||
|
||||
void comm_session_send_buffer_destroy(SendBuffer *sb) {
|
||||
++s_send_buffer_destroy_count;
|
||||
}
|
||||
|
||||
void fake_session_send_buffer_init(void) {
|
||||
s_send_buffer_create_count = 0;
|
||||
s_send_buffer_destroy_count = 0;
|
||||
s_send_buffer_create_simulate_oom = false;
|
||||
}
|
||||
|
||||
void fake_session_send_buffer_set_simulate_oom(bool enabled) {
|
||||
s_send_buffer_create_simulate_oom = enabled;
|
||||
}
|
||||
|
||||
SendBuffer *fake_session_send_buffer_get_buffer(void) {
|
||||
return s_stub_send_buffer;
|
||||
}
|
27
tests/fakes/fake_session_send_buffer.h
Normal file
27
tests/fakes/fake_session_send_buffer.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "services/common/comm_session/session_send_buffer.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void fake_session_send_buffer_init(void);
|
||||
|
||||
void fake_session_send_buffer_set_simulate_oom(bool enabled);
|
||||
|
||||
SendBuffer *fake_session_send_buffer_get_buffer(void);
|
197
tests/fakes/fake_settings_file.c
Normal file
197
tests/fakes/fake_settings_file.c
Normal file
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* 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 "services/normal/settings/settings_file.h"
|
||||
#include "system/status_codes.h"
|
||||
#include "util/crc8.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
struct {
|
||||
bool open;
|
||||
void *values[UINT8_MAX];
|
||||
void *keys[UINT8_MAX];
|
||||
uint32_t val_lens[UINT8_MAX];
|
||||
uint32_t key_lens[UINT8_MAX];
|
||||
bool dirty[UINT8_MAX];
|
||||
} s_settings_file;
|
||||
|
||||
void fake_settings_file_reset(void) {
|
||||
for (unsigned i = 0; i < UINT8_MAX; ++i) {
|
||||
free(s_settings_file.values[i]);
|
||||
s_settings_file.values[i] = NULL;
|
||||
free(s_settings_file.keys[i]);
|
||||
s_settings_file.keys[i] = NULL;
|
||||
s_settings_file.val_lens[i] = 0;
|
||||
s_settings_file.key_lens[i] = 0;
|
||||
s_settings_file.dirty[i] = false;
|
||||
}
|
||||
s_settings_file.open = false;
|
||||
}
|
||||
|
||||
status_t settings_file_get(SettingsFile *file, const void *key, size_t key_len,
|
||||
void *val_out, size_t val_out_len) {
|
||||
if (settings_file_exists(file, key, key_len)) {
|
||||
const uint8_t key_crc8 = crc8_calculate_bytes(key, key_len, false);
|
||||
memcpy(val_out, s_settings_file.values[key_crc8], val_out_len);
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
memset(val_out, 0, val_out_len);
|
||||
return E_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
status_t settings_file_set(SettingsFile *file, const void *key, size_t key_len,
|
||||
const void *val, size_t val_len) {
|
||||
void *val_copy = malloc(val_len);
|
||||
void *key_copy = malloc(key_len);
|
||||
cl_assert(val_copy && key_copy);
|
||||
memcpy(val_copy, val, val_len);
|
||||
memcpy(key_copy, key, key_len);
|
||||
const uint8_t key_crc8 = crc8_calculate_bytes(key, key_len, false);
|
||||
if (s_settings_file.values[key_crc8] != NULL) {
|
||||
cl_assert(memcmp(key, s_settings_file.keys[key_crc8], key_len) == 0);
|
||||
}
|
||||
|
||||
free(s_settings_file.values[key_crc8]);
|
||||
free(s_settings_file.keys[key_crc8]);
|
||||
s_settings_file.values[key_crc8] = val_copy;
|
||||
s_settings_file.keys[key_crc8] = key_copy;
|
||||
s_settings_file.val_lens[key_crc8] = val_len;
|
||||
s_settings_file.key_lens[key_crc8] = key_len;
|
||||
s_settings_file.dirty[key_crc8] = true;
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
int settings_file_get_len(SettingsFile *file, const void *key, size_t key_len) {
|
||||
if (settings_file_exists(file, key, key_len)) {
|
||||
const uint8_t key_crc8 = crc8_calculate_bytes(key, key_len, false);
|
||||
return s_settings_file.val_lens[key_crc8];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
status_t settings_file_delete(SettingsFile *file,
|
||||
const void *key, size_t key_len) {
|
||||
if (settings_file_exists(file, key, key_len)) {
|
||||
const uint8_t key_crc8 = crc8_calculate_bytes(key, key_len, false);
|
||||
free(s_settings_file.values[key_crc8]);
|
||||
free(s_settings_file.keys[key_crc8]);
|
||||
s_settings_file.values[key_crc8] = NULL;
|
||||
s_settings_file.keys[key_crc8] = NULL;
|
||||
s_settings_file.val_lens[key_crc8] = 0;
|
||||
s_settings_file.key_lens[key_crc8] = 0;
|
||||
s_settings_file.dirty[key_crc8] = false;
|
||||
return S_SUCCESS;
|
||||
} else {
|
||||
return E_DOES_NOT_EXIST;
|
||||
}
|
||||
}
|
||||
|
||||
status_t settings_file_open(SettingsFile *file, const char *name,
|
||||
int max_used_space) {
|
||||
if (s_settings_file.open) {
|
||||
return E_BUSY;
|
||||
} else {
|
||||
*file = (SettingsFile){};
|
||||
s_settings_file.open = true;
|
||||
return S_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
void settings_file_close(SettingsFile *file) {
|
||||
cl_assert(s_settings_file.open);
|
||||
s_settings_file.open = false;
|
||||
}
|
||||
|
||||
bool settings_file_exists(SettingsFile *file, const void *key, size_t key_len) {
|
||||
const uint8_t key_crc8 = crc8_calculate_bytes(key, key_len, false);
|
||||
return (s_settings_file.values[key_crc8] != NULL &&
|
||||
memcmp(s_settings_file.keys[key_crc8], key, key_len) == 0);
|
||||
}
|
||||
|
||||
status_t settings_file_mark_synced(SettingsFile *file, const void *key, size_t key_len) {
|
||||
if (settings_file_exists(file, key, key_len)) {
|
||||
const uint8_t key_crc8 = crc8_calculate_bytes(key, key_len, false);
|
||||
s_settings_file.dirty[key_crc8] = false;
|
||||
return S_SUCCESS;
|
||||
}
|
||||
return E_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
status_t settings_file_set_byte(SettingsFile *file, const void *key, size_t key_len, size_t offset,
|
||||
uint8_t byte) {
|
||||
if (settings_file_exists(file, key, key_len)) {
|
||||
const uint8_t key_crc8 = crc8_calculate_bytes(key, key_len, false);
|
||||
uint8_t *val_bytes = s_settings_file.values[key_crc8];
|
||||
val_bytes[offset] &= byte;
|
||||
return S_SUCCESS;
|
||||
} else {
|
||||
return E_DOES_NOT_EXIST;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t s_cur_itr;
|
||||
|
||||
static void prv_get_key(SettingsFile *file, void *key, size_t key_len) {
|
||||
memcpy(key, s_settings_file.keys[s_cur_itr], key_len);
|
||||
}
|
||||
|
||||
static void prv_get_val(SettingsFile *file, void *val, size_t val_len) {
|
||||
memcpy(val, s_settings_file.values[s_cur_itr], val_len);
|
||||
}
|
||||
|
||||
status_t settings_file_each(SettingsFile *file, SettingsFileEachCallback cb,
|
||||
void *context) {
|
||||
for (unsigned i = 0; i < UINT8_MAX; ++i) {
|
||||
if (s_settings_file.keys[i] != NULL &&
|
||||
s_settings_file.values[i] != NULL) {
|
||||
s_cur_itr = i;
|
||||
|
||||
SettingsRecordInfo info = {
|
||||
.get_key = prv_get_key,
|
||||
.get_val = prv_get_val,
|
||||
.key_len = s_settings_file.key_lens[i],
|
||||
.val_len = s_settings_file.val_lens[i],
|
||||
.dirty = s_settings_file.dirty[i],
|
||||
};
|
||||
if (!cb(file, &info, context)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
status_t settings_file_rewrite(SettingsFile *file,
|
||||
SettingsFileRewriteCallback cb,
|
||||
void *context) {
|
||||
// TODO
|
||||
fake_settings_file_reset();
|
||||
return S_SUCCESS;
|
||||
}
|
||||
|
||||
status_t settings_file_rewrite_filtered(SettingsFile *file,
|
||||
SettingsFileRewriteFilterCallback filter_cb,
|
||||
void *context) {
|
||||
// TODO
|
||||
fake_settings_file_reset();
|
||||
return S_SUCCESS;
|
||||
}
|
19
tests/fakes/fake_settings_file.h
Normal file
19
tests/fakes/fake_settings_file.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void fake_settings_file_reset(void);
|
155
tests/fakes/fake_shared_prf_storage.c
Normal file
155
tests/fakes/fake_shared_prf_storage.c
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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 "fake_shared_prf_storage.h"
|
||||
|
||||
#include <bluetooth/bluetooth_types.h>
|
||||
#include <bluetooth/sm_types.h>
|
||||
|
||||
static int s_prf_storage_ble_store_count;
|
||||
static int s_prf_storage_ble_delete_count;
|
||||
static int s_prf_storage_bt_classic_store_count;
|
||||
static int s_prf_storage_bt_classic_platform_bits_count;
|
||||
static int s_prf_storage_bt_classic_delete_count;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! Test functions
|
||||
|
||||
void fake_shared_prf_storage_reset_counts(void) {
|
||||
s_prf_storage_ble_store_count = 0;
|
||||
s_prf_storage_ble_delete_count = 0;
|
||||
s_prf_storage_bt_classic_store_count = 0;
|
||||
s_prf_storage_bt_classic_platform_bits_count = 0;
|
||||
s_prf_storage_bt_classic_delete_count = 0;
|
||||
}
|
||||
|
||||
int fake_shared_prf_storage_get_ble_store_count(void) {
|
||||
return s_prf_storage_ble_store_count;
|
||||
}
|
||||
|
||||
int fake_shared_prf_storage_get_ble_delete_count(void) {
|
||||
return s_prf_storage_ble_delete_count;
|
||||
}
|
||||
|
||||
int fake_shared_prf_storage_get_bt_classic_store_count(void) {
|
||||
return s_prf_storage_bt_classic_store_count;
|
||||
}
|
||||
|
||||
int fake_shared_prf_storage_get_bt_classic_platform_bits_count(void) {
|
||||
return s_prf_storage_bt_classic_platform_bits_count;
|
||||
}
|
||||
|
||||
int fake_shared_prf_storage_get_bt_classic_delete_count(void) {
|
||||
return s_prf_storage_bt_classic_delete_count;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! Custom Local Device Name
|
||||
|
||||
bool shared_prf_storage_get_local_device_name(char *local_device_name_out, size_t max_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void shared_prf_storage_set_local_device_name(char *local_device_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! BLE Root Keys
|
||||
|
||||
bool shared_prf_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void shared_prf_storage_set_root_keys(SM128BitKey *keys_in) {
|
||||
return;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! BLE Pairing Data
|
||||
|
||||
bool shared_prf_storage_get_ble_pairing_data(SMPairingInfo *pairing_info_out,
|
||||
char *name_out, bool *requires_address_pinning_out,
|
||||
uint8_t *flags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void shared_prf_storage_store_ble_pairing_data(const SMPairingInfo *pairing_info,
|
||||
char *name, bool requires_address_pinning,
|
||||
uint8_t flags) {
|
||||
s_prf_storage_ble_delete_count++;
|
||||
s_prf_storage_ble_store_count++;
|
||||
}
|
||||
|
||||
void shared_prf_storage_erase_ble_pairing_data(void) {
|
||||
s_prf_storage_ble_delete_count++;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! BT Classic Pairing Data
|
||||
|
||||
bool shared_prf_storage_get_bt_classic_pairing_data(BTDeviceAddress *addr_out,
|
||||
char *device_name_out,
|
||||
SM128BitKey *link_key_out,
|
||||
uint8_t *platform_bits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void shared_prf_storage_store_bt_classic_pairing_data(BTDeviceAddress *addr,
|
||||
char *device_name,
|
||||
SM128BitKey *link_key,
|
||||
uint8_t platform_bits) {
|
||||
s_prf_storage_bt_classic_delete_count++;
|
||||
s_prf_storage_bt_classic_store_count++;
|
||||
}
|
||||
|
||||
void shared_prf_storage_store_platform_bits(uint8_t platform_bits) {
|
||||
s_prf_storage_bt_classic_platform_bits_count++;
|
||||
}
|
||||
|
||||
void shared_prf_storage_erase_bt_classic_pairing_data(void) {
|
||||
s_prf_storage_bt_classic_delete_count++;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! Getting Started Is Complete
|
||||
|
||||
bool shared_prf_storage_get_getting_started_complete(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void shared_prf_storage_set_getting_started_complete(bool set) {
|
||||
return;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! Factory Reset
|
||||
|
||||
void shared_prf_storage_wipe_all(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! Pinned Address
|
||||
|
||||
bool shared_prf_storage_get_ble_pinned_address(BTDeviceAddress *address_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Stores the new BLE Pinned Address in the shared storage.
|
||||
void shared_prf_storage_set_ble_pinned_address(const BTDeviceAddress *address) {
|
||||
}
|
24
tests/fakes/fake_shared_prf_storage.h
Normal file
24
tests/fakes/fake_shared_prf_storage.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void fake_shared_prf_storage_reset_counts(void);
|
||||
int fake_shared_prf_storage_get_ble_store_count(void);
|
||||
int fake_shared_prf_storage_get_ble_delete_count(void);
|
||||
int fake_shared_prf_storage_get_bt_classic_store_count(void);
|
||||
int fake_shared_prf_storage_get_bt_classic_platform_bits_count(void);
|
||||
int fake_shared_prf_storage_get_bt_classic_delete_count(void);
|
26
tests/fakes/fake_smartstrap_connection.c
Normal file
26
tests/fakes/fake_smartstrap_connection.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 "fake_smartstrap_connection.h"
|
||||
|
||||
void smartstrap_connection_kick_monitor(void) {
|
||||
}
|
||||
|
||||
void sys_smartstrap_subscribe(void) {
|
||||
}
|
||||
|
||||
void sys_smartstrap_unsubscribe(void) {
|
||||
}
|
21
tests/fakes/fake_smartstrap_connection.h
Normal file
21
tests/fakes/fake_smartstrap_connection.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void smartstrap_connection_kick_monitor(void);
|
||||
void sys_smartstrap_subscribe(void);
|
||||
void sys_smartstrap_unsubscribe(void);
|
80
tests/fakes/fake_smartstrap_profiles.c
Normal file
80
tests/fakes/fake_smartstrap_profiles.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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 "fake_smartstrap_profiles.h"
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
static bool s_did_read;
|
||||
static bool s_read_success;
|
||||
static SmartstrapProfile s_read_profile;
|
||||
static uint32_t s_read_length;
|
||||
|
||||
static bool s_did_notify;
|
||||
static bool s_notify_success;
|
||||
static bool s_notify_profile;
|
||||
|
||||
static bool s_did_request;
|
||||
static SmartstrapRequest s_request;
|
||||
|
||||
|
||||
void smartstrap_profiles_handle_read(bool success, SmartstrapProfile profile, uint32_t length) {
|
||||
s_read_success = success;
|
||||
s_read_profile = profile;
|
||||
s_read_length = length;
|
||||
s_did_read = true;
|
||||
}
|
||||
|
||||
void smartstrap_profiles_handle_read_aborted(SmartstrapProfile profile) {
|
||||
}
|
||||
|
||||
void fake_smartstrap_profiles_check_read_params(bool success, SmartstrapProfile profile,
|
||||
uint32_t length) {
|
||||
cl_assert(s_did_read);
|
||||
cl_assert(success == s_read_success);
|
||||
cl_assert(profile == s_read_profile);
|
||||
cl_assert(length == s_read_length);
|
||||
s_did_read = false;
|
||||
}
|
||||
|
||||
void smartstrap_profiles_handle_notification(bool success, SmartstrapProfile profile) {
|
||||
s_notify_success = success;
|
||||
s_notify_profile = profile;
|
||||
s_did_notify = true;
|
||||
}
|
||||
|
||||
void fake_smartstrap_profiles_check_notify_params(bool success, SmartstrapProfile profile) {
|
||||
cl_assert(s_did_notify);
|
||||
cl_assert(success = s_notify_success);
|
||||
cl_assert(profile = s_notify_profile);
|
||||
s_did_notify = false;
|
||||
}
|
||||
|
||||
SmartstrapResult smartstrap_profiles_handle_request(const SmartstrapRequest *request) {
|
||||
s_request = *request;
|
||||
s_did_request = true;
|
||||
return SmartstrapResultOk;
|
||||
}
|
||||
|
||||
void fake_smartstrap_profiles_check_request_params(const SmartstrapRequest *request) {
|
||||
cl_assert(s_did_request);
|
||||
cl_assert(s_request.service_id == request->service_id);
|
||||
cl_assert(s_request.attribute_id == request->attribute_id);
|
||||
cl_assert((s_request.write_mbuf == NULL) == (request->write_mbuf == NULL));
|
||||
cl_assert((s_request.read_mbuf == NULL) == (request->read_mbuf == NULL));
|
||||
cl_assert(s_request.timeout_ms == request->timeout_ms);
|
||||
s_did_request = false;
|
||||
}
|
37
tests/fakes/fake_smartstrap_profiles.h
Normal file
37
tests/fakes/fake_smartstrap_profiles.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "services/normal/accessory/smartstrap_comms.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void smartstrap_profiles_handle_read(bool success, SmartstrapProfile profile, uint32_t length);
|
||||
|
||||
void fake_smartstrap_profiles_check_read_params(bool success, SmartstrapProfile profile,
|
||||
uint32_t length);
|
||||
|
||||
void smartstrap_profiles_handle_notification(bool success, SmartstrapProfile profile);
|
||||
|
||||
void fake_smartstrap_profiles_check_notify_params(bool success, SmartstrapProfile profile);
|
||||
|
||||
SmartstrapResult smartstrap_profiles_handle_request(const SmartstrapRequest *request);
|
||||
|
||||
void smartstrap_profiles_handle_read_aborted(SmartstrapProfile profile);
|
||||
|
||||
void fake_smartstrap_profiles_check_request_params(const SmartstrapRequest *request);
|
86
tests/fakes/fake_smartstrap_state.c
Normal file
86
tests/fakes/fake_smartstrap_state.c
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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_asserts.h"
|
||||
|
||||
#include "fake_smartstrap_state.h"
|
||||
|
||||
static bool s_locked = false;
|
||||
static SmartstrapState s_fsm_state = SmartstrapStateUnsubscribed;
|
||||
|
||||
static void prv_check_fsm_transition(SmartstrapState prev_state, SmartstrapState new_state) {
|
||||
if (new_state == SmartstrapStateUnsubscribed) {
|
||||
} else if ((prev_state == SmartstrapStateUnsubscribed) &&
|
||||
(new_state == SmartstrapStateReadReady)) {
|
||||
} else if ((prev_state == SmartstrapStateReadReady) &&
|
||||
(new_state == SmartstrapStateNotifyInProgress)) {
|
||||
} else if ((prev_state == SmartstrapStateReadReady) &&
|
||||
(new_state == SmartstrapStateReadDisabled)) {
|
||||
} else if ((prev_state == SmartstrapStateNotifyInProgress) &&
|
||||
(new_state == SmartstrapStateReadComplete)) {
|
||||
} else if ((prev_state == SmartstrapStateReadDisabled) &&
|
||||
(new_state == SmartstrapStateReadInProgress)) {
|
||||
} else if ((prev_state == SmartstrapStateReadDisabled) &&
|
||||
(new_state == SmartstrapStateReadReady)) {
|
||||
} else if ((prev_state == SmartstrapStateReadInProgress) &&
|
||||
(new_state == SmartstrapStateReadComplete)) {
|
||||
} else if ((prev_state == SmartstrapStateReadComplete) &&
|
||||
(new_state == SmartstrapStateReadReady)) {
|
||||
} else {
|
||||
// all other transitions are invalid
|
||||
cl_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
SmartstrapState smartstrap_fsm_state_get(void) {
|
||||
return s_fsm_state;
|
||||
}
|
||||
|
||||
void smartstrap_fsm_state_reset(void) {
|
||||
s_fsm_state = SmartstrapStateReadReady;
|
||||
}
|
||||
|
||||
bool smartstrap_fsm_state_test_and_set(SmartstrapState expected_state, SmartstrapState next_state) {
|
||||
if (s_fsm_state != expected_state) {
|
||||
return false;
|
||||
}
|
||||
prv_check_fsm_transition(s_fsm_state, next_state);
|
||||
s_fsm_state = next_state;
|
||||
return true;
|
||||
}
|
||||
|
||||
void smartstrap_fsm_state_set(SmartstrapState next_state) {
|
||||
prv_check_fsm_transition(s_fsm_state, next_state);
|
||||
s_fsm_state = next_state;
|
||||
}
|
||||
|
||||
void smartstrap_state_lock(void) {
|
||||
cl_assert(!s_locked);
|
||||
s_locked = true;
|
||||
}
|
||||
|
||||
void smartstrap_state_unlock(void) {
|
||||
cl_assert(s_locked);
|
||||
s_locked = false;
|
||||
}
|
||||
|
||||
void smartstrap_state_assert_locked_by_current_task(void) {
|
||||
cl_assert(s_locked);
|
||||
}
|
||||
|
||||
bool sys_smartstrap_is_service_connected(uint16_t service_id) {
|
||||
return true;
|
||||
}
|
39
tests/fakes/fake_smartstrap_state.h
Normal file
39
tests/fakes/fake_smartstrap_state.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
SmartstrapStateUnsubscribed,
|
||||
SmartstrapStateReadReady,
|
||||
SmartstrapStateNotifyInProgress,
|
||||
SmartstrapStateReadDisabled,
|
||||
SmartstrapStateReadInProgress,
|
||||
SmartstrapStateReadComplete
|
||||
} SmartstrapState;
|
||||
|
||||
|
||||
SmartstrapState smartstrap_fsm_state_get(void);
|
||||
void smartstrap_fsm_state_reset(void);
|
||||
bool smartstrap_fsm_state_test_and_set(SmartstrapState expected_state, SmartstrapState next_state);
|
||||
void smartstrap_fsm_state_set(SmartstrapState next_state);
|
||||
void smartstrap_state_lock(void);
|
||||
void smartstrap_state_unlock(void);
|
||||
void smartstrap_state_assert_locked_by_current_task(void);
|
||||
bool sys_smartstrap_is_service_connected(uint16_t service_id);
|
195
tests/fakes/fake_spi_flash.c
Normal file
195
tests/fakes/fake_spi_flash.c
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* 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 "fake_spi_flash.h"
|
||||
|
||||
#include "flash_region/flash_region.h"
|
||||
#include "system/status_codes.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "clar_asserts.h"
|
||||
|
||||
typedef struct FakeFlashState {
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
uint32_t bytes_left_till_write_failure;
|
||||
jmp_buf *jmp_on_failure;
|
||||
uint8_t* storage; //! Allocated buffer of length bytes.
|
||||
uint32_t write_count;
|
||||
uint32_t erase_count;
|
||||
} FakeFlashState;
|
||||
|
||||
static FakeFlashState s_state = { 0 };
|
||||
|
||||
void fake_spi_flash_erase(void) {
|
||||
memset(s_state.storage, 0xff, s_state.length);
|
||||
}
|
||||
|
||||
void fake_spi_flash_cleanup(void) {
|
||||
free(s_state.storage);
|
||||
s_state.storage = NULL;
|
||||
s_state = (FakeFlashState) { 0 };
|
||||
}
|
||||
|
||||
//! @param offset the offset at which this fake region of flash begins.
|
||||
//! @param length the length of this fake region of flash.
|
||||
void fake_spi_flash_init(uint32_t offset, uint32_t length) {
|
||||
// Clients are not required to cleanup due to prior code, so do so here.
|
||||
fake_spi_flash_cleanup();
|
||||
|
||||
s_state.offset = offset;
|
||||
s_state.length = length;
|
||||
s_state.storage = malloc(length);
|
||||
s_state.write_count = 0;
|
||||
// Note: this is a harness failure, not a code failure.
|
||||
cl_assert(s_state.storage != NULL);
|
||||
memset(s_state.storage, 0xff, length);
|
||||
}
|
||||
|
||||
void fake_flash_assert_region_untouched(uint32_t start_addr, uint32_t length) {
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
cl_assert(s_state.storage[start_addr + i] == 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t fake_spi_flash_find_next_write(int32_t offset) {
|
||||
if(offset < s_state.offset || offset >= s_state.offset + s_state.length) {
|
||||
return E_RANGE;
|
||||
}
|
||||
do {
|
||||
if(s_state.storage[offset] != 0xff) {
|
||||
return offset;
|
||||
}
|
||||
offset++;
|
||||
} while(offset < s_state.offset + s_state.length);
|
||||
return E_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
void fake_spi_flash_populate_from_file(char *path, uint32_t offset) {
|
||||
cl_assert(s_state.storage);
|
||||
cl_assert(offset >= s_state.offset);
|
||||
cl_assert((offset - s_state.offset) <= s_state.length);
|
||||
|
||||
// find the offset in the storage array
|
||||
uint32_t fake_offset = offset - s_state.offset;
|
||||
|
||||
// check that file exists and fits in buffer
|
||||
struct stat st;
|
||||
cl_assert(stat(path, &st) == 0);
|
||||
cl_assert(st.st_size < (s_state.length - fake_offset));
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
cl_assert(file);
|
||||
|
||||
// copy file to fake flash storage
|
||||
cl_assert(fread(&s_state.storage[fake_offset], 1, st.st_size, file) > 0);
|
||||
}
|
||||
|
||||
void fake_spi_flash_force_future_failure(int after_n_bytes, jmp_buf *retire_to) {
|
||||
s_state.bytes_left_till_write_failure = after_n_bytes;
|
||||
s_state.jmp_on_failure = retire_to;
|
||||
}
|
||||
|
||||
void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) {
|
||||
cl_assert(start_addr >= s_state.offset);
|
||||
cl_assert(start_addr + buffer_size <= s_state.offset + s_state.length);
|
||||
|
||||
memcpy(buffer, s_state.storage + (start_addr - s_state.offset), buffer_size);
|
||||
}
|
||||
|
||||
void flash_write_bytes(const uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) {
|
||||
cl_assert(start_addr >= s_state.offset);
|
||||
cl_assert(start_addr + buffer_size <= s_state.offset + s_state.length);
|
||||
|
||||
++s_state.write_count;
|
||||
|
||||
for (int i = 0; i < buffer_size; ++i) {
|
||||
if (s_state.jmp_on_failure != NULL) {
|
||||
if (s_state.bytes_left_till_write_failure == 0) {
|
||||
longjmp(*s_state.jmp_on_failure, 1);
|
||||
} else {
|
||||
s_state.bytes_left_till_write_failure--;
|
||||
}
|
||||
}
|
||||
// 0 write 0 = 0
|
||||
// 1 write 0 = 0
|
||||
// 1 write 1 = 1
|
||||
// 0 write 1 = 0
|
||||
s_state.storage[start_addr - s_state.offset + i] &= buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
//! @param block_size must be a power of two
|
||||
static void erase_block(uint32_t block_addr, uint32_t block_size) {
|
||||
++s_state.erase_count;
|
||||
|
||||
const uint32_t block_mask = ~(block_size - 1);
|
||||
uint32_t block_start = block_addr & block_mask;
|
||||
|
||||
cl_assert(block_start >= s_state.offset);
|
||||
if (block_start + block_size > s_state.offset + s_state.length) {
|
||||
printf("-0x%x 0x%x\n", block_start + block_size, s_state.offset + s_state.length);
|
||||
}
|
||||
cl_assert(block_start + block_size <= s_state.offset + s_state.length);
|
||||
|
||||
memset(&s_state.storage[block_start - s_state.offset], 0xff, block_size);
|
||||
}
|
||||
|
||||
void flash_erase_sector_blocking(uint32_t sector_addr) {
|
||||
#if PLATFORM_SNOWY
|
||||
if (sector_addr <= BOTTOM_BOOT_REGION_END) {
|
||||
erase_block(sector_addr, BOTTOM_BOOT_SECTOR_SIZE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
erase_block(sector_addr, SECTOR_SIZE_BYTES);
|
||||
}
|
||||
|
||||
uint32_t flash_get_subsector_base_address(uint32_t flash_addr) {
|
||||
return flash_addr & ~(SUBSECTOR_SIZE_BYTES - 1);
|
||||
}
|
||||
|
||||
void flash_erase_subsector_blocking(uint32_t subsector_addr) {
|
||||
erase_block(subsector_addr, SUBSECTOR_SIZE_BYTES);
|
||||
}
|
||||
|
||||
uint32_t flash_get_sector_base_address(uint32_t flash_addr) {
|
||||
#if PLATFORM_SNOWY
|
||||
if (flash_addr <= BOTTOM_BOOT_REGION_END) {
|
||||
return (flash_addr & ~(BOTTOM_BOOT_SECTOR_SIZE - 1));
|
||||
}
|
||||
#endif
|
||||
|
||||
return (flash_addr & ~(SECTOR_SIZE_BYTES - 1));
|
||||
}
|
||||
|
||||
uint32_t fake_flash_write_count(void) {
|
||||
return s_state.write_count;
|
||||
}
|
||||
|
||||
uint32_t fake_flash_erase_count(void) {
|
||||
return s_state.erase_count;
|
||||
}
|
47
tests/fakes/fake_spi_flash.h
Normal file
47
tests/fakes/fake_spi_flash.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
/*!
|
||||
Initialize the fake SPI flash region.
|
||||
|
||||
@param offset the offset at which this fake region of flash begins.
|
||||
@param length the length of this fake region of flash.
|
||||
*/
|
||||
void fake_spi_flash_init(uint32_t offset, uint32_t length);
|
||||
|
||||
//! Add data to the fake spi flash from a file on your local filesystem
|
||||
void fake_spi_flash_populate_from_file(char *path, uint32_t offset);
|
||||
|
||||
//! Cleanup the fake SPI flash region, freeing all resources.
|
||||
void fake_spi_flash_erase(void);
|
||||
|
||||
//! Cleanup the fake SPI flash region, freeing all resources.
|
||||
void fake_spi_flash_cleanup(void);
|
||||
|
||||
//! Force the SPI flash to fail at some point in the future, jumping back to
|
||||
//! the given jmp_buf when it does so. This is intended for use in verifying
|
||||
//! the atomicity of algorithms which are purported to be so.
|
||||
void fake_spi_flash_force_future_failure(int after_n_bytes, jmp_buf *retire_to);
|
||||
|
||||
void fake_flash_assert_region_untouched(uint32_t start_addr, uint32_t length);
|
||||
|
||||
uint32_t fake_flash_write_count(void);
|
||||
uint32_t fake_flash_erase_count(void);
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue