mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-11-01 15:11:39 +01:00
Merge pull request #904 from david-cermak/feat/add_tests_v1.9
[mdns]: Add tests for recent feats/fixes
This commit is contained in:
21
.github/workflows/mdns__build-target-test.yml
vendored
21
.github/workflows/mdns__build-target-test.yml
vendored
@@ -24,6 +24,11 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
if [[ "${{ matrix.idf_ver }}" == "latest" ]]; then
|
||||
export EXPECTED_WARNING="warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_IP101'"
|
||||
else
|
||||
export EXPECTED_WARNING="warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_GENERIC'"
|
||||
fi
|
||||
python -m pip install idf-build-apps
|
||||
# Build default configs for all targets
|
||||
python ./ci/build_apps.py components/mdns/${{ matrix.test.path }} -r default -d
|
||||
@@ -71,6 +76,22 @@ jobs:
|
||||
- name: Run ${{ matrix.test.app }} application on ${{ matrix.idf_target }}
|
||||
working-directory: components/mdns/${{ matrix.test.path }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool
|
||||
pip install --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
|
||||
40
.github/workflows/mdns__host-tests.yml
vendored
40
.github/workflows/mdns__host-tests.yml
vendored
@@ -68,3 +68,43 @@ jobs:
|
||||
diff -q $file /tmp/$file || exit 1
|
||||
echo "OK"
|
||||
done
|
||||
|
||||
fuzz_test:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns-fuzz') || github.event_name == 'push'
|
||||
name: Fuzzer tests for mdns lib
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: aflplusplus/aflplusplus
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout ESP-IDF
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: espressif/esp-idf
|
||||
path: idf
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Necessary Libs
|
||||
run: |
|
||||
apt-get update -y
|
||||
apt-get install -y libbsd-dev
|
||||
|
||||
- name: Run AFL++
|
||||
shell: bash
|
||||
run: |
|
||||
export IDF_PATH=$GITHUB_WORKSPACE/idf
|
||||
cd components/mdns/tests/test_afl_fuzz_host/
|
||||
make fuzz
|
||||
|
||||
- name: Upload Crash Artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fuzz-crashes
|
||||
path: components/mdns/tests/test_afl_fuzz_host/out/default/crashes.tar.gz
|
||||
if-no-files-found: ignore
|
||||
|
||||
@@ -12,6 +12,7 @@ CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_GENERIC=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import logging
|
||||
import re
|
||||
@@ -92,10 +92,58 @@ class DnsPythonWrapper:
|
||||
if expect is None:
|
||||
expect = name
|
||||
if expected:
|
||||
assert any(expect in answer for answer in answers), f"Expected record '{expect}' not found in answer section"
|
||||
assert any(expect in answer for answer in answers), f"Expected record '{expect}' not in answer section"
|
||||
else:
|
||||
assert not any(expect in answer for answer in answers), f"Unexpected record '{expect}' found in answer section"
|
||||
|
||||
def parse_section(self, response, section: str, rdtype_text: str):
|
||||
"""Parse a specific response section (answer, authority, additional) for given rdtype.
|
||||
|
||||
Returns list of textual records for that rdtype.
|
||||
"""
|
||||
out = []
|
||||
if not response:
|
||||
return out
|
||||
rrsets = []
|
||||
if section == 'answer':
|
||||
rrsets = response.answer
|
||||
elif section == 'authority':
|
||||
rrsets = response.authority
|
||||
elif section == 'additional':
|
||||
rrsets = response.additional
|
||||
else:
|
||||
raise ValueError('invalid section')
|
||||
for rr in rrsets:
|
||||
if dns.rdatatype.to_text(rr.rdtype) != rdtype_text:
|
||||
continue
|
||||
for item in rr.items:
|
||||
full = (
|
||||
f'{rr.name} {rr.ttl} '
|
||||
f'{dns.rdataclass.to_text(rr.rdclass)} '
|
||||
f'{dns.rdatatype.to_text(rr.rdtype)} '
|
||||
f'{item.to_text()}'
|
||||
)
|
||||
out.append(full)
|
||||
return out
|
||||
|
||||
def check_additional(self, response, rdtype_text: str, owner_contains: str, expected: bool = True, expect_substr: str | None = None):
|
||||
"""Check Additional section for an RR of type rdtype_text whose owner includes owner_contains.
|
||||
|
||||
If expect_substr is provided, also require it to appear in the textual RR.
|
||||
"""
|
||||
records = self.parse_section(response, 'additional', rdtype_text)
|
||||
logger.info(f'additional({rdtype_text}): {records}')
|
||||
|
||||
def _matches(line: str) -> bool:
|
||||
in_owner = owner_contains in line
|
||||
has_val = (expect_substr in line) if expect_substr else True
|
||||
return in_owner and has_val
|
||||
found = any(_matches(r) for r in records)
|
||||
if expected:
|
||||
assert found, f"Expected {rdtype_text} for {owner_contains} in Additional not found"
|
||||
else:
|
||||
assert not found, f"Unexpected {rdtype_text} for {owner_contains} found in Additional"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 3:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import logging
|
||||
|
||||
@@ -65,6 +65,17 @@ def test_add_service(mdns_console, dig_app):
|
||||
dig_app.check_record('_http._tcp.local', query_type='PTR', expected=True)
|
||||
|
||||
|
||||
def test_ptr_additional_records_for_service(dig_app):
|
||||
# Query PTR for the service type and ensure SRV/TXT are in Additional (RFC 6763 §12.1)
|
||||
resp = dig_app.run_query('_http._tcp.local', query_type='PTR')
|
||||
# Answer section should have at least one PTR to the instance
|
||||
answers = dig_app.parse_answer_section(resp, 'PTR')
|
||||
assert any('test_service._http._tcp.local' in a for a in answers)
|
||||
# Additional section should include SRV and TXT for the same instance
|
||||
dig_app.check_additional(resp, 'SRV', 'test_service._http._tcp.local', expected=True)
|
||||
dig_app.check_additional(resp, 'TXT', 'test_service._http._tcp.local', expected=True)
|
||||
|
||||
|
||||
def test_remove_service(mdns_console, dig_app):
|
||||
mdns_console.send_input('mdns_service_remove _http _tcp')
|
||||
mdns_console.send_input('mdns_service_lookup _http _tcp')
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#INSTR=off
|
||||
TEST_NAME=test
|
||||
FUZZ=afl-fuzz
|
||||
COMPONENTS_DIR=$(IDF_PATH)/components
|
||||
COMPILER_ICLUDE_DIR=$(shell echo `which xtensa-esp32-elf-gcc | xargs dirname | xargs dirname`/xtensa-esp32-elf)
|
||||
# Use ESP32 toolchain include path if available, otherwise fall back to system includes for host-based compilation
|
||||
COMPILER_INCLUDE_DIR=$(shell if command -v xtensa-esp32-elf-gcc >/dev/null 2>&1; then echo `which xtensa-esp32-elf-gcc | xargs dirname | xargs dirname`/xtensa-esp32-elf; else echo /usr; fi)
|
||||
|
||||
CFLAGS=-g -Wno-unused-value -Wno-missing-declarations -Wno-pointer-bool-conversion -Wno-macro-redefined -Wno-int-to-void-pointer-cast -DHOOK_MALLOC_FAILED -DESP_EVENT_H_ -D__ESP_LOG_H__ \
|
||||
-I. -I../.. -I../../include -I../../private_include -I ./build/config \
|
||||
@@ -35,7 +37,7 @@ CFLAGS=-g -Wno-unused-value -Wno-missing-declarations -Wno-pointer-bool-conversi
|
||||
-I$(COMPONENTS_DIR)/xtensa/include \
|
||||
-I$(COMPONENTS_DIR)/xtensa/esp32/include \
|
||||
-I$(COMPONENTS_DIR)/esp_hw_support/etm/include \
|
||||
-I$(COMPILER_ICLUDE_DIR)/include
|
||||
-I$(COMPILER_INCLUDE_DIR)/include
|
||||
|
||||
|
||||
MDNS_C_DEPENDENCY_INJECTION=-include mdns_di.h
|
||||
@@ -77,7 +79,18 @@ $(TEST_NAME): $(OBJECTS)
|
||||
@$(LD) $(OBJECTS) -o $@ $(LDLIBS)
|
||||
|
||||
fuzz: $(TEST_NAME)
|
||||
@$(FUZZ) -i "in" -o "out" -- ./$(TEST_NAME)
|
||||
# timeout returns 124 if time limit is reached, original return code otherwise
|
||||
# pass only if: fuzzing was running smoothly until timeout AND no crash found
|
||||
@timeout 10m $(FUZZ) -i "in" -o "out" -- ./$(TEST_NAME) || \
|
||||
if [ $$? -eq 124 ]; then \
|
||||
if [ -n "$$(find out/default/crashes -type f 2>/dev/null)" ]; then \
|
||||
echo "Crashes found!"; \
|
||||
tar -czf out/default/crashes.tar.gz -C out/default crashes; \
|
||||
exit 1; \
|
||||
fi \
|
||||
else \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
@rm -rf *.o *.SYM $(TEST_NAME) out
|
||||
|
||||
@@ -55,8 +55,7 @@
|
||||
|
||||
#define pdMS_TO_TICKS(a) a
|
||||
#define xSemaphoreTake(s,d) true
|
||||
#define xTaskDelete(a)
|
||||
#define vTaskDelete(a) free(a)
|
||||
#define vTaskDelete(a) free(NULL)
|
||||
#define xSemaphoreGive(s)
|
||||
#define xQueueCreateMutex(s)
|
||||
#define _mdns_pcb_init(a,b) true
|
||||
@@ -66,7 +65,7 @@
|
||||
#define vSemaphoreDelete(s) free(s)
|
||||
#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U
|
||||
#define xTaskCreatePinnedToCore(a,b,c,d,e,f,g) *(f) = malloc(1)
|
||||
#define xTaskCreateStaticPinnedToCore(a,b,c,d,e,f,g,h) true
|
||||
#define xTaskCreateStaticPinnedToCore(a,b,c,d,e,f,g,h) ((void*)1)
|
||||
#define vTaskDelay(m) usleep((m)*0)
|
||||
#define esp_random() (rand()%UINT32_MAX)
|
||||
|
||||
@@ -139,4 +138,8 @@ TaskHandle_t xTaskGetCurrentTaskHandle(void);
|
||||
void xTaskNotifyGive(TaskHandle_t task);
|
||||
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time);
|
||||
|
||||
static inline void xTaskGetStaticBuffers(void *pvTaskBuffer, void *pvStackBuffer, void *pvTaskTCB)
|
||||
{
|
||||
}
|
||||
|
||||
#endif //_ESP32_COMPAT_H_
|
||||
|
||||
@@ -78,30 +78,20 @@ static int mdns_test_service_txt_set(const char *service, const char *proto, ui
|
||||
static int mdns_test_sub_service_add(const char *sub_name, const char *service_name, const char *proto, uint32_t port)
|
||||
{
|
||||
if (mdns_service_add(NULL, service_name, proto, port, NULL, 0)) {
|
||||
// This is expected failure as the service thread is not running
|
||||
return ESP_FAIL;
|
||||
}
|
||||
mdns_action_t *a = NULL;
|
||||
GetLastItem(&a);
|
||||
mdns_test_execute_action(a);
|
||||
|
||||
if (mdns_test_mdns_get_service_item(service_name, proto) == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int ret = mdns_service_subtype_add_for_host(NULL, service_name, proto, NULL, sub_name);
|
||||
a = NULL;
|
||||
GetLastItem(&a);
|
||||
mdns_test_execute_action(a);
|
||||
return ret;
|
||||
return mdns_service_subtype_add_for_host(NULL, service_name, proto, NULL, sub_name);
|
||||
}
|
||||
|
||||
static int mdns_test_service_add(const char *service_name, const char *proto, uint32_t port)
|
||||
{
|
||||
if (mdns_service_add(NULL, service_name, proto, port, NULL, 0)) {
|
||||
// This is expected failure as the service thread is not running
|
||||
return ESP_FAIL;
|
||||
}
|
||||
mdns_action_t *a = NULL;
|
||||
GetLastItem(&a);
|
||||
mdns_test_execute_action(a);
|
||||
|
||||
if (mdns_test_mdns_get_service_item(service_name, proto) == NULL) {
|
||||
return ESP_FAIL;
|
||||
@@ -266,9 +256,6 @@ int main(int argc, char **argv)
|
||||
}
|
||||
#ifndef MDNS_NO_SERVICES
|
||||
mdns_service_remove_all();
|
||||
mdns_action_t *a = NULL;
|
||||
GetLastItem(&a);
|
||||
mdns_test_execute_action(a);
|
||||
#endif
|
||||
ForceTaskDelete();
|
||||
mdns_free();
|
||||
|
||||
Reference in New Issue
Block a user