Merge branch 'ci/unity_test' into 'master'

Ci/unity test

See merge request speech-recognition-framework/esp-sr!46
This commit is contained in:
Sun Xiang Yu 2023-07-13 15:17:34 +08:00
commit 573cf30234
34 changed files with 20724 additions and 223 deletions

8
.gitignore vendored
View File

@ -24,4 +24,10 @@ _build/*
# Downloaded font files
docs/_static/DejaVuSans.ttf
docs/_static/NotoSansSC-Regular.otf
docs/_static/NotoSansSC-Regular.otf
# ci
test_apps/*/dependencies.lock
test_apps/*/managed_components
test_apps/*/*/build_*
pytest_log

View File

@ -1,31 +1,125 @@
stages:
- build
- build_docs
- deploy_docs
- deploy
variables:
# Versioned esp-idf-doc env image to use for all document building jobs
ESP_DOCS_ENV_IMAGE: "$CI_DOCKER_REGISTRY/esp-idf-doc-env-v5.0"
ESP_SR_PATH: "$CI_PROJECT_DIR"
IDF_PATH: $CI_PROJECT_DIR/esp-idf
IDF_REPO: ${GITLAB_SSH_SERVER}/espressif/esp-idf.git
BATCH_BUILD: "1"
V: "0"
IDF_CI_BUILD: "1"
DOCKER_TARGET_TEST_v5_0_ENV_IMAGE: "$CI_DOCKER_REGISTRY/target-test-env-v5.0:3"
before_script:
# add gitlab ssh key
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo -n $GITLAB_KEY > ~/.ssh/id_rsa_base64
- base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- git --version
- git submodule update --init --recursive --force
- pip install idf_build_apps
- pip install -r test_apps/requirements.txt
.setup_idf_ci_env: &setup_idf_ci_env
- source esp-idf/tools/ci/utils.sh
- source esp-idf/tools/ci/configure_ci_environment.sh
- esp-idf/tools/idf_tools.py install
- esp-idf/tools/idf_tools.py export
.build_test_script: &build_test_script
- python ./tools/ci/build_apps.py $EXAMPLES_PATH -t all
.build_template:
.build_test_script: &build_test_script
- python ./test_apps/build_apps.py $EXAMPLES_PATH -t all
.build_test_template:
stage: build
tags:
- build
artifacts:
when: always
paths:
- "**/build*/size.json"
- "**/build*/build_log.txt"
- "**/build*/*.bin"
# upload to s3 server to save the artifacts size
- "**/build*/*.map"
- "**/build*/*.elf"
- "**/build*/flasher_args.json"
- "**/build*/flash_project_args"
- "**/build*/config/sdkconfig.json"
- "**/build*/bootloader/*.bin"
- "**/build*/bootloader/*.elf"
- "**/build*/partition_table/*.bin"
- "**/build*/srmodels/*.bin"
- size_info.txt
expire_in: 1 week
variables:
EXAMPLES_PATH: "test_apps"
script:
- *build_test_script
build_esp_sr:
extends: .build_test_template
image: espressif/idf:release-v5.0
variables:
EXAMPLES_PATH: "test_apps/esp-sr"
build_esp_tts:
extends: .build_test_template
image: espressif/idf:release-v5.0
variables:
EXAMPLES_PATH: "test_apps/esp-tts"
.test_template: &test_template
image: DOCKER_TARGET_TEST_v5_0_ENV_IMAGE
stage: target_test
timeout: 10 hour
variables:
GIT_DEPTH: 1
SUBMODULES_TO_FETCH: "none"
cache:
# Usually do not need submodule-cache in target_test
- key: pip-cache
paths:
- .cache/pip
policy: pull
.pytest_template:
<<: *test_template
artifacts:
when: always
paths:
- XUNIT_RESULT.xml
- pytest_log/
reports:
junit: XUNIT_RESULT.xml
expire_in: 4 days
variables:
TEST_TARGET: 'esp32s3'
TEST_FOLDER: 'test_apps'
TEST_ENV: 'esp32s3'
script:
- pytest ${TEST_FOLDER} --target ${TEST_TARGET} --env ${TEST_ENV} --junitxml=XUNIT_RESULT.xml
.if-dev-push: &if-dev-push
if: '$CI_COMMIT_REF_NAME != "master" && $CI_COMMIT_BRANCH !~ /^release\/v/ && $CI_COMMIT_TAG !~ /^v\d+\.\d+(\.\d+)?($|-)/ && ($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event")'
.if-protected: &if-protected
if: '($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_BRANCH =~ /^release\/v/ || $CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/)'
.rules:test:test_esp_sr:
rules:
- <<: *if-protected
- <<: *if-dev-push
.build_doc_template:
stage: build_docs
image: $ESP_DOCS_ENV_IMAGE
build_esp_sr_html:
extends:
- .build_template
- .build_doc_template
variables:
DOCS_DIR: $CI_PROJECT_DIR/docs
artifacts:
@ -46,7 +140,7 @@ build_esp_sr_html:
build_esp_sr_pdf:
extends:
- .build_template
- .build_doc_template
variables:
DOCS_DIR: $CI_PROJECT_DIR/docs
artifacts:
@ -128,4 +222,4 @@ push_to_github:
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- git remote remove github &>/dev/null || true
- git remote add github git@github.com:espressif/esp-sr.git
- git push github "${CI_COMMIT_SHA}:refs/heads/${CI_COMMIT_REF_NAME}"
- git push github "${CI_COMMIT_SHA}:refs/heads/${CI_COMMIT_REF_NAME}"

215
conftest.py Normal file
View File

