diff options
-rw-r--r-- | .ci/templates/job-uhd-streaming-tests-beauty.yml | 25 | ||||
-rw-r--r-- | .ci/templates/job-uhd-streaming-tests.yml | 83 | ||||
-rw-r--r-- | .ci/templates/stages-uhd-pipeline.yml | 16 | ||||
-rw-r--r-- | .ci/uhd-pipeline-pr.yml | 11 | ||||
-rw-r--r-- | .ci/uhd-pipeline.yml | 11 | ||||
-rw-r--r-- | host/tests/pytests/conftest.py | 59 | ||||
-rw-r--r-- | host/tests/pytests/test_length_utils.py | 20 | ||||
-rw-r--r-- | host/tests/pytests/test_streaming.py | 303 |
8 files changed, 528 insertions, 0 deletions
diff --git a/.ci/templates/job-uhd-streaming-tests-beauty.yml b/.ci/templates/job-uhd-streaming-tests-beauty.yml new file mode 100644 index 000000000..0f9065830 --- /dev/null +++ b/.ci/templates/job-uhd-streaming-tests-beauty.yml @@ -0,0 +1,25 @@ +parameters: +- name: testOS + type: string + values: + - ubuntu2004 +- name: uhdSrcDir + type: string +- name: testLength + type: string + +jobs: +- template: job-uhd-streaming-tests.yml + parameters: + suiteName: 'beauty' + testOS: '${{ parameters.testOS }}' + testLength: '${{ parameters.testLength }}' + toolset: 'make' + uhdSrcDir: '${{ parameters.uhdSrcDir }}' + dutMatrix: + beauty-N320-0 XG: + dutName: 'beauty-N320-0' + dutType: 'N320' + dutAddr: '192.168.10.2' + dutSecondAddr: '192.168.20.2' + dutMgmtAddr: '10.0.57.13' diff --git a/.ci/templates/job-uhd-streaming-tests.yml b/.ci/templates/job-uhd-streaming-tests.yml new file mode 100644 index 000000000..9a9b3168d --- /dev/null +++ b/.ci/templates/job-uhd-streaming-tests.yml @@ -0,0 +1,83 @@ +parameters: +- name: suiteName + type: string +- name: testOS + type: string + values: + - ubuntu2004 +- name: toolset + type: string + values: + - make +- name: uhdSrcDir + type: string +- name: dutMatrix + type: object +- name: testLength + type: string + values: + - 'smoke' + - 'full' + - 'stress' + +jobs: +- job: uhd_streaming_tests_${{ parameters.suiteName }} + displayName: uhd streaming tests ${{ parameters.suiteName }} + timeoutInMinutes: 180 + pool: + name: de-dre-lab + demands: + - suiteName -equals ${{ parameters.suiteName }} + strategy: + matrix: ${{ parameters.dutMatrix }} + workspace: + clean: outputs + steps: + - checkout: self + clean: true + - download: current + artifact: ${{ parameters.testOS }}-${{ parameters.toolset }} + displayName: download pipeline artifact ${{ parameters.testOS }}-${{ parameters.toolset }} + - task: ExtractFiles@1 + inputs: + archiveFilePatterns: $(Pipeline.Workspace)/${{ parameters.testOS }}-${{ parameters.toolset }}/${{ parameters.testOS }}-${{ parameters.toolset }}.tar.gz + destinationFolder: $(Build.BinariesDirectory) + cleanDestinationFolder: true + - script: | + cd ${{ parameters.uhdSrcDir }}/host/tests/streaming_performance + sudo ./setup.sh --auto + sleep 5 + displayName: setup interfaces for use without DPDK + - script: | + set -x + export PYTHONPATH=${{ parameters.uhdSrcDir }}/host/tests/streaming_performance + cd ${{ parameters.uhdSrcDir }}/host/tests/pytests + python3 -m pytest -s test_streaming.py -m "not dpdk" --dut_type $(dutType) --test_length ${{ parameters.testLength }} \ + --addr $(dutAddr) --second_addr $(dutSecondAddr) --mgmt_addr $(dutMgmtAddr) \ + --uhd_build_dir $(Build.BinariesDirectory)/uhddev/build --junitxml $(Common.TestResultsDirectory)/TEST-$(dutName).xml + continueOnError: true + displayName: Run streaming tests on $(dutName) + - script: | + cd ${{ parameters.uhdSrcDir }}/host/tests/streaming_performance + sudo ./setup.sh --auto --dpdk + sleep 5 + displayName: setup interfaces for use with DPDK + - script: | + set -x + export PYTHONPATH=${{ parameters.uhdSrcDir }}/host/tests/streaming_performance + cd ${{ parameters.uhdSrcDir }}/host/tests/pytests + sudo --preserve-env=PYTHONPATH python3 -m pytest -s test_streaming.py -m "dpdk" --dut_type $(dutType) --test_length ${{ parameters.testLength }} \ + --addr $(dutAddr) --second_addr $(dutSecondAddr) --mgmt_addr $(dutMgmtAddr) \ + --uhd_build_dir $(Build.BinariesDirectory)/uhddev/build --junitxml $(Common.TestResultsDirectory)/TEST-$(dutName)-dpdk.xml + continueOnError: true + displayName: Run streaming tests with DPDK on $(dutName) + - task: PublishTestResults@2 + inputs: + testResultsFormat: 'JUnit' + testResultsFiles: '$(Common.TestResultsDirectory)/TEST-*.xml' + testRunTitle: $(dutName) streaming tests + buildConfiguration: 'Release' + mergeTestResults: true + failTaskOnFailedTests: false + displayName: Upload streaming test results + diff --git a/.ci/templates/stages-uhd-pipeline.yml b/.ci/templates/stages-uhd-pipeline.yml index f5ff5c227..a67dbed97 100644 --- a/.ci/templates/stages-uhd-pipeline.yml +++ b/.ci/templates/stages-uhd-pipeline.yml @@ -14,6 +14,11 @@ parameters: - name: release_binaries type: boolean default: false +- name: testLength + type: string +- name: run_streaming_tests + type: boolean + default: false variables: - template: ../uhd-pipeline-vars.yml @@ -71,3 +76,14 @@ stages: parameters: testOS: ubuntu2004 uhdSrcDir: $(Build.SourcesDirectory) + +- stage: test_streaming_stage + displayName: Test UHD Streaming + dependsOn: build_uhd_stage + condition: and(succeeded('build_uhd_stage'), ${{ parameters.run_streaming_tests }}) + jobs: + - template: job-uhd-streaming-tests-beauty.yml + parameters: + testOS: ubuntu2004 + uhdSrcDir: $(Build.SourcesDirectory) + testLength: ${{ parameters.testLength }}
\ No newline at end of file diff --git a/.ci/uhd-pipeline-pr.yml b/.ci/uhd-pipeline-pr.yml index 9887214bf..2c1c7d247 100644 --- a/.ci/uhd-pipeline-pr.yml +++ b/.ci/uhd-pipeline-pr.yml @@ -14,6 +14,15 @@ parameters: - name: release_binaries type: boolean default: false +- name: testLength + type: string + values: + - 'smoke' + - 'full' + - 'stress' +- name: run_streaming_tests + type: boolean + default: False trigger: none @@ -38,3 +47,5 @@ extends: custom_boost_version: ${{ parameters.custom_boost_version }} custom_boost_version_url: ${{ parameters.custom_boost_version_url }} release_binaries: ${{ parameters.release_binaries }} + testLength: ${{ parameters.testLength }} + run_streaming_tests: ${{ parameters.run_streaming_tests }} diff --git a/.ci/uhd-pipeline.yml b/.ci/uhd-pipeline.yml index 8ea2eb4f5..b2ffffc6f 100644 --- a/.ci/uhd-pipeline.yml +++ b/.ci/uhd-pipeline.yml @@ -8,6 +8,15 @@ parameters: - name: release_binaries type: boolean default: false +- name: testLength + type: string + values: + - 'smoke' + - 'full' + - 'stress' +- name: run_streaming_tests + type: boolean + default: False trigger: batch: true @@ -31,3 +40,5 @@ extends: template: templates/stages-uhd-pipeline.yml parameters: release_binaries: ${{ parameters.release_binaries }} + testLength: ${{ parameters.testLength }} + run_streaming_tests: ${{ parameters.run_streaming_tests }} diff --git a/host/tests/pytests/conftest.py b/host/tests/pytests/conftest.py new file mode 100644 index 000000000..dfca5d69f --- /dev/null +++ b/host/tests/pytests/conftest.py @@ -0,0 +1,59 @@ +import test_length_utils + +dut_type_list = [ + "N310", + "N320", + "B210", + "E320", + "X310", + "X310_TwinRx", + "X410" +] + + +test_length_list = [ + test_length_utils.Test_Length_Smoke, + test_length_utils.Test_Length_Full, + test_length_utils.Test_Length_Stress +] + + +def pytest_addoption(parser): + parser.addoption( + "--addr", + type=str, + help="address of first 10 GbE interface",) + parser.addoption( + "--second_addr", + type=str, + help="address of second 10 GbE interface") + parser.addoption( + "--name", + type=str, + help="name of B2xx device") + parser.addoption( + "--mgmt_addr", + type=str, + help="address of management interface. only needed for DPDK test cases") + parser.addoption( + "--dut_type", + type=str, + required=True, + choices=dut_type_list, + help="") + parser.addoption( + "--test_length", + type=str, + default=test_length_utils.Test_Length_Full, + choices=test_length_list, + help="") + parser.addoption( + "--uhd_build_dir", + required=True, + type=str, + help="") + + +def pytest_configure(config): + # register additional markers + config.addinivalue_line("markers", "dpdk: run with DPDK enable") diff --git a/host/tests/pytests/test_length_utils.py b/host/tests/pytests/test_length_utils.py new file mode 100644 index 000000000..db4eeab13 --- /dev/null +++ b/host/tests/pytests/test_length_utils.py @@ -0,0 +1,20 @@ +from collections import namedtuple +import pytest + +# This provides a way to run a quick smoke test run for PRs, a more exhaustive set +# of tests for nightly runs, and long running tests for stress tests over the weekend +# +# smoke: subset of tests, short duration +# full: all test cases, short duration +# stress: subset of tests, long duration +Test_Length_Smoke = "smoke" +Test_Length_Full = "full" +Test_Length_Stress = "stress" + +test_length_params = namedtuple('test_length_params', 'iterations duration') + +def select_test_cases_by_length(test_length, test_cases): + if test_length == Test_Length_Full: + return [test_case[1] for test_case in test_cases] + else: + return [test_case[1] for test_case in test_cases if test_length in test_case[0]]
\ No newline at end of file diff --git a/host/tests/pytests/test_streaming.py b/host/tests/pytests/test_streaming.py new file mode 100644 index 000000000..a7e26354a --- /dev/null +++ b/host/tests/pytests/test_streaming.py @@ -0,0 +1,303 @@ +import pytest +from pathlib import Path +import batch_run_benchmark_rate +import test_length_utils +from test_length_utils import Test_Length_Smoke, Test_Length_Full, Test_Length_Stress + +ARGNAMES_DUAL_10G = ["dual_10G", "rate", "rx_rate", "rx_channels", "tx_rate", "tx_channels"] +ARGNAMES = ["rate", "rx_rate", "rx_channels", "tx_rate", "tx_channels"] + +def parametrize_test_length(metafunc, test_length, fast_params, stress_params): + argnames = ["iterations", "duration"] + + # select how long to run tests + if(test_length == Test_Length_Smoke or test_length == Test_Length_Full): + argvalues = [ + # iterations duration test case ID + # ------------------------------------------------------------ + pytest.param(fast_params.iterations, fast_params.duration, id="fast"), + ] + elif(test_length == Test_Length_Stress): + argvalues = [ + # iterations duration test case ID + # ---------------------------------------------------------- + pytest.param(stress_params.iterations, stress_params.duration, id="stress"), + ] + + metafunc.parametrize(argnames, argvalues) + + +def generate_N310_test_cases(metafunc, test_length): + test_cases = [ + # Test Lengths dual_10G rate rx_rate rx_channels tx_rate tx_channels test case ID + # ---------------------------------------------------------------------------------------------------------------------------------------------- + [{}, pytest.param(False, 153.6e6, 153.6e6, "0", 0, "", id="1x10GbE-1xRX@153.6e6")], + [{}, pytest.param(False, 153.6e6, 153.6e6, "0,1", 0, "", id="1x10GbE-2xRX@153.6e6")], + [{}, pytest.param(False, 153.6e6, 0, "", 153.6e6, "0", id="1x10GbE-1xTX@153.6e6")], + [{}, pytest.param(False, 153.6e6, 0, "", 153.6e6, "0,1", id="1x10GbE-2xTX@153.6e6")], + [{}, pytest.param(False, 153.6e6, 153.6e6, "0", 153.6e6, "0", id="1x10GbE-1xTRX@153.6e6")], + [{Test_Length_Stress, Test_Length_Smoke}, pytest.param(False, 153.6e6, 153.6e6, "0,1", 153.6e6, "0,1", id="1x10GbE-2xTRX@153.6e6")], + [{}, pytest.param(False, 125e6, 125e6, "0,1", 125e6, "0,1", id="1x10GbE-2xTRX@125e6")], + [{}, pytest.param(False, 62.5e6, 62.5e6, "0,1,2,3", 0, "", id="1x10GbE-4xRX@62.5e6")], + [{}, pytest.param(False, 62.5e6, 0, "", 62.5e6, "0,1,2,3", id="1x10GbE-4xTX@62.5e6")], + [{Test_Length_Smoke, Test_Length_Stress}, pytest.param(False, 62.5e6, 62.5e6, "0,1,2,3", 62.5e6, "0,1,2,3", id="1x10GbE-4xTRX@62.5e6")], + [{}, pytest.param(True, 153.6e6, 153.6e6, "0,1", 0, "", id="2x10GbE-2xRX@153.6e6")], + [{}, pytest.param(True, 153.6e6, 0, "", 153.6e6, "0,1", id="2x10GbE-2xTX@153.6e6")], + [{}, pytest.param(True, 153.6e6, 153.6e6, "0,1", 153.6e6, "0,1", id="2x10GbE-2xTRX@153.6e6")], + [{}, pytest.param(True, 153.6e6, 153.6e6, "0,1,2,3", 0, "", id="2x10GbE-4xRX@153.6e6")], + [{}, pytest.param(True, 153.6e6, 0, "", 153.6e6, "0,1,2,3", id="2x10GbE-4xTX@153.6e6")], + [{}, pytest.param(True, 153.6e6, 153.6e6, "0,1,2,3", 153.6e6, "0,1,2,3", id="2x10GbE-4xTRX@153.6e6")], + [{}, pytest.param(True, 125e6, 125e6, "0,1,2,3", 0, "", id="2x10GbE-4xRX@125e6")], + [{}, pytest.param(True, 125e6, 0, "", 125e6, "0,1,2,3", id="2x10GbE-4xTX@62.5e6")], + [{Test_Length_Smoke, Test_Length_Stress}, pytest.param(True, 125e6, 125e6, "0,1,2,3", 125e6, "0,1,2,3", id="2x10GbE-4xTRX@62.5e6")], + ] + + argvalues = test_length_utils.select_test_cases_by_length(test_length, test_cases) + metafunc.parametrize(ARGNAMES_DUAL_10G, argvalues) + + fast_params = test_length_utils.test_length_params(iterations=10, duration=30) + stress_params = test_length_utils.test_length_params(iterations=2, duration=600) + parametrize_test_length(metafunc, test_length, fast_params, stress_params) + + +def generate_N320_test_cases(metafunc, test_length): + test_cases = [ + # Test Lengths dual_10G rate rx_rate rx_channels tx_rate tx_channels test case ID + # --------------------------------------------------------------------------------------------------------------------------------------------- + [{}, pytest.param(False, 250e6, 250e6, "0", 0, "", id="1x10GbE-1xRX@250e6")], + [{}, pytest.param(False, 250e6, 0, "", 250e6, "0", id="1x10GbE-1xTX@250e6")], + [{Test_Length_Stress, Test_Length_Smoke}, pytest.param(False, 250e6, 250e6, "0", 250e6, "0", id="1x10GbE-1xTRX@250e6")], + [{}, pytest.param(True, 250e6, 250e6, "0,1", 0, "", id="2x10GbE-2xRX@250e6")], + [{}, pytest.param(True, 250e6, 0, "", 250e6, "0,1", id="2x10GbE-2xTX@250e6")], + [{Test_Length_Stress, Test_Length_Smoke}, pytest.param(True, 250e6, 250e6, "0,1", 250e6, "0,1", id="2x10GbE-2xTRX@250e6")], + ] + + argvalues = test_length_utils.select_test_cases_by_length(test_length, test_cases) + metafunc.parametrize(ARGNAMES_DUAL_10G, argvalues) + + fast_params = test_length_utils.test_length_params(iterations=10, duration=30) + stress_params = test_length_utils.test_length_params(iterations=2, duration=600) + parametrize_test_length(metafunc, test_length, fast_params, stress_params) + + +def generate_B210_test_cases(metafunc, test_length): + test_cases = [ + # Test Lengths rate rx_rate rx_channels tx_rate tx_channels test case ID + # ------------------------------------------------------------------------------------------------------------------------------ + [{}, pytest.param(61.44e6, 61.44e6, "0", 0, "", id="1xRX@61.44e6")], + [{}, pytest.param(30.72e6, 30.72e6, "0,1", 0, "", id="2xRX@30.72e6")], + [{}, pytest.param(61.44e6, 0, "", 61.44e6, "0", id="1xTX@61.44e6")], + [{}, pytest.param(30.72e6, 0, "", 30.72e6, "0,1", id="2xTX@30.72e6")], + [{Test_Length_Stress, Test_Length_Smoke}, pytest.param(30.72e6, 30.72e6, "0", 30.72e6, "0", id="1xTRX@30.72e6")], + [{}, pytest.param(15.36e6, 15.36e6, "0,1", 15.36e6, "0,1", id="2xTRX@15.36e6")], + ] + + argvalues = test_length_utils.select_test_cases_by_length(test_length, test_cases) + metafunc.parametrize(ARGNAMES, argvalues) + + fast_params = test_length_utils.test_length_params(iterations=10, duration=30) + stress_params = test_length_utils.test_length_params(iterations=2, duration=600) + parametrize_test_length(metafunc, test_length, fast_params, stress_params) + + +def generate_E320_test_cases(metafunc, test_length): + test_cases = [ + # Test Lengths rate rx_rate rx_channels tx_rate tx_channels test case ID + # ------------------------------------------------------------------------------------------------------------------------------ + [{}, pytest.param(61.44e6, 61.44e6, "0", 0, "", id="1xRX@61.44e6")], + [{}, pytest.param(61.44e6, 61.44e6, "0,1", 0, "", id="2xRX@61.44e6")], + [{}, pytest.param(61.44e6, 0, "", 61.44e6, "0", id="1xTX@61.44e6")], + [{}, pytest.param(61.44e6, 0, "", 61.44e6, "0,1", id="2xTX@61.44e6")], + [{Test_Length_Stress, Test_Length_Smoke}, pytest.param(61.44e6, 61.44e6, "0", 61.44e6, "0", id="1xTRX@61.44e6")], + [{}, pytest.param(61.44e6, 61.44e6, "0,1", 61.44e6, "0,1", id="2xTRX@61.44e6")], + + ] + + argvalues = test_length_utils.select_test_cases_by_length(test_length, test_cases) + metafunc.parametrize(ARGNAMES, argvalues) + + fast_params = test_length_utils.test_length_params(iterations=10, duration=30) + stress_params = test_length_utils.test_length_params(iterations=2, duration=600) + parametrize_test_length(metafunc, test_length, fast_params, stress_params) + +def generate_X310_test_cases(metafunc, test_length): + test_cases = [ + # Test Lengths dual_10G rate rx_rate rx_channels tx_rate tx_channels test case ID + # --------------------------------------------------------------------------------------------------------------------------------------------- + [{}, pytest.param(False, 200e6, 200e6, "0", 0, "", id="1x10GbE-1xRX@200e6")], + [{}, pytest.param(False, 100e6, 100e6, "0,1", 0, "", id="1x10GbE-2xRX@100e6")], + [{}, pytest.param(False, 200e6, 0, "", 200e6, "0", id="1x10GbE-1xTX@200e6")], + [{}, pytest.param(False, 100e6, 0, "", 100e6, "0,1", id="1x10GbE-2xTX@100e6")], + [{}, pytest.param(False, 200e6, 200e6, "0", 200e6, "0", id="1x10GbE-1xTRX@200e6")], + [{Test_Length_Stress, Test_Length_Smoke}, pytest.param(False, 100e6, 100e6, "0,1", 100e6, "0", id="1x10GbE-2xTRX@100e6")], + [{}, pytest.param(True, 200e6, 200e6, "0,1", 0, "", id="2x10GbE-2xRX@200e6")], + [{}, pytest.param(True, 200e6, 0, "", 200e6, "0,1", id="2x10GbE-2xTX@200e6")], + [{Test_Length_Stress, Test_Length_Smoke}, pytest.param(True, 200e6, 200e6, "0,1", 200e6, "0,1", id="2x10GbE-2xTRX@200e6")], + ] + + argvalues = test_length_utils.select_test_cases_by_length(test_length, test_cases) + metafunc.parametrize(ARGNAMES_DUAL_10G, argvalues) + + fast_params = test_length_utils.test_length_params(iterations=10, duration=60) + stress_params = test_length_utils.test_length_params(iterations=2, duration=600) + parametrize_test_length(metafunc, test_length, fast_params, stress_params) + +def generate_X310_TwinRx_test_cases(metafunc, test_length): + test_cases = [ + # Test Lengths dual_10G rate rx_rate rx_channels tx_rate tx_channels test case ID + # -------------------------------------------------------------------------------------------------------------------------------------------- + [{}, pytest.param(False, 100e6, 100e6, "0,1,2", 0, "", id="1x10GbE-3xRX@100e6")], + [{}, pytest.param(False, 50e6, 50e6, "0,1,2,4", 0, "", id="1x10GbE-4xRX@50e6")], + [{Test_Length_Stress, Test_Length_Smoke}, pytest.param(True, 100e6, 100e6, "0,1,2,4", 0, "", id="2x10GbE-4xRX@100e6")], + ] + + argvalues = test_length_utils.select_test_cases_by_length(test_length, test_cases) + metafunc.parametrize(ARGNAMES_DUAL_10G, argvalues) + + fast_params = test_length_utils.test_length_params(iterations=10, duration=30) + stress_params = test_length_utils.test_length_params(iterations=2, duration=600) + parametrize_test_length(metafunc, test_length, fast_params, stress_params) + +def generate_X410_test_cases(metafunc, test_length): + test_cases = [ + # Test Lengths dual_10G rate rx_rate rx_channels tx_rate tx_channels test case ID + # ------------------------------------------------------------------------------------------------------------------------------ + [{}, pytest.param(False, 200e6, 200e6, "0", 0, "", id="1x10GbE-1xRX@200e6")], + [{}, pytest.param(False, 200e6, 100e6, "0,1", 0, "", id="1x10GbE-2xRX@100e6")], + [{}, pytest.param(False, 200e6, 0, "", 200e6, "0", id="1x10GbE-1xTX@200e6")], + [{}, pytest.param(False, 200e6, 0, "", 100e6, "0,1", id="1x10GbE-2xTX@100e6")], + [{Test_Length_Stress, Test_Length_Smoke}, pytest.param(False, 200e6, 200e6, "0", 200e6, "0", id="1x10GbE-1xTRX@200e6")], + [{}, pytest.param(False, 200e6, 100e6, "0,1", 100e6, "0,1", id="1x10GbE-2xTRX@100e6")], + [{}, pytest.param(True, 200e6, 200e6, "0,1", 0, "", id="2x10GbE-2xRX@200e6")], + [{}, pytest.param(True, 200e6, 0, "", 200e6, "0,1", id="2x10GbE-2xTX@200e6")], + [{Test_Length_Stress, Test_Length_Smoke}, pytest.param(True, 200e6, 100e6, "0,1", 100e6, "0,1", id="2x10GbE-2xTRX@100e6")], + ] + + argvalues = test_length_utils.select_test_cases_by_length(test_length, test_cases) + metafunc.parametrize(ARGNAMES_DUAL_10G, argvalues) + + fast_params = test_length_utils.test_length_params(iterations=10, duration=60) + stress_params = test_length_utils.test_length_params(iterations=2, duration=600) + parametrize_test_length(metafunc, test_length, fast_params, stress_params) + + +def pytest_generate_tests(metafunc): + dut_type = metafunc.config.getoption("dut_type") + test_length = metafunc.config.getoption("test_length") + + metafunc.parametrize("dut_type", [dut_type]) + + if dut_type.lower() != "b210": + argvalues_DPDK = [ + # use_dpdk test case ID marks + pytest.param(True, id="DPDK", marks=pytest.mark.dpdk), + pytest.param(False, id="NO DPDK",) + ] + metafunc.parametrize("use_dpdk", argvalues_DPDK) + + if dut_type.lower() == 'n310': + generate_N310_test_cases(metafunc, test_length) + elif dut_type.lower() == 'n320': + generate_N320_test_cases(metafunc, test_length) + elif dut_type.lower() == 'b210': + generate_B210_test_cases(metafunc, test_length) + elif dut_type.lower() == 'e320': + generate_E320_test_cases(metafunc, test_length) + elif dut_type.lower() == 'x310': + generate_X310_test_cases(metafunc, test_length) + elif dut_type.lower() == 'x310_twinrx': + generate_X310_TwinRx_test_cases(metafunc, test_length) + elif dut_type.lower() == 'x410': + generate_X410_test_cases(metafunc, test_length) + + +def test_streaming(pytestconfig, dut_type, use_dpdk, dual_10G, rate, rx_rate, rx_channels, + tx_rate, tx_channels, iterations, duration): + + benchmark_rate_path = Path(pytestconfig.getoption('uhd_build_dir')) / 'examples/benchmark_rate' + + # construct device args string + device_args = f"master_clock_rate={rate}," + + if dut_type == "B210": + device_args += f"name={pytestconfig.getoption('name')}," + else: + device_args += f"addr={pytestconfig.getoption('addr')}," + + if dual_10G: + device_args += f"second_addr={pytestconfig.getoption('second_addr')}," + + if use_dpdk: + device_args += f"use_dpdk=1,mgmt_addr={pytestconfig.getoption('mgmt_addr')}" + + # construct benchmark_rate params dictionary + benchmark_rate_params = { + "args": device_args, + "duration": duration, + } + + if rx_channels: + benchmark_rate_params["rx_rate"] = rx_rate + benchmark_rate_params["rx_channels"] = rx_channels + + if tx_channels: + benchmark_rate_params["tx_rate"] = tx_rate + benchmark_rate_params["tx_channels"] = tx_channels + + # run benchmark rate + print() + results = batch_run_benchmark_rate.run(benchmark_rate_path, iterations, benchmark_rate_params) + stats = batch_run_benchmark_rate.calculate_stats(results) + print(batch_run_benchmark_rate.get_summary_string(stats, iterations, benchmark_rate_params)) + + # compare results against thresholds + dropped_samps_threshold = 0 + overruns_threshold = 2 + rx_timeouts_threshold = 0 + rx_seq_err_threshold = 0 + + underruns_threshold = 2 + tx_timeouts_threshold = 0 + tx_seq_err_threshold = 0 + + late_cmds_threshold = 0 + + # TODO: define custom failed assertion explanations to avoid extra output + # https://docs.pytest.org/en/6.2.x/assert.html#defining-your-own-explanation-for-failed-assertions + + if rx_channels: + assert stats.avg_vals.dropped_samps <= dropped_samps_threshold, \ + f"""Number of dropped samples exceeded threshold. + Expected dropped samples: <= {dropped_samps_threshold} + Actual dropped samples: {stats.avg_vals.dropped_samps}""" + assert stats.avg_vals.overruns <= overruns_threshold, \ + f"""Number of overruns exceeded threshold. + Expected overruns: <= {overruns_threshold} + Actual overruns: {stats.avg_vals.overruns}""" + assert stats.avg_vals.rx_timeouts <= rx_timeouts_threshold, \ + f"""Number of rx timeouts exceeded threshold. + Expected rx timeouts: <= {rx_timeouts_threshold} + Actual rx timeouts: {stats.avg_vals.rx_timeouts}""" + assert stats.avg_vals.rx_seq_errs <= rx_seq_err_threshold, \ + f"""Number of rx sequence errors exceeded threshold. + Expected rx sequence errors: <= {rx_seq_err_threshold} + Actual rx sequence errors: {stats.avg_vals.rx_seq_errs}""" + + if tx_channels: + assert stats.avg_vals.underruns <= underruns_threshold, \ + f"""Number of underruns exceeded threshold. + Expected underruns: <= {underruns_threshold} + Actual underruns: {stats.avg_vals.underruns}""" + assert stats.avg_vals.tx_timeouts <= tx_timeouts_threshold, \ + f"""Number of tx timeouts exceeded threshold. + Expected tx timeouts: <= {tx_timeouts_threshold} + Actual tx timeouts: {stats.avg_vals.tx_timeouts}""" + assert stats.avg_vals.tx_seq_errs <= tx_seq_err_threshold, \ + f"""Number of tx sequence errors exceeded threshold. + Expected tx sequence errors: <= {tx_seq_err_threshold} + Actual tx sequence errors: {stats.avg_vals.tx_seq_errs}""" + + assert stats.avg_vals.late_cmds <= late_cmds_threshold, \ + f"""Number of late commands exceeded threshold. + Expected late commands: <= {late_cmds_threshold} + Actual late commands: {stats.avg_vals.late_cmds}""" |