mirror of
https://github.com/google/pebble
synced 2026-10-17 16:54:11 +02:00
Import of the watch repository from Pebble
This commit is contained in:
@@ -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 "services/common/analytics/analytics_event.h"
|
||||
#include "services/common/analytics/analytics_logging.h"
|
||||
#include "services/common/comm_session/session.h"
|
||||
#include "services/common/comm_session/session_analytics.h"
|
||||
|
||||
#include <util/uuid.h>
|
||||
|
||||
#include <clar.h>
|
||||
|
||||
//
|
||||
// Stubs
|
||||
//
|
||||
#include "stubs_bt_lock.h"
|
||||
#include "stubs_passert.h"
|
||||
|
||||
CommSessionTransportType comm_session_analytics_get_transport_type(CommSession *session) {
|
||||
return CommSessionTransportType_PlainSPP;
|
||||
}
|
||||
|
||||
bool comm_session_is_valid(const CommSession *session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct GAPLEConnection GAPLEConnection;
|
||||
|
||||
GAPLEConnection *gap_le_connection_get_gateway(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const PebbleProcessMd *launcher_menu_app_get_app_info(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void sys_analytics_logging_log_event(AnalyticsEventBlob *event_blob) {
|
||||
}
|
||||
|
||||
bool sys_process_manager_get_current_process_uuid(Uuid *uuid_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Fakes & helpers
|
||||
//
|
||||
|
||||
#define TEST_EVENT_BLOB_VERSION (0)
|
||||
#define TEST_EVENT_TIMESTAMP (0)
|
||||
|
||||
static AnalyticsEventBlob s_last_blob;
|
||||
void analytics_logging_log_event(AnalyticsEventBlob *event_blob) {
|
||||
s_last_blob = *event_blob;
|
||||
s_last_blob.version = TEST_EVENT_BLOB_VERSION;
|
||||
s_last_blob.timestamp = TEST_EVENT_TIMESTAMP;
|
||||
s_last_blob.kind = ANALYTICS_BLOB_KIND_EVENT;
|
||||
}
|
||||
|
||||
#define cl_assert_equal_last_blob(b) \
|
||||
cl_assert_equal_m(&s_last_blob, &b, sizeof(s_last_blob));
|
||||
|
||||
//
|
||||
// Tests
|
||||
//
|
||||
void test_analytics_event__initialization(void) {
|
||||
s_last_blob = (AnalyticsEventBlob) {};
|
||||
}
|
||||
|
||||
void test_analytics_event__cleanup(void) {
|
||||
|
||||
}
|
||||
|
||||
void test_analytics_event__analytics_event_app_crash(void) {
|
||||
const Uuid app_uuid = UuidMake(0xBE, 0x85, 0x14, 0x68, 0x70, 0x21, 0x43, 0xC6,
|
||||
0xAB, 0x44, 0xB8, 0x36, 0xFC, 0xD0, 0x33, 0x04);
|
||||
const uint32_t pc = 0x8888888;
|
||||
const uint32_t lr = 0x2222222;
|
||||
const uint8_t build_id[BUILD_ID_EXPECTED_LEN] = {
|
||||
0x53, 0x98, 0xB6, 0x7E, 0x98, 0xA2, 0x44, 0x35, 0x67, 0x9B,
|
||||
0xA4, 0xB0, 0x08, 0x95, 0xB8, 0x8F, 0x14, 0xDA, 0x5A, 0x11,
|
||||
};
|
||||
|
||||
AnalyticsEventBlob expected_blob = {
|
||||
.kind = ANALYTICS_BLOB_KIND_EVENT,
|
||||
.version = TEST_EVENT_BLOB_VERSION,
|
||||
.timestamp = TEST_EVENT_TIMESTAMP,
|
||||
.event = AnalyticsEvent_AppCrash,
|
||||
.app_crash_report = {
|
||||
.uuid = app_uuid,
|
||||
.pc = pc,
|
||||
.lr = lr,
|
||||
.build_id_slice = {
|
||||
build_id[0], build_id[1], build_id[2], build_id[3],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Non-Rocky.js app:
|
||||
analytics_event_app_crash(&app_uuid, pc, lr, build_id, false /* is_rocky_app */);
|
||||
expected_blob.event = AnalyticsEvent_AppCrash;
|
||||
cl_assert_equal_last_blob(expected_blob);
|
||||
|
||||
// Rocky.js app:
|
||||
expected_blob.event = AnalyticsEvent_RockyAppCrash;
|
||||
analytics_event_app_crash(&app_uuid, pc, lr, build_id, true /* is_rocky_app */);
|
||||
cl_assert_equal_last_blob(expected_blob);
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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/analytics/analytics_heartbeat.h"
|
||||
#include "services/common/analytics/analytics_metric.h"
|
||||
#include "clar.h"
|
||||
#include "fake_kernel_malloc.h"
|
||||
#include "fake_rtc.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_mutex.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pebble_tasks.h"
|
||||
#include "stubs_rand_ptr.h"
|
||||
|
||||
void test_analytics_heartbeat__initialize(void) {
|
||||
analytics_metric_init();
|
||||
}
|
||||
|
||||
void test_analytics_heartbeat__cleanup(void) {
|
||||
|
||||
}
|
||||
|
||||
static Uuid test_uuid = (Uuid){0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
|
||||
0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF};
|
||||
|
||||
// A minimal, basic test that heartbeats don't overwrite adjacent data when
|
||||
// fields next to eachother are set. We set UUID first (well, create_app does),
|
||||
// and then set the fields on either side, and verify that UUID remains
|
||||
// unchanged.
|
||||
// struct AppHeartbeat {
|
||||
// [...]
|
||||
// uint32 TIME_INTERVAL
|
||||
// Uuid UUID
|
||||
// uint8 SDK_MAJOR_VERSION
|
||||
// [...]
|
||||
// }
|
||||
void test_analytics_heartbeat__test_read_write_sanity(void) {
|
||||
// Set Metrics
|
||||
AnalyticsHeartbeat *heartbeat = analytics_heartbeat_app_create(&test_uuid);
|
||||
int64_t time_interval = 0x10111213;
|
||||
analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_TIME_INTERVAL, time_interval);
|
||||
int64_t sdk_major_version = 0x14;
|
||||
analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_SDK_MAJOR_VERSION, sdk_major_version);
|
||||
|
||||
// Verify that things were set as expected, and adjacent metrics were not
|
||||
// overwritten.
|
||||
int64_t got_time_interval = analytics_heartbeat_get(heartbeat, ANALYTICS_APP_METRIC_TIME_INTERVAL);
|
||||
cl_assert(time_interval == got_time_interval);
|
||||
int64_t got_sdk_major_version = analytics_heartbeat_get(heartbeat, ANALYTICS_APP_METRIC_SDK_MAJOR_VERSION);
|
||||
cl_assert(sdk_major_version == got_sdk_major_version);
|
||||
for (int i = 0; i < sizeof(test_uuid); i++) {
|
||||
int64_t expected_uuid_byte = i;
|
||||
int64_t got_uuid_byte = analytics_heartbeat_get_array(heartbeat, ANALYTICS_APP_METRIC_UUID, i);
|
||||
cl_assert(got_uuid_byte == expected_uuid_byte);
|
||||
}
|
||||
|
||||
kernel_free(heartbeat);
|
||||
}
|
||||
|
||||
// Repeat a given bit several times.
|
||||
static int64_t pattern(uint8_t i, size_t n) {
|
||||
uint64_t pat = 0;
|
||||
uint64_t mask = i;
|
||||
for (size_t j = 0; j < n; j++) {
|
||||
pat |= mask << (j*8);
|
||||
}
|
||||
pat &= 0x7f; // truncate so there are no overflows
|
||||
return *(int64_t*)&pat;
|
||||
}
|
||||
|
||||
static void verify_metric(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, uint8_t i, size_t j) {
|
||||
uint32_t item_size = analytics_metric_element_size(metric);
|
||||
int64_t expected = pattern(i, item_size);
|
||||
int64_t got;
|
||||
if (j == -1) {
|
||||
got = analytics_heartbeat_get(heartbeat, metric);
|
||||
} else {
|
||||
got = analytics_heartbeat_get_array(heartbeat, metric, j);
|
||||
}
|
||||
printf("Expected %"PRIx64", got %"PRIx64" (item_size=%"PRIu32")\n",
|
||||
expected, got, item_size);
|
||||
cl_assert(got == expected);
|
||||
}
|
||||
|
||||
void test_analytics_heartbeat__clipping(void) {
|
||||
AnalyticsHeartbeat *heartbeat = analytics_heartbeat_app_create(&test_uuid);
|
||||
int64_t time_interval = 0x10111213;
|
||||
|
||||
// uint8_t overflow
|
||||
analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_LAUNCH_COUNT, 300);
|
||||
uint8_t val8 = analytics_heartbeat_get(heartbeat,
|
||||
ANALYTICS_APP_METRIC_LAUNCH_COUNT);
|
||||
cl_assert(val8 == (uint8_t)0xff);
|
||||
analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_LAUNCH_COUNT, 80);
|
||||
val8 = analytics_heartbeat_get(heartbeat,
|
||||
ANALYTICS_APP_METRIC_LAUNCH_COUNT);
|
||||
cl_assert(val8 == 80);
|
||||
|
||||
// uint16_t overflow
|
||||
analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_MSG_DROP_COUNT,
|
||||
70000);
|
||||
analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_MSG_DROP_COUNT,
|
||||
70001);
|
||||
uint16_t val16 = analytics_heartbeat_get(heartbeat,
|
||||
ANALYTICS_APP_METRIC_MSG_DROP_COUNT);
|
||||
cl_assert(val16 == (uint16_t)0xffff);
|
||||
|
||||
// uint32_t overflow
|
||||
analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_MSG_BYTE_IN_COUNT,
|
||||
((uint64_t)0x1) << 34);
|
||||
uint32_t val32 = analytics_heartbeat_get(heartbeat,
|
||||
ANALYTICS_APP_METRIC_MSG_BYTE_IN_COUNT);
|
||||
cl_assert(val32 == (uint32_t)0xffffffff);
|
||||
|
||||
kernel_free(heartbeat);
|
||||
}
|
||||
|
||||
// Set every single app metric defined in the app heartbeat, and verify they
|
||||
// are read out correctly, without overwriting any adjacent fields. Our malloc()
|
||||
// mock also verifies that we don't write past the end of the heartbeat.
|
||||
void test_analytics_heartbeat__test_read_write_all_app_metrics(void) {
|
||||
printf("Starting test...\n");
|
||||
AnalyticsHeartbeat *heartbeat = analytics_heartbeat_app_create(&test_uuid);
|
||||
|
||||
uint8_t i = 0x80;
|
||||
for (AnalyticsMetric metric = ANALYTICS_APP_METRIC_START + 1;
|
||||
metric < ANALYTICS_APP_METRIC_END; metric++) {
|
||||
if (analytics_metric_is_array(metric)) {
|
||||
for (int j = 0; j < analytics_metric_num_elements(metric); j++) {
|
||||
analytics_heartbeat_set_array(heartbeat, metric, j, pattern(i, 8));
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
analytics_heartbeat_set(heartbeat, metric, pattern(i, 8));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
analytics_heartbeat_print(heartbeat);
|
||||
|
||||
i = 0x80;
|
||||
for (AnalyticsMetric metric = ANALYTICS_APP_METRIC_START + 1;
|
||||
metric < ANALYTICS_APP_METRIC_END; metric++) {
|
||||
if (analytics_metric_is_array(metric)) {
|
||||
for (int j = 0; j < analytics_metric_num_elements(metric); j++) {
|
||||
verify_metric(heartbeat, metric, i, j);
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
verify_metric(heartbeat, metric, i, -1);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
kernel_free(heartbeat);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
from waftools.pebble_test import clar
|
||||
|
||||
def build(ctx):
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/services/normal/analytics/analytics_heartbeat.c" \
|
||||
" src/fw/services/normal/analytics/analytics_metric.c" \
|
||||
" tests/fakes/fake_rtc.c" \
|
||||
" src/fw/util/rand/rand.c" \
|
||||
" src/fw/vendor/tinymt32/tinymt32.c",
|
||||
test_sources_ant_glob = "test_analytics_heartbeat.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob=(
|
||||
" src/fw/services/normal/analytics/analytics_event.c"
|
||||
),
|
||||
test_sources_ant_glob="test_analytics_event.c",
|
||||
override_includes=['dummy_board'])
|
||||
Reference in New Issue
Block a user