@ -0,0 +1,215 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: Apache-2.0
import logging
import os
import pathlib
import re
import sys
from datetime import datetime
from typing import Callable, List, Optional, Tuple
import pytest
from pytest import Config, FixtureRequest, Function, Session
from pytest_embedded.plugin import multi_dut_argument, multi_dut_fixture
IDF_VERSION = os.environ.get('IDF_VERSION')
PYTEST_ROOT_DIR = str(pathlib.Path(__file__).parent)
logging.info(f'Pytest root dir: {PYTEST_ROOT_DIR}')
@pytest.fixture(scope='session', autouse=True)
def idf_version() -> str:
if os.environ.get('IDF_VERSION'):
return os.environ.get('IDF_VERSION')
idf_path = os.environ.get('IDF_PATH')
if not idf_path:
logging.warning('Failed to get IDF_VERSION!')
return ''
version_path = os.path.join(os.environ['IDF_PATH'], 'tools/cmake/version.cmake')
regex = re.compile(r'^\s*set\s*\(\s*IDF_VERSION_([A-Z]{5})\s+(\d+)')
ver = {}
with open(version_path) as f:
for line in f:
m = regex.match(line)
if m:
ver[m.group(1)] = m.group(2)
return '{}.{}'.format(int(ver['MAJOR']), int(ver['MINOR']))
@pytest.fixture(scope='session', autouse=True)
def session_tempdir() -> str:
_tmpdir = os.path.join(
os.path.dirname(__file__),
'pytest_log',
datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),
)
os.makedirs(_tmpdir, exist_ok=True)
return _tmpdir
@pytest.fixture
@multi_dut_argument
def config(request: FixtureRequest) -> str:
config_marker = list(request.node.iter_markers(name='config'))
return config_marker[0].args[0] if config_marker else 'default'
@pytest.fixture
@multi_dut_argument
def app_path(request: FixtureRequest, test_file_path: str) -> str:
config_marker = list(request.node.iter_markers(name='app_path'))
if config_marker:
return config_marker[0].args[0]
else:
# compatible with old pytest-embedded parametrize --app_path
return request.config.getoption('app_path', None) or os.path.dirname(test_file_path)
@pytest.fixture
def test_case_name(request: FixtureRequest, target: str, config: str) -> str:
if not isinstance(target, str):
target = '|'.join(sorted(list(set(target))))
if not isinstance(config, str):
config = '|'.join(sorted(list(config)))
return f'{target}.{config}.{request.node.originalname}'
@pytest.fixture
@multi_dut_fixture
def build_dir(
app_path: str,
target: Optional[str],
config: Optional[str],
idf_version: str
) -> Optional[str]:
"""
Check local build dir with the following priority:
1. <app_path>/${IDF_VERSION}/build_<target>_<config>
2. <app_path>/${IDF_VERSION}/build_<target>
3. <app_path>/build_<target>_<config>
4. <app_path>/build
5. <app_path>
Args:
app_path: app path
target: target
config: config
Returns:
valid build directory
"""
assert target
assert config
check_dirs = []
if idf_version:
check_dirs.append(os.path.join(idf_version, f'build_{target}_{config}'))
check_dirs.append(os.path.join(idf_version, f'build_{target}'))
check_dirs.append(f'build_{target}_{config}')
check_dirs.append('build')
check_dirs.append('.')
for check_dir in check_dirs:
binary_path = os.path.join(app_path, check_dir)
if os.path.isdir(binary_path):
logging.info(f'find valid binary path: {binary_path}')
return check_dir
logging.warning(
f'checking binary path: {binary_path} ... missing ... try another place')
logging.error(
f'no build dir. Please build the binary "python tools/build_apps.py {app_path}" and run pytest again')
sys.exit(1)
@pytest.fixture(autouse=True)
@multi_dut_fixture
def junit_properties(
test_case_name: str, record_xml_attribute: Callable[[str, object], None]
) -> None:
"""
This fixture is autoused and will modify the junit report test case name to <target>.<config>.<case_name>
"""
record_xml_attribute('name', test_case_name)
##################
# Hook functions #
##################
_idf_pytest_embedded_key = pytest.StashKey['IdfPytestEmbedded']
def pytest_addoption(parser: pytest.Parser) -> None:
base_group = parser.getgroup('idf')
base_group.addoption(
'--env',
help='only run tests matching the environment NAME.',
)
def pytest_configure(config: Config) -> None:
# Require cli option "--target"
help_commands = ['--help', '--fixtures', '--markers', '--version']
for cmd in help_commands:
if cmd in config.invocation_params.args:
target = 'unneeded'
break
else:
target = config.getoption('target')
if not target:
raise ValueError('Please specify one target marker via "--target [TARGET]"')
config.stash[_idf_pytest_embedded_key] = IdfPytestEmbedded(
target=target,
env_name=config.getoption('env'),
)
config.pluginmanager.register(config.stash[_idf_pytest_embedded_key])
def pytest_unconfigure(config: Config) -> None:
_pytest_embedded = config.stash.get(_idf_pytest_embedded_key, None)
if _pytest_embedded:
del config.stash[_idf_pytest_embedded_key]
config.pluginmanager.unregister(_pytest_embedded)
class IdfPytestEmbedded:
def __init__(
self,
target: Optional[str] = None,
env_name: Optional[str] = None,
):
# CLI options to filter the test cases
self.target = target
self.env_name = env_name
self._failed_cases: List[
Tuple[str, bool, bool]
] = [] # (test_case_name, is_known_failure_cases, is_xfail)
@pytest.hookimpl(tryfirst=True)
def pytest_sessionstart(self, session: Session) -> None:
if self.target:
self.target = self.target.lower()
session.config.option.target = self.target
# @pytest.hookimpl(tryfirst=True)
def pytest_collection_modifyitems(self, items: List[Function]) -> None:
# set default timeout 10 minutes for each case
for item in items:
# default timeout 5 mins
if 'timeout' not in item.keywords:
item.add_marker(pytest.mark.timeout(5 * 60))
# filter all the test cases with "--target"
if self.target:
def item_targets(item): return [m.args[0] for m in item.iter_markers(name='target')]
items[:] = [item for item in items if self.target in item_targets(item)]
# filter all the test cases with "--env"
if self.env_name:
def item_envs(item): return [m.args[0] for m in item.iter_markers(name='env')]
items[:] = [item for item in items if self.env_name in item_envs(item)]

