/* * 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); }