system_test.cpp
1 #ifdef PIO_UNIT_TESTING 2 3 #include <config.h> 4 #include <testing/utils.h> 5 6 namespace hardware::system { void test(void); } 7 8 #include <Arduino.h> 9 #include <Esp.h> 10 #include <freertos/FreeRTOS.h> 11 #include <freertos/task.h> 12 13 // ───────────────────────────────────────────────────────────────────────────── 14 // Chip temperature 15 // ───────────────────────────────────────────────────────────────────────────── 16 17 static void test_system_temperature_read(void) { 18 WHEN("the chip temperature is read"); 19 20 float temp = temperatureRead(); 21 char msg[32]; 22 snprintf(msg, sizeof(msg), "chip temperature: %.1f C", temp); 23 TEST_MESSAGE(msg); 24 25 TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(temp, 26 "device: chip temperature is NaN or Inf"); 27 TEST_ASSERT_GREATER_OR_EQUAL_FLOAT_MESSAGE(0.0f, temp, 28 "device: chip temperature below 0 C"); 29 TEST_ASSERT_LESS_OR_EQUAL_FLOAT_MESSAGE(100.0f, temp, 30 "device: chip temperature above 100 C"); 31 } 32 33 // ───────────────────────────────────────────────────────────────────────────── 34 // FreeRTOS task list 35 // ───────────────────────────────────────────────────────────────────────────── 36 37 static void test_system_task_list(void) { 38 WHEN("the FreeRTOS task list is queried"); 39 40 uint32_t count = uxTaskGetNumberOfTasks(); 41 TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(0, count, 42 "device: no tasks running"); 43 44 TaskStatus_t *tasks = (TaskStatus_t *)malloc(count * sizeof(TaskStatus_t)); 45 TEST_ASSERT_NOT_NULL_MESSAGE(tasks, "device: malloc failed for task list"); 46 47 uint32_t total_runtime = 0; 48 uint32_t filled = uxTaskGetSystemState(tasks, count, &total_runtime); 49 50 static const char *states[] = {"Running", "Ready", "Blocked", "Suspend", "Deleted", "Invalid"}; 51 52 for (uint32_t i = 0; i < filled; i++) { 53 int state = (int)tasks[i].eCurrentState; 54 if (state > 5) state = 5; 55 int core = (int)tasks[i].xCoreID; 56 char core_str[4]; 57 if (core == tskNO_AFFINITY) snprintf(core_str, sizeof(core_str), "*"); 58 else snprintf(core_str, sizeof(core_str), "%d", core); 59 60 char line[80]; 61 snprintf(line, sizeof(line), "%-16s %8s prio=%lu stack=%lu core=%s", 62 tasks[i].pcTaskName, states[state], 63 (unsigned long)tasks[i].uxCurrentPriority, 64 (unsigned long)tasks[i].usStackHighWaterMark, core_str); 65 TEST_MESSAGE(line); 66 } 67 68 free(tasks); 69 70 TEST_PRINTF("%lu tasks total", (unsigned long)filled); 71 } 72 73 // ───────────────────────────────────────────────────────────────────────────── 74 // Firmware integrity (sketch MD5, size, free space) 75 // ───────────────────────────────────────────────────────────────────────────── 76 77 static void test_system_sketch_md5(void) { 78 WHEN("the firmware integrity hash is read"); 79 80 String md5 = ESP.getSketchMD5(); 81 TEST_ASSERT_EQUAL_UINT32_MESSAGE(32, md5.length(), 82 "device: sketch MD5 should be 32-char hex string"); 83 84 TEST_PRINTF("sketch MD5: %s", md5.c_str()); 85 } 86 87 static void test_system_sketch_size(void) { 88 WHEN("the firmware size and free OTA space are queried"); 89 90 uint32_t sketch_size = ESP.getSketchSize(); 91 uint32_t free_space = ESP.getFreeSketchSpace(); 92 93 TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(0, sketch_size, 94 "device: sketch size should be > 0"); 95 TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(0, free_space, 96 "device: free sketch space should be > 0"); 97 98 TEST_PRINTF("sketch: %lu KB, free OTA: %lu KB", 99 (unsigned long)(sketch_size / 1024), 100 (unsigned long)(free_space / 1024)); 101 } 102 103 // ───────────────────────────────────────────────────────────────────────────── 104 // Watchdog timer 105 // ───────────────────────────────────────────────────────────────────────────── 106 107 static void test_system_watchdog(void) { 108 WHEN("the watchdog is enabled and fed"); 109 110 // These should not crash 111 enableLoopWDT(); 112 feedLoopWDT(); 113 feedLoopWDT(); 114 disableLoopWDT(); 115 116 } 117 118 // ───────────────────────────────────────────────────────────────────────────── 119 // Heap fragmentation 120 // ───────────────────────────────────────────────────────────────────────────── 121 122 static void test_system_heap_fragmentation(void) { 123 WHEN("heap fragmentation is measured"); 124 125 uint32_t free_heap = ESP.getFreeHeap(); 126 uint32_t max_alloc = ESP.getMaxAllocHeap(); 127 uint32_t min_free = ESP.getMinFreeHeap(); 128 uint32_t total = ESP.getHeapSize(); 129 130 TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(free_heap, max_alloc, 131 "device: max alloc should be <= free heap"); 132 TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(total, free_heap, 133 "device: free heap should be <= total heap"); 134 135 uint32_t frag_pct = (free_heap > 0) 136 ? 100 - (max_alloc * 100 / free_heap) 137 : 0; 138 139 char msg[128]; 140 snprintf(msg, sizeof(msg), "heap: %lu/%lu KB free, max_alloc=%lu KB, min_free=%lu KB, frag=%lu%%", 141 (unsigned long)(free_heap / 1024), 142 (unsigned long)(total / 1024), 143 (unsigned long)(max_alloc / 1024), 144 (unsigned long)(min_free / 1024), 145 (unsigned long)frag_pct); 146 TEST_MESSAGE(msg); 147 } 148 149 // ───────────────────────────────────────────────────────────────────────────── 150 // Flash chip info 151 // ───────────────────────────────────────────────────────────────────────────── 152 153 static void test_system_flash_chip_info(void) { 154 WHEN("the flash chip configuration is read"); 155 156 uint32_t flash_size = ESP.getFlashChipSize(); 157 uint32_t flash_speed = ESP.getFlashChipSpeed(); 158 FlashMode_t flash_mode = ESP.getFlashChipMode(); 159 160 TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(0, flash_size, 161 "device: flash size should be > 0"); 162 TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(0, flash_speed, 163 "device: flash speed should be > 0"); 164 165 const char *mode_str = "unknown"; 166 switch (flash_mode) { 167 case FM_QIO: mode_str = "QIO"; break; 168 case FM_QOUT: mode_str = "QOUT"; break; 169 case FM_DIO: mode_str = "DIO"; break; 170 case FM_DOUT: mode_str = "DOUT"; break; 171 default: break; 172 } 173 174 TEST_PRINTF("flash: %lu MB, %lu MHz, mode=%s", 175 (unsigned long)(flash_size / (1024 * 1024)), 176 (unsigned long)(flash_speed / 1000000), 177 mode_str); 178 } 179 180 // ───────────────────────────────────────────────────────────────────────────── 181 // CPU frequency 182 // ───────────────────────────────────────────────────────────────────────────── 183 184 static void test_system_cpu_frequency(void) { 185 WHEN("the CPU frequency is read"); 186 187 uint32_t freq = ESP.getCpuFreqMHz(); 188 TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(0, freq, 189 "device: CPU frequency should be > 0"); 190 191 TEST_PRINTF("CPU: %lu MHz", (unsigned long)freq); 192 193 // ESP32-S3 default is 240 MHz 194 TEST_ASSERT_EQUAL_UINT32_MESSAGE(240, freq, 195 "device: ESP32-S3 should default to 240 MHz"); 196 } 197 198 // ───────────────────────────────────────────────────────────────────────────── 199 // Version strings 200 // ───────────────────────────────────────────────────────────────────────────── 201 202 static void test_system_version_strings(void) { 203 WHEN("the firmware and SDK version strings are read"); 204 205 const char *sdk = ESP.getSdkVersion(); 206 const char *idf = esp_get_idf_version(); 207 const char *chip = ESP.getChipModel(); 208 209 TEST_ASSERT_NOT_NULL_MESSAGE(sdk, "device: ESP SDK version string is null"); 210 TEST_ASSERT_NOT_NULL_MESSAGE(idf, "device: ESP-IDF version string is null"); 211 TEST_ASSERT_NOT_NULL_MESSAGE(chip, "device: chip model string is null"); 212 213 TEST_PRINTF("chip=%s cores=%u rev=%u arduino=%s idf=%s", 214 chip, ESP.getChipCores(), ESP.getChipRevision(), 215 ESP_ARDUINO_VERSION_STR, idf); 216 } 217 218 // ───────────────────────────────────────────────────────────────────────────── 219 // Runner 220 // ───────────────────────────────────────────────────────────────────────────── 221 222 void hardware::system::test(void) { 223 MODULE("System"); 224 RUN_TEST(test_system_temperature_read); 225 RUN_TEST(test_system_task_list); 226 RUN_TEST(test_system_sketch_md5); 227 RUN_TEST(test_system_sketch_size); 228 RUN_TEST(test_system_watchdog); 229 RUN_TEST(test_system_heap_fragmentation); 230 RUN_TEST(test_system_flash_chip_info); 231 RUN_TEST(test_system_cpu_frequency); 232 RUN_TEST(test_system_version_strings); 233 } 234 235 #endif