42
pytest.ini Normal file
View File

@ -0,0 +1,42 @@
[pytest]
# exclude examples/ota/simple_ota_example/pytest_simple_ota.py
norecursedirs = examples/ota/*
# only the files with prefix `pytest_` would be recognized as pytest test scripts.
python_files = pytest_*.py
# set traceback to "short" to prevent the overwhelming tracebacks
addopts =
-s
--embedded-services esp,idf
--tb short
--skip-check-coredump y
# ignore PytestExperimentalApiWarning for record_xml_attribute
filterwarnings =
ignore::_pytest.warning_types.PytestExperimentalApiWarning
markers =
# target markers
target: target chip name (--target)
# env markers
env: target test env name (--env)
# config markers
config: choose specific bins built by `sdkconfig.ci.<config>`
# app_path markers
app_path: choose specific app_path, <app_path>[/build_xxx]
# log related
log_cli = True
log_cli_level = INFO
log_cli_format = %(asctime)s %(levelname)s %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S
# junit related
junit_family = xunit1
## log all to `system-out` when case fail
junit_logging = stdout
junit_log_passing_tests = False

View File

@ -1,19 +0,0 @@
set(srcs
test_chinese_tts.c
)
if(IDF_TARGET STREQUAL "esp32")
list(APPEND srcs test_wakenet.c
test_afe.c
test_multinet.c)
elseif(IDF_TARGET STREQUAL "esp32s3")
list(APPEND srcs test_wakenet.c
test_afe.c
test_multinet.c)
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
REQUIRES cmock esp-sr)

View File

@ -1,186 +0,0 @@
/* test_mean.c: Implementation of a testable component.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "string.h"
#include <limits.h>
#include "unity.h"
#include "model_path.h"
#include "esp_mn_iface.h"
#include "esp_mn_models.h"
#include "dakaidiandeng.h"
#include "alexa.h"
#include "dl_lib_convq_queue.h"
#include <sys/time.h>
TEST_CASE("multinet create/destroy API & memory leak", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
int start_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
int start_internal_size = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
// test model loading time
struct timeval tv_start, tv_end;
gettimeofday(&tv_start, NULL);
printf("load multinet!\n");
model_iface_data_t *model_data = multinet->create(model_name, 6000);
gettimeofday(&tv_end, NULL);
int tv_ms = (tv_end.tv_sec - tv_start.tv_sec) * 1000 + (tv_end.tv_usec - tv_start.tv_usec) / 1000;
printf("create latency:%d ms\n", tv_ms);
// test model memory concumption
int create_size = start_size - heap_caps_get_free_size(MALLOC_CAP_8BIT);
int create_internal_size = start_internal_size - heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
printf("Internal RAM: %d, PSRAM:%d\n", create_internal_size, create_size - create_internal_size);
multinet->destroy(model_data);
esp_srmodel_deinit(models);
// test memory leak
int first_end_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
int last_end_size = first_end_size;
int mem_leak = start_size - last_end_size;
printf("create&destroy times:%d, memory leak:%d\n", 1, mem_leak);
for (int i = 0; i < 5; i++) {
printf("init partition ...\n");
models = esp_srmodel_init("model");
model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
multinet = esp_mn_handle_from_name(model_name);
int time_out = 3000 + i * 2000;
printf("create ..., time out = %d\n", time_out);
model_data = multinet->create(model_name, time_out);
printf("destroy ...\n");
multinet->destroy(model_data);
esp_srmodel_deinit(models);
last_end_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
mem_leak = start_size - last_end_size;
printf("create&destroy times:%d, memory leak:%d\n", i + 2, mem_leak);
}
TEST_ASSERT_EQUAL(true, (mem_leak) < 1000 && last_end_size == first_end_size);
}
TEST_CASE("multinet detect/get_results API & cpu loading", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
int frequency = multinet->get_samp_rate(model_data);
int audio_chunksize = multinet->get_samp_chunksize(model_data) * sizeof(int16_t);
int16_t *buffer = malloc(audio_chunksize);
int chunks = 0;
struct timeval tv_start, tv_end;
gettimeofday(&tv_start, NULL);
while (1) {
if ((chunks + 1)*audio_chunksize <= sizeof(alexa)) {
memcpy(buffer, alexa + chunks * audio_chunksize, audio_chunksize);
} else {
memset(buffer, 0, audio_chunksize);
}
esp_mn_state_t mn_state = multinet->detect(model_data, buffer);
if (mn_state == ESP_MN_STATE_DETECTED) {
esp_mn_results_t *mn_result = multinet->get_results(model_data);
printf("Command id:%d\n", mn_result->command_id[0]);
}
chunks++;
if (chunks == 64) {
break;
}
}
gettimeofday(&tv_end, NULL);
int tv_ms = (tv_end.tv_sec - tv_start.tv_sec) * 1000 + (tv_end.tv_usec - tv_start.tv_usec) / 1000;
chunks -= 7;
int run_ms = (chunks) * audio_chunksize / sizeof(int16_t) * 1000 / frequency;
float cpu_loading = tv_ms * 100.0 / run_ms;
printf("Done! Took %d ms to parse %d ms worth of samples in %d iterations. CPU loading(single core):%.1f%%\n",
tv_ms, run_ms, chunks, cpu_loading);
multinet->destroy(model_data);
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, (cpu_loading < 75 && tv_ms > 0));
}
TEST_CASE("multinet switch loader mode", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, "mn6", NULL);
if (model_name != NULL) {
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
int start_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
int start_internal_size = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
struct timeval tv_start, tv_end;
gettimeofday(&tv_start, NULL);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
int create_size = start_size - heap_caps_get_free_size(MALLOC_CAP_8BIT);
int create_internal_size = start_internal_size - heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
int first_create_size = create_size;
printf("Internal RAM: %d, PSRAM:%d\n\n", create_internal_size, create_size-create_internal_size);
model_data = multinet->switch_loader_mode(model_data, ESP_MN_LOAD_FROM_PSRAM_FLASH);
create_size = start_size - heap_caps_get_free_size(MALLOC_CAP_8BIT);
create_internal_size = start_internal_size - heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
printf("Internal RAM: %d, PSRAM:%d\n\n", create_internal_size, create_size-create_internal_size);
model_data = multinet->switch_loader_mode(model_data, ESP_MN_LOAD_FROM_PSRAM);
create_size = start_size - heap_caps_get_free_size(MALLOC_CAP_8BIT);
create_internal_size = start_internal_size - heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
printf("Internal RAM: %d, PSRAM:%d\n\n", create_internal_size, create_size-create_internal_size);
model_data = multinet->switch_loader_mode(model_data, ESP_MN_LOAD_FROM_PSRAM);
model_data = multinet->switch_loader_mode(model_data, ESP_MN_LOAD_FROM_PSRAM_FLASH);
create_size = start_size - heap_caps_get_free_size(MALLOC_CAP_8BIT);
create_internal_size = start_internal_size - heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
printf("Internal RAM: %d, PSRAM:%d\n\n", create_internal_size, create_size-create_internal_size);
model_data = multinet->switch_loader_mode(model_data, ESP_MN_LOAD_FROM_FLASH);
create_size = start_size - heap_caps_get_free_size(MALLOC_CAP_8BIT);
create_internal_size = start_internal_size - heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
printf("Internal RAM: %d, PSRAM:%d\n\n", create_internal_size, create_size-create_internal_size);
model_data = multinet->switch_loader_mode(model_data, ESP_MN_LOAD_FROM_FLASH);
model_data = multinet->switch_loader_mode(model_data, ESP_MN_LOAD_FROM_PSRAM);
model_data = multinet->switch_loader_mode(model_data, ESP_MN_LOAD_FROM_FLASH);
model_data = multinet->switch_loader_mode(model_data, ESP_MN_LOAD_FROM_PSRAM_FLASH);
create_size = start_size - heap_caps_get_free_size(MALLOC_CAP_8BIT);
create_internal_size = start_internal_size - heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
printf("Internal RAM: %d, PSRAM:%d\n\n", create_internal_size, create_size-create_internal_size);
gettimeofday(&tv_end, NULL);
int tv_ms=(tv_end.tv_sec-tv_start.tv_sec)*1000+(tv_end.tv_usec-tv_start.tv_usec)/1000;
printf("create latency:%d ms\n", tv_ms);
multinet->destroy(model_data);
int first_end_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
int first_end_internal_size = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
int last_end_size = first_end_size;
int last_end_internal_size = first_end_internal_size;
int mem_leak = start_size - last_end_size;
printf("create&destroy times:%d, memory leak:%d\n", 1, mem_leak);
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, (mem_leak) < 1000 && first_create_size == create_size);
} else {
printf("Just support multinet6 and the later versions\n");
TEST_ASSERT_EQUAL(true, 1);
}
}

View File

@ -0,0 +1,9 @@
test_apps/esp-sr:
enable:
- if: IDF_TARGET in ["esp32s3"]
temporary: false
test_apps/esp-tts:
enable:
- if: IDF_TARGET in ["esp32s3", "esp32"]
temporary: false

11
test_apps/README.md Normal file
View File

@ -0,0 +1,11 @@
Steps to run these cases:
- Build
- . ${IDF_PATH}/export.sh
- pip install idf_build_apps
- python test_apps/build_apps.py test_apps -t esp32s3
- Test
- pip install -r test_apps/requirement.txt
- pytest test_apps --target esp32s3

152
test_apps/build_apps.py Normal file
View File

@ -0,0 +1,152 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: Apache-2.0
"""
This file is used in CI generate binary files for different kinds of apps
"""
import argparse
import sys
import os
import re
from pathlib import Path
from typing import List
from idf_build_apps import LOGGER, App, build_apps, find_apps, setup_logging
PROJECT_ROOT = Path(__file__).parent.parent.absolute()
print(PROJECT_ROOT)
APPS_BUILD_PER_JOB = 30
IGNORE_WARNINGS = [
r'1/2 app partitions are too small',
r'This clock source will be affected by the DFS of the power management',
r'The current IDF version does not support using the gptimer API',
r'pkg_resources is deprecated as an API',
]
def _get_idf_version():
if os.environ.get('IDF_VERSION'):
return os.environ.get('IDF_VERSION')
version_path = os.path.join(os.environ['IDF_PATH'], 'tools/cmake/version.cmake')
regex = re.compile(r'^\s*set\s*\(\s*IDF_VERSION_([A-Z]{5})\s+(\d+)')
ver = {}
with open(version_path) as f:
for line in f:
m = regex.match(line)
if m:
ver[m.group(1)] = m.group(2)
return '{}.{}'.format(int(ver['MAJOR']), int(ver['MINOR']))
def get_cmake_apps(
paths,
target,
config_rules_str,
default_build_targets,
): # type: (List[str], str, List[str]) -> List[App]
idf_ver = _get_idf_version()
apps = find_apps(
paths,
recursive=True,
target=target,
build_dir=f'{idf_ver}/build_@t_@w',
config_rules_str=config_rules_str,
build_log_path='build_log.txt',
size_json_path='size.json',
check_warnings=True,
preserve=True,
default_build_targets=default_build_targets,
manifest_files=[
# str(Path(PROJECT_ROOT) /'examples'/'.build-rules.yml'),
str(Path(PROJECT_ROOT) /'test_apps'/'.build-rules.yml'),
],
)
return apps
def main(args): # type: (argparse.Namespace) -> None
default_build_targets = args.default_build_targets.split(',') if args.default_build_targets else None
apps = get_cmake_apps(args.paths, args.target, args.config, default_build_targets)
print(apps)
if args.exclude_apps:
apps_to_build = [app for app in apps if app.name not in args.exclude_apps]
else:
apps_to_build = apps[:]
LOGGER.info('Found %d apps after filtering', len(apps_to_build))
LOGGER.info(
'Suggest setting the parallel count to %d for this build job',
len(apps_to_build) // APPS_BUILD_PER_JOB + 1,
)
ret_code = build_apps(
apps_to_build,
parallel_count=args.parallel_count,
parallel_index=args.parallel_index,
dry_run=False,
collect_size_info=args.collect_size_info,
keep_going=True,
ignore_warning_strs=IGNORE_WARNINGS,
copy_sdkconfig=True,
)
sys.exit(ret_code)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Build all the apps for different test types. Will auto remove those non-test apps binaries',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument('paths', nargs='*', help='Paths to the apps to build.')
parser.add_argument(
'-t', '--target',
default='all',
help='Build apps for given target. could pass "all" to get apps for all targets',
)
parser.add_argument(
'--config',
default=['sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'],
action='append',
help='Adds configurations (sdkconfig file names) to build. This can either be '
'FILENAME[=NAME] or FILEPATTERN. FILENAME is the name of the sdkconfig file, '
'relative to the project directory, to be used. Optional NAME can be specified, '
'which can be used as a name of this configuration. FILEPATTERN is the name of '
'the sdkconfig file, relative to the project directory, with at most one wildcard. '
'The part captured by the wildcard is used as the name of the configuration.',
)
parser.add_argument(
'--parallel-count', default=1, type=int, help='Number of parallel build jobs.'
)
parser.add_argument(
'--parallel-index',
default=1,
type=int,
help='Index (1-based) of the job, out of the number specified by --parallel-count.',
)
parser.add_argument(
'--collect-size-info',
type=argparse.FileType('w'),
help='If specified, the test case name and size info json will be written to this file',
)
parser.add_argument(
'--exclude-apps',
nargs='*',
help='Exclude build apps',
)
parser.add_argument(
'--default-build-targets',
default=None,
help='default build targets used in manifest files',
)
parser.add_argument(
'-v', '--verbose',
action='count', default=0,
help='Show verbose log message',
)
arguments = parser.parse_args()
if not arguments.paths:
arguments.paths = [PROJECT_ROOT]
setup_logging(verbose=arguments.verbose) # Info
main(arguments)

View File

@ -0,0 +1,10 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.5)
# Include the components directory of the main application:
#
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
"../../../esp-sr")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp_sr_test)

View File

@ -0,0 +1,15 @@
dependencies:
espressif/esp-dsp:
component_hash: 59a67ce3be799201752ebde99890b0ab947054eff6463d83e944f2d4165d6905
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.4.4
idf:
component_hash: null
source:
type: idf
version: 5.0.2
manifest_hash: 1c2a956562f88dbb4682372296ee9497ceb4a13d8ecd1e49e6be10eca321dd51
target: esp32s3
version: 1.0.0

View File

@ -0,0 +1,16 @@
if(IDF_TARGET STREQUAL "esp32s3")
set(srcs
"test_app_main.c"
"test_wakenet.c"
"test_multinet.c"
"test_afe.c"
)
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "." "samples"
REQUIRES unity esp-sr
WHOLE_ARCHIVE)
endif()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
/* Example test application for testable component.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "unity.h"
void app_main(void)
{
/* These are the different ways of running registered tests.
* In practice, only one of them is usually needed.
*
* UNITY_BEGIN() and UNITY_END() calls tell Unity to print a summary
* (number of tests executed/failed/ignored) of tests executed between these calls.
*/
// print_banner("Executing one test by its name");
// UNITY_BEGIN();
// unity_run_test_by_name("Mean of an empty array is zero");
// UNITY_END();
// print_banner("Running tests with [mean] tag");
// UNITY_BEGIN();
// unity_run_tests_by_tag("[mean]", false);
// UNITY_END();
// print_banner("Running tests without [fails] tag");
// UNITY_BEGIN();
// unity_run_tests_by_tag("[fails]", true);
// UNITY_END();
// print_banner("Running all the registered tests");
// UNITY_BEGIN();
// unity_run_all_tests();
// UNITY_END();
// print_banner("Starting interactive test menu");
/* This function will not return, and will be busy waiting for UART input.
* Make sure that task watchdog is disabled if you use this function.
*/
unity_run_menu();
}

