diff --git a/backend/app/services/espidf_compiler.py b/backend/app/services/espidf_compiler.py index 175915bc..a94830a2 100644 --- a/backend/app/services/espidf_compiler.py +++ b/backend/app/services/espidf_compiler.py @@ -898,6 +898,17 @@ class ESPIDFCompiler: (r'esp_lcd_touch_cst816s\.h', 'espressif/esp_lcd_touch_cst816s', '*'), (r'esp_lcd_gc9a01\.h', 'espressif/esp_lcd_gc9a01', '*'), (r'esp_lcd_ek79007\.h', 'espressif/esp_lcd_ek79007', '*'), + # The building blocks Espressif's OWN board demos are made of — the + # C3-LCDkit knob_panel drives its WS2812 through led_strip and its + # EC11 through knob+button, the P4 devkit's panel is a GT911, and + # every audio demo on the VoCat/P4 goes through esp_codec_dev. A + # visitor pasting official example code hit "No such file or + # directory" on all four. + (r'led_strip\.h', 'espressif/led_strip', '*'), + (r'iot_button\.h', 'espressif/button', '*'), + (r'iot_knob\.h', 'espressif/knob', '*'), + (r'esp_lcd_touch_gt911\.h', 'espressif/esp_lcd_touch_gt911', '*'), + (r'esp_codec_dev\.h', 'espressif/esp_codec_dev', '*'), ) def _detect_managed_components(self, code: str) -> dict: diff --git a/test/backend/unit/test_espidf_pure_mode.py b/test/backend/unit/test_espidf_pure_mode.py index d5541c91..43b5db52 100644 --- a/test/backend/unit/test_espidf_pure_mode.py +++ b/test/backend/unit/test_espidf_pure_mode.py @@ -140,3 +140,46 @@ class TestJobKeyLanguageVariance(unittest.TestCase): if __name__ == '__main__': unittest.main() + + +class ManagedComponentDetectionTests(unittest.TestCase): + """The registry components a sketch's #includes must pull in. + + The template ships no idf_component.yml, so a header that lives in a + managed component and is not on this list dies at "No such file or + directory" — which is exactly what official Espressif board-demo code + did for led_strip / button / knob / GT911 / esp_codec_dev. + """ + + def setUp(self) -> None: + self.comp = make_compiler() + + def test_official_demo_components_are_declared(self) -> None: + cases = { + '#include "led_strip.h"': 'espressif/led_strip', + '#include "iot_button.h"': 'espressif/button', + '#include "iot_knob.h"': 'espressif/knob', + '#include "esp_lcd_touch_gt911.h"': 'espressif/esp_lcd_touch_gt911', + '#include "esp_codec_dev.h"': 'espressif/esp_codec_dev', + '#include "esp_camera.h"': 'espressif/esp32-camera', + '#include "esp_lcd_gc9a01.h"': 'espressif/esp_lcd_gc9a01', + '#include "esp_lcd_st77916.h"': 'espressif/esp_lcd_st77916', + '#include "esp_lcd_touch_cst816s.h"': 'espressif/esp_lcd_touch_cst816s', + '#include "esp_lcd_ek79007.h"': 'espressif/esp_lcd_ek79007', + } + for line, component in cases.items(): + with self.subTest(line=line): + self.assertIn(component, self.comp._detect_managed_components(line)) + + def test_angle_bracket_form_counts_too(self) -> None: + deps = self.comp._detect_managed_components('#include ') + self.assertIn('espressif/led_strip', deps) + + def test_unrelated_source_declares_nothing(self) -> None: + src = '#include \n#include "driver/gpio.h"\nvoid app_main(void) {}' + self.assertEqual(self.comp._detect_managed_components(src), {}) + + def test_a_similarly_named_header_does_not_match(self) -> None: + # "my_led_strip.h" is the user's own file, not the registry component. + deps = self.comp._detect_managed_components('#include "my_led_strip.h"') + self.assertEqual(deps, {})