Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/scripts/setup_environment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ function setup_ubuntu_install_dependencies() {
clang \
libsdl2-dev \
libsdl2-ttf-dev \
cmake
cmake \
linuxptp \
ethtool \
netsniff-ng

# CiCd only
if [ "${CICD_BUILD}" == "1" ]; then
Expand Down
13 changes: 11 additions & 2 deletions .github/workflows/custom-pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,20 @@ jobs:
--pci_device "${{ env.PCI_DEVICE }}" \
--ip_address 127.0.0.1 \
--username "${{ secrets.RUNNER_USERNAME }}" \
--key_path "${{ secrets.RUNNER_KEY_PATH }}"
--key_path ${{ secrets.RUNNER_KEY_PATH }} \
--ebu_ip ${{ secrets.RUNNER_EBU_LIST_IP }} \
--ebu_user ${{ secrets.RUNNER_EBU_LIST_USER }} \
--ebu_password ${{ secrets.RUNNER_EBU_LIST_PASSWORD }}
- name: Kill active MtlManager and pytest processes
run: |
sudo killall -SIGINT MtlManager || true
sudo killall -SIGINT pipenv || true
sudo killall -SIGINT pytest || true
sudo killall -SIGINT MtlManager || true
sudo killall -SIGINT phc2sys || true
- name: Export workflow tag for PCAP naming
shell: bash
run: |
echo "MTL_GITHUB_WORKFLOW=${{ github.workflow }}:${{ inputs.nic }}" >> "$GITHUB_ENV"
- name: Start MtlManager
run: |
sudo MtlManager &
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/nightly-pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,20 @@ jobs:
--pci_device ${{ env.PCI_DEVICE }} \
--ip_address 127.0.0.1 \
--username ${{ secrets.RUNNER_USERNAME }} \
--key_path ${{ secrets.RUNNER_KEY_PATH }}
--key_path ${{ secrets.RUNNER_KEY_PATH }} \
--ebu_ip ${{ secrets.RUNNER_EBU_LIST_IP }} \
--ebu_user ${{ secrets.RUNNER_EBU_LIST_USER }} \
--ebu_password ${{ secrets.RUNNER_EBU_LIST_PASSWORD }}
- name: 'preparation: Kill MtlManager and pytest routines'
run: |
sudo killall -SIGINT pipenv || true
sudo killall -SIGINT pytest || true
sudo killall -SIGINT MtlManager || true
sudo killall -SIGINT phc2sys || true
- name: Export workflow tag for PCAP naming
shell: bash
run: |
echo "MTL_GITHUB_WORKFLOW=${{ github.workflow }}:${{ matrix.nic }}" >> "$GITHUB_ENV"
- name: 'preparation: Start MtlManager at background'
run: |
sudo MtlManager &
Expand Down
17 changes: 13 additions & 4 deletions .github/workflows/smoke-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ jobs:
if: ${{ needs.smoke-check-for-changes.outputs.changed == 'true' }}
run: |
if [ "${{ matrix.nic }}" = "e810" ]; then
echo "PCI_DEVICE=8086:1592" >> "$GITHUB_ENV"
echo "PCI_DEVICE=8086:1592,8086:1592" >> "$GITHUB_ENV"
elif [ "${{ matrix.nic }}" = "e810-dell" ]; then
echo "PCI_DEVICE=8086:1592" >> "$GITHUB_ENV"
echo "PCI_DEVICE=8086:1592,8086:1592" >> "$GITHUB_ENV"
elif [ "${{ matrix.nic }}" = "e830" ]; then
echo "PCI_DEVICE=8086:12d2" >> "$GITHUB_ENV"
echo "PCI_DEVICE=8086:12d2,8086:12d2" >> "$GITHUB_ENV"
fi
- name: Generate test framework config files
if: ${{ needs.smoke-check-for-changes.outputs.changed == 'true' }}
Expand All @@ -84,13 +84,22 @@ jobs:
--pci_device ${{ env.PCI_DEVICE }} \
--ip_address 127.0.0.1 \
--username ${{ secrets.RUNNER_USERNAME }} \
--key_path ${{ secrets.RUNNER_KEY_PATH }}
--key_path ${{ secrets.RUNNER_KEY_PATH }} \
--ebu_ip ${{ secrets.RUNNER_EBU_LIST_IP }} \
--ebu_user ${{ secrets.RUNNER_EBU_LIST_USER }} \
--ebu_password ${{ secrets.RUNNER_EBU_LIST_PASSWORD }}
- name: 'preparation: Kill MtlManager and pytest routines'
if: ${{ needs.smoke-check-for-changes.outputs.changed == 'true' }}
run: |
sudo killall -SIGINT pipenv || true
sudo killall -SIGINT pytest || true
sudo killall -SIGINT MtlManager || true
sudo killall -SIGINT phc2sys || true
- name: Export workflow tag for PCAP naming
if: ${{ needs.smoke-check-for-changes.outputs.changed == 'true' }}
shell: bash
run: |
echo "MTL_GITHUB_WORKFLOW=${{ github.workflow }}:${{ matrix.nic }}" >> "$GITHUB_ENV"
- name: 'preparation: Start MtlManager at background'
if: ${{ needs.smoke-check-for-changes.outputs.changed == 'true' }}
run: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ dkms.conf

# Ours
.*
!.github/
!.github/**
*.log
*.log_*
logs_*
Expand Down
25 changes: 21 additions & 4 deletions tests/validation/common/integrity/audio_integrity.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,34 @@ def check_integrity_file(self, out_url) -> bool:
)
src_chunk_sums = self.src_chunk_sums
out_chunk_sums = calculate_chunk_hashes(out_url, self.frame_size)

src_frames = len(src_chunk_sums)
out_frames = len(out_chunk_sums)

# In some pipelines the input is looped and transmitted multiple times.
# In that case the output may contain multiple repetitions of the source.
# We only validate the first `src_frames` frames and ignore any trailing frames.
if out_frames > src_frames:
self.logger.info(
f"Output contains {out_frames} frames; source contains {src_frames} frames. "
f"Ignoring {out_frames - src_frames} trailing frames (looped output)."
)

frames_to_check = min(src_frames, out_frames)
bad_frames = 0
for idx, chunk_sum in enumerate(out_chunk_sums):
if idx >= len(src_chunk_sums) or chunk_sum != src_chunk_sums[idx]:
for idx in range(frames_to_check):
if out_chunk_sums[idx] != src_chunk_sums[idx]:
self.logger.error(f"Bad audio frame at index {idx} in {out_url}")
bad_frames += 1
if bad_frames:
self.logger.error(
f"Received {bad_frames} bad frames out of {len(out_chunk_sums)} checked."
f"Received {bad_frames} bad frames out of {frames_to_check} checked."
)
return False
self.logger.info(f"All {len(out_chunk_sums)} frames in {out_url} are correct.")

self.logger.info(
f"All {frames_to_check} checked frames in {out_url} are correct."
)
return True


Expand Down
15 changes: 15 additions & 0 deletions tests/validation/configs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ ramdisk:
- **pcap_dir**: Directory to store capture files
- **capture_time**: Duration of packet capture in seconds
- **interface**: Network interface to capture from

### Packet Capture (netsniff-ng)

When `capture_cfg.enable: true`, the framework can capture traffic with `netsniff-ng`.

By default it captures on the **second PF of the same NIC** (same `pci_device`) if available, otherwise it uses `host.network_interfaces[0]`.

To capture on a specific NIC, set one of:

- `capture_cfg.sniff_interface`: OS interface name (e.g., `enp24s0f0`)
- `capture_cfg.sniff_interface_index`: index into `host.network_interfaces`
- `capture_cfg.sniff_pci_device`: vendor:device ID (e.g., `8086:12d2`)

Priority is `sniff_interface` > `sniff_interface_index` > `sniff_pci_device`.

- **ramdisk**: RAM disk configuration for high-performance testing
- **media.mountpoint**: Mount point for media RAM disk
- **media.size_gib**: Size of media RAM disk in GiB
Expand Down
3 changes: 3 additions & 0 deletions tests/validation/configs/examples/test_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ compliance: false
capture_cfg:
enable: false
pcap_dir: /mnt/ramdisk/pcap
# Optional: pick a different NIC for netsniff-ng capture than the one used by the test.
# Priority: sniff_interface > sniff_interface_index > sniff_pci_device.
# sniff_interface_index: 1
ebu_server:
ebu_ip: ebu_ip
user: user
Expand Down
4 changes: 4 additions & 0 deletions tests/validation/configs/examples/topology_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ hosts:
instantiate: true
role: sut
network_interfaces:
# Test NIC(s)
- pci_device: 8086:1592
interface_index: 0
# Optional: separate NIC for packet capture (netsniff-ng)
- pci_device: 8086:1592
interface_index: 1
connections:
- ip_address: 127.0.0.1
connection_type: SSHConnection
Expand Down
78 changes: 64 additions & 14 deletions tests/validation/configs/gen_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,44 @@
import yaml


def gen_test_config(session_id: int, build: str, mtl_path: str) -> str:
def gen_test_config(
session_id: int,
build: str,
mtl_path: str,
pci_device: str,
ebu_ip: str,
ebu_user: str,
ebu_password: str,
) -> str:
# Support comma-separated PCI devices for multiple interfaces.
# The capture sniff interface must be explicitly provided as the second device.
pci_devices = [dev.strip() for dev in pci_device.split(",") if dev.strip()]
if len(pci_devices) < 2:
raise ValueError(
"At least two PCI devices are required (e.g., '0000:4b:00.0,0000:4b:00.1'); "
"the second device is used as sniff_pci_device"
)

test_config = {
"session_id": session_id,
"build": build,
"mtl_path": mtl_path,
"media_path": "/mnt/media",
"ramdisk": {
"media": {"mountpoint": "/mnt/ramdisk/media", "size_gib": 32},
"pcap": {"mountpoint": "/mnt/ramdisk/pcap", "size_gib": 768},
"tmpfs_size_gib": 12,
"pcap_dir": "/mnt/ramdisk/pcap",
},
"compliance": True,
"capture_cfg": {
"enable": True,
"pcap_dir": "/mnt/ramdisk/pcap",
"sniff_pci_device": pci_devices[1],
},
"compliance": False,
"capture_cfg": {"enable": False, "pcap_dir": "/mnt/ramdisk/pcap"},
"ebu_server": {
"ebu_ip": "ebu_ip",
"user": "user",
"password": "password",
"ebu_ip": ebu_ip,
"user": ebu_user,
"password": ebu_password,
"proxy": False,
},
}
Expand Down Expand Up @@ -95,8 +117,26 @@ def main() -> None:
"--pci_device",
type=str,
required=True,
help="specify PCI ID of the NIC (comma-separated for multiple interfaces, e.g., \
'8086:1592')",
help="specify PCI BDF(s) of the NIC (comma-separated for multiple interfaces, e.g., \
'0000:4b:00.0,0000:4b:00.1'); the second device is used for capture sniffing",
)
parser.add_argument(
"--ebu_ip",
type=str,
required=True,
help="EBU LIST server IP/hostname (RUNNER_EBU_LIST_IP)",
)
parser.add_argument(
"--ebu_user",
type=str,
required=True,
help="EBU LIST username (RUNNER_EBU_LIST_USER)",
)
parser.add_argument(
"--ebu_password",
type=str,
required=True,
help="EBU LIST password (RUNNER_EBU_LIST_PASSWORD)",
)
parser.add_argument(
"--ip_address",
Expand Down Expand Up @@ -125,12 +165,22 @@ def main() -> None:
args = parser.parse_args()
if args.password == "None" and args.key_path == "None":
parser.error("one of the arguments --password --key_path is required")
with open("test_config.yaml", "w") as file:
file.write(
gen_test_config(
session_id=args.session_id, build=args.build, mtl_path=args.mtl_path
)

try:
test_config_yaml = gen_test_config(
session_id=args.session_id,
build=args.build,
mtl_path=args.mtl_path,
pci_device=args.pci_device,
ebu_ip=args.ebu_ip,
ebu_user=args.ebu_user,
ebu_password=args.ebu_password,
)
except ValueError as exc:
parser.error(str(exc))

with open("test_config.yaml", "w") as file:
file.write(test_config_yaml)
with open("topology_config.yaml", "w") as file:
file.write(
gen_topology_config(
Expand Down
Loading
Loading