View File

@ -0,0 +1,395 @@
/* test_mean.c: Implementation of a testable component.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRAMTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "string.h"
#include <limits.h>
#include "unity.h"
#include "model_path.h"
#include "esp_mn_iface.h"
#include "esp_mn_models.h"
#include "da_kai_kong_tiao.h"
#include "tell_me_a_joke.h"
#include "alexa.h"
#include "dl_lib_convq_queue.h"
#include <sys/time.h>
#include "esp_mn_speech_commands.h"
#include "esp_process_sdkconfig.h"
TEST_CASE("multinet create/destroy API & memory leak", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
int start_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
int start_internal_size = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
struct timeval tv_start, tv_end;
gettimeofday(&tv_start, NULL);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
int create_size = start_size - heap_caps_get_free_size(MALLOC_CAP_8BIT);
int create_internal_size = start_internal_size - heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
printf("Internal RAM: %d, PSRAM:%d\n", create_internal_size, create_size-create_internal_size);
gettimeofday(&tv_end, NULL);
int tv_ms=(tv_end.tv_sec-tv_start.tv_sec)*1000+(tv_end.tv_usec-tv_start.tv_usec)/1000;
printf("create latency:%d ms\n", tv_ms);
multinet->destroy(model_data);
int first_end_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
int last_end_size = first_end_size;
int mem_leak = start_size - last_end_size;
printf("create&destroy times:%d, memory leak:%d\n", 1, mem_leak);
for (int i=0; i<3; i++) {
printf("create ...\n");
model_data = multinet->create(model_name, 6000);
printf("destroy ...\n");
multinet->destroy(model_data);
last_end_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
mem_leak = start_size - last_end_size;
printf("create&destroy times:%d, memory leak:%d\n", i+2, mem_leak);
}
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, (mem_leak) < 1000 && last_end_size == first_end_size);
}
TEST_CASE("multinet cpu loading", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
int frequency = multinet->get_samp_rate(model_data);
int audio_chunksize = multinet->get_samp_chunksize(model_data) * sizeof(int16_t);
char *lang = multinet->get_language(model_data);
esp_mn_commands_update_from_sdkconfig(multinet, model_data);
unsigned char* data = NULL;
size_t data_size = 0;
if (strcmp(lang, ESP_MN_ENGLISH) == 0) {
data = (unsigned char*)tell_me_a_joke;
data_size = sizeof(tell_me_a_joke);
printf("commands: tell me a joke, size:%d\n", data_size);
} else if(strcmp(lang, ESP_MN_CHINESE) == 0) {
data = (unsigned char*)da_kai_kong_tiao;
data_size = sizeof(da_kai_kong_tiao);
printf("commands: da kai kong tiao, size:%d\n", data_size);
}
int16_t *buffer = malloc(audio_chunksize);
int chunks = 0;
struct timeval tv_start, tv_end;
gettimeofday(&tv_start, NULL);
esp_mn_state_t mn_state;
while (1) {
if ((chunks + 1)*audio_chunksize <= data_size) {
memcpy(buffer, data + chunks * audio_chunksize, audio_chunksize);
} else {
memset(buffer, 0, audio_chunksize);
}
mn_state = multinet->detect(model_data, buffer);
if (mn_state == ESP_MN_STATE_DETECTED) {
esp_mn_results_t *mn_result = multinet->get_results(model_data);
if (mn_result->num > 0)
printf("detected: command id:%d, string:%s\n",mn_result->command_id[0], mn_result->string);
else
printf("timeout\n");
break;
}
chunks++;
if (chunks > 600)
break;
}
gettimeofday(&tv_end, NULL);
int tv_ms=(tv_end.tv_sec-tv_start.tv_sec)*1000+(tv_end.tv_usec-tv_start.tv_usec)/1000;
chunks -= 7;
int run_ms = (chunks)*audio_chunksize/sizeof(int16_t)*1000/frequency;
printf("Done! Took %d ms to parse %d ms worth of samples in %d iterations. CPU loading(single core):%.1f%%\n",
tv_ms, run_ms, chunks, tv_ms*100.0/run_ms);
multinet->destroy(model_data);
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, mn_state == ESP_MN_STATE_DETECTED);
}
TEST_CASE("multinet set commands", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
char *lang = multinet->get_language(model_data);
esp_mn_error_t *error_phrases = NULL;
printf("MODEL NAME %s\n", model_name);
// first
if (strcmp(lang, ESP_MN_ENGLISH) == 0) {
if (strcmp(model_name, "mn5q8_en") == 0) {
TEST_ESP_OK(esp_mn_commands_add(1, "TfL Mm c qbK"));
TEST_ESP_OK(esp_mn_commands_add(2, "hicST qbK"));
} else {
TEST_ESP_OK(esp_mn_commands_add(1, "TURN ON THE LIGHT"));
TEST_ESP_OK(esp_mn_commands_add(2, "TURN OFF THE KITCHEN LIGHT"));
}
error_phrases = esp_mn_commands_update();
} else if(strcmp(lang, ESP_MN_CHINESE) == 0) {
TEST_ESP_OK(esp_mn_commands_add(1, "da kai dian deng"));
TEST_ESP_OK(esp_mn_commands_add(2, "guan bi chu fang dian deng"));
error_phrases = esp_mn_commands_update();
} else {
printf("Invalid language\n");
}
TEST_ASSERT_EQUAL(true, error_phrases == NULL);
// second
if (strcmp(lang, ESP_MN_ENGLISH) == 0) {
if (strcmp(model_name, "mn5q8_en") == 0) {
TEST_ESP_OK(esp_mn_commands_add(3, "TkN nN eL jc LiTS"));
TEST_ESP_OK(esp_mn_commands_add(4, "TkN eF eL jc LiTS"));
} else {
TEST_ESP_OK(esp_mn_commands_add(3, "TURN OFF THE LIGHT"));
TEST_ESP_OK(esp_mn_commands_add(4, "TURN OM THE KITCHEN LIGHT"));
}
error_phrases = esp_mn_commands_update();
} else if(strcmp(lang, ESP_MN_CHINESE) == 0) {
esp_mn_commands_add(3, "guan bi dian deng");
esp_mn_commands_add(4, "da kai chu fang dian deng");
error_phrases = esp_mn_commands_update();
} else {
printf("Invalid language\n");
}
multinet->destroy(model_data);
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, error_phrases == NULL);
}
TEST_CASE("multinet add incorrect commands", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
char *lang = multinet->get_language(model_data);
esp_mn_error_t *error_phrases = NULL;
esp_err_t state = ESP_OK;
if (strcmp(lang, ESP_MN_ENGLISH) == 0) {
state = esp_mn_commands_add(1, "TURN ON THE LIGHT 123");
assert(state == ESP_ERR_INVALID_STATE);
state = esp_mn_commands_add(2, "TURN. OFF THE LIGHT?");
assert(state == ESP_ERR_INVALID_STATE);
} else if(strcmp(lang, ESP_MN_CHINESE) == 0) {
if (strcmp(model_name, "mn6_cn_ac") == 0 || strcmp(model_name, "mn6_cn") == 0) {
state = esp_mn_commands_add(1, "dakai dian deng");
assert(state == ESP_ERR_INVALID_STATE);
state = esp_mn_commands_add(2, "关闭电灯");
assert(state == ESP_ERR_INVALID_STATE);
} else {
state = esp_mn_commands_add(1, "k");
assert(state == ESP_ERR_INVALID_STATE);
}
} else {
printf("Invalid language\n");
}
multinet->destroy(model_data);
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, error_phrases == NULL);
}
TEST_CASE("multinet add duplicated commands", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
char *lang = multinet->get_language(model_data);
esp_mn_error_t *error_phrases = NULL;
if (strcmp(lang, ESP_MN_ENGLISH) == 0) {
if (strcmp(model_name, "mn5q8_en") == 0) {
esp_mn_commands_add(1, "TfL Mm c qbK");
esp_mn_commands_add(1, "TfL Mm c qbK");
} else {
esp_mn_commands_add(1, "TURN ON THE LIGHT");
esp_mn_commands_add(1, "TURN ON THE LIGHT");
}
} else if(strcmp(lang, ESP_MN_CHINESE) == 0) {
esp_mn_commands_add(1, "da kai dian deng");
esp_mn_commands_add(1, "da kai dian deng");
} else {
printf("Invalid language\n");
}
multinet->destroy(model_data);
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, error_phrases == NULL);
}
TEST_CASE("multinet print active commands", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
char *lang = multinet->get_language(model_data);
esp_mn_commands_update_from_sdkconfig(multinet, model_data);
if (strcmp(lang, ESP_MN_ENGLISH) == 0) {
if (strcmp(model_name, "mn5q8_en") == 0) {
esp_mn_commands_add(1, "TfL Mm qbK");
} else {
esp_mn_commands_add(1, "THIS SHOULD NOT APPEAR IN ACTIVE COMMANDS");
}
} else if(strcmp(lang, ESP_MN_CHINESE) == 0) {
esp_mn_commands_add(1, "bu ying gai chu xian zai biao zhong");
} else {
printf("Invalid language\n");
}
multinet->print_active_speech_commands(model_data);
multinet->destroy(model_data);
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, 1);
}
TEST_CASE("multinet remove commands", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
char *lang = multinet->get_language(model_data);
esp_mn_commands_update_from_sdkconfig(multinet, model_data);
if (strcmp(lang, ESP_MN_ENGLISH) == 0) {
if (strcmp(model_name, "mn5q8_en") == 0) {
esp_mn_commands_remove("TfL Mm c qbK");
} else {
esp_mn_commands_remove("TURN ON THE LIGHT");;
}
} else if(strcmp(lang, ESP_MN_CHINESE) == 0) {
esp_mn_commands_remove("da kai dian deng");
} else {
printf("Invalid language\n");
}
esp_mn_commands_update();
multinet->print_active_speech_commands(model_data);
multinet->destroy(model_data);
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, 1);
}
TEST_CASE("multinet clear and add commands", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
char *lang = multinet->get_language(model_data);
esp_mn_commands_update_from_sdkconfig(multinet, model_data);
esp_mn_commands_clear();
if (strcmp(lang, ESP_MN_ENGLISH) == 0) {
if (strcmp(model_name, "mn5q8_en") == 0) {
esp_mn_commands_add(1, "TfL Mm c qbK");
} else {
esp_mn_commands_add(1, "TURN ON THE LIGHT");
}
} else if(strcmp(lang, ESP_MN_CHINESE) == 0) {
esp_mn_commands_add(1, "da kai dian deng");
} else {
printf("Invalid language\n");
}
esp_mn_commands_update();
esp_mn_commands_print();
multinet->print_active_speech_commands(model_data);
if (strcmp(lang, ESP_MN_ENGLISH) == 0) {
if (strcmp(model_name, "mn5q8_en") == 0) {
esp_mn_commands_add(2, "Sgl c Sel");
} else {
esp_mn_commands_add(2, "SING A SONG");
}
} else if(strcmp(lang, ESP_MN_CHINESE) == 0) {
esp_mn_commands_add(1, "guan bi dian deng");
} else {
printf("Invalid language\n");
}
esp_mn_commands_update();
esp_mn_commands_print();
multinet->print_active_speech_commands(model_data);
multinet->destroy(model_data);
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, 1);
}
TEST_CASE("multinet modify commands", "[mn]")
{
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name);
model_iface_data_t *model_data = multinet->create(model_name, 6000);
char *lang = multinet->get_language(model_data);
esp_mn_commands_update_from_sdkconfig(multinet, model_data);
if (strcmp(lang, ESP_MN_ENGLISH) == 0) {
if (strcmp(model_name, "mn5q8_en") == 0) {
esp_mn_commands_modify("TfL Mm c qbK", "TfL TfL Mm c qbK");
} else {
esp_mn_commands_modify("TURN ON THE LIGHT", "TURN ON THE KITCHEN LIGHT");
}
} else if(strcmp(lang, ESP_MN_CHINESE) == 0) {
esp_mn_commands_modify("da kai dian deng", "da kai chu fang deng");
} else {
printf("Invalid language\n");
}
esp_mn_commands_update();
multinet->print_active_speech_commands(model_data);
multinet->destroy(model_data);
esp_srmodel_deinit(models);
TEST_ASSERT_EQUAL(true, 1);
}

View File

@ -25,7 +25,7 @@ TEST_CASE("wakenet create/destroy API & memory leak", "[wn]")
int start_internal_size = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, NULL);
esp_wn_iface_t *wakenet = esp_wn_handle_from_name(model_name);
esp_wn_iface_t *wakenet = (esp_wn_iface_t*)esp_wn_handle_from_name(model_name);
// test model loading time
struct timeval tv_start, tv_end;
@ -52,7 +52,7 @@ TEST_CASE("wakenet create/destroy API & memory leak", "[wn]")
printf("init partition ...\n");
models = esp_srmodel_init("model");
model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, NULL);
wakenet = esp_wn_handle_from_name(model_name);
wakenet = (esp_wn_iface_t*)esp_wn_handle_from_name(model_name);
printf("create ...\n");
// typedef enum {
@ -82,7 +82,7 @@ TEST_CASE("wakenet detect API & cpu loading", "[wn]")
vTaskDelay(500 / portTICK_PERIOD_MS);
srmodel_list_t *models = esp_srmodel_init("model");
char *model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, NULL);
esp_wn_iface_t *wakenet = esp_wn_handle_from_name(model_name);
esp_wn_iface_t *wakenet = (esp_wn_iface_t*)esp_wn_handle_from_name(model_name);
model_iface_data_t *model_data = wakenet->create(model_name, DET_MODE_95);
int frequency = wakenet->get_samp_rate(model_data);
int audio_chunksize = wakenet->get_samp_chunksize(model_data) * sizeof(int16_t);

View File

@ -0,0 +1,4 @@
# Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size
factory, app, factory, 0x010000, 8000k
model, data, , , 7000K,
1 # Espressif ESP32 Partition Table
2 # Name, Type, SubType, Offset, Size
3 factory, app, factory, 0x010000, 8000k
4 model, data, , , 7000K,

View File

@ -0,0 +1,39 @@
import pytest
from pytest_embedded import Dut
@pytest.mark.target('esp32s3')
@pytest.mark.env('esp32s3')
@pytest.mark.parametrize(
'config',
[
'mn5q8_cn',
'mn6_cn',
],
)
def test_multinet(dut: Dut)-> None:
dut.run_all_single_board_cases(group="mn")
@pytest.mark.target('esp32s3')
@pytest.mark.env('esp32s3')
@pytest.mark.parametrize(
'config',
[
'mn5q8_cn',
'wn9_hilexin',
],
)
def test_wakenet(dut: Dut)-> None:
dut.run_all_single_board_cases(group="wn")
@pytest.mark.target('esp32s3')
@pytest.mark.env('esp32s3')
@pytest.mark.parametrize(
'config',
[
'wn9_hilexin',
],
)
def test_afe(dut: Dut)-> None:
dut.run_all_single_board_cases(group="afe")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.5)
# Include the components directory of the main application:
#
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
"../../../esp-sr")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp_sr_test)

View File

@ -0,0 +1,26 @@
set(srcs
"test_app_main.c"
"test_chinese_tts.c"
)
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
REQUIRES unity esp-sr
WHOLE_ARCHIVE)
set(voice_data_image ${PROJECT_DIR}/../../esp-tts/esp_tts_chinese/esp_tts_voice_data_xiaoxin_small.dat)
add_custom_target(voice_data ALL DEPENDS ${voice_data_image})
add_dependencies(flash voice_data)
partition_table_get_partition_info(size "--partition-name voice_data" "size")
partition_table_get_partition_info(offset "--partition-name voice_data" "offset")
if("${size}" AND "${offset}")
esptool_py_flash_to_partition(flash "voice_data" "${voice_data_image}")
else()
set(message "Failed to find model in partition table file"
"Please add a line(Name=voice_data, Type=data, Size=3890K) to the partition file.")
endif()

View File

@ -0,0 +1,48 @@
/* Example test application for testable component.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "unity.h"
void app_main(void)
{
/* These are the different ways of running registered tests.
* In practice, only one of them is usually needed.
*
* UNITY_BEGIN() and UNITY_END() calls tell Unity to print a summary
* (number of tests executed/failed/ignored) of tests executed between these calls.
*/
// print_banner("Executing one test by its name");
// UNITY_BEGIN();
// unity_run_test_by_name("Mean of an empty array is zero");
// UNITY_END();
// print_banner("Running tests with [mean] tag");
// UNITY_BEGIN();
// unity_run_tests_by_tag("[mean]", false);
// UNITY_END();
// print_banner("Running tests without [fails] tag");
// UNITY_BEGIN();
// unity_run_tests_by_tag("[fails]", true);
// UNITY_END();
// print_banner("Running all the registered tests");
// UNITY_BEGIN();
// unity_run_all_tests();
// UNITY_END();
// print_banner("Starting interactive test menu");
/* This function will not return, and will be busy waiting for UART input.
* Make sure that task watchdog is disabled if you use this function.
*/
unity_run_menu();
}

View File

@ -27,7 +27,7 @@ TEST_CASE("chinese tts create/destroy API & memory leak", "[tts]")
int start_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
int start_internal_size = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
/*** 1. create esp tts handle ***/
void* voicedata;
const void* voicedata;
const esp_partition_t* partition=esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "voice_data");
assert(partition != NULL);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)

View File

@ -0,0 +1,3 @@
# Name, Type, SubType, Offset, Size
factory, app, factory, 0x010000, 4M
voice_data, data, fat, 0x410000, 3890K
1 # Name Type SubType Offset Size
2 factory app factory 0x010000 4M
3 voice_data data fat 0x410000 3890K

View File

@ -0,0 +1,13 @@
import pytest
from pytest_embedded import Dut
@pytest.mark.target('esp32s3')
@pytest.mark.env('esp32s3')
@pytest.mark.parametrize(
'config',
[
'xiaoxin',
],
)
def test_tts(dut: Dut)-> None:
dut.run_all_single_board_cases(group="tts")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
pytest
pytest-embedded-idf
pytest-embedded-serial-esp
pytest-rerunfailures
pytest-timeout