144 lines
4.5 KiB
Python
144 lines
4.5 KiB
Python
# SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
import subprocess
|
|
import time
|
|
import os
|
|
import pytest
|
|
import signal
|
|
import pty
|
|
import select # Import select here or at top level
|
|
|
|
# Note: bin_path and interface fixtures are provided implicitly by conftest.py
|
|
|
|
def test_testj1939_usage(bin_path):
|
|
"""
|
|
Test usage/help output for testj1939.
|
|
|
|
Manual Reproduction:
|
|
1. Run: ./testj1939 -h
|
|
2. Expect: Output containing "Usage: testj1939".
|
|
"""
|
|
testj1939 = os.path.join(bin_path, "testj1939")
|
|
if not os.path.exists(testj1939):
|
|
pytest.skip("testj1939 binary not found")
|
|
|
|
result = subprocess.run(
|
|
[testj1939, "-h"],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True
|
|
)
|
|
|
|
assert "Usage: testj1939" in result.stderr or "Usage: testj1939" in result.stdout
|
|
|
|
def test_testj1939_send(bin_path, can_interface):
|
|
"""
|
|
Test sending functionality of testj1939.
|
|
|
|
Description:
|
|
1. Start j1939spy (as receiver) in promiscuous mode on the interface using PTY.
|
|
2. Run testj1939 to send 10 bytes of dummy data (-s 10) from Source 0x90 to Dest 0x80.
|
|
3. Verify that j1939spy captures the payload.
|
|
|
|
Manual Reproduction:
|
|
1. Receiver: ./j1939spy -P vcan0
|
|
2. Sender: ./testj1939 vcan0:90 vcan0:80 -s 10
|
|
3. Check j1939spy output for payload "01234567 89abcdef" (standard dummy pattern of testj1939).
|
|
"""
|
|
testj1939 = os.path.join(bin_path, "testj1939")
|
|
j1939spy = os.path.join(bin_path, "j1939spy")
|
|
|
|
for tool in [testj1939, j1939spy]:
|
|
if not os.path.exists(tool):
|
|
pytest.skip(f"{tool} not found")
|
|
|
|
# 1. Start Receiver (j1939spy)
|
|
# Command: ./j1939spy -P vcan0
|
|
cmd_spy = [j1939spy, "-P", can_interface]
|
|
print(f"DEBUG: Starting spy: {' '.join(cmd_spy)}")
|
|
|
|
# Use PTY to ensure line buffering for the spy tool
|
|
master_fd, slave_fd = pty.openpty()
|
|
|
|
spy_proc = subprocess.Popen(
|
|
cmd_spy,
|
|
stdout=slave_fd,
|
|
stderr=subprocess.PIPE,
|
|
text=True
|
|
)
|
|
os.close(slave_fd) # Close slave in parent
|
|
|
|
try:
|
|
# Allow spy to start
|
|
time.sleep(0.5)
|
|
if spy_proc.poll() is not None:
|
|
_, err = spy_proc.communicate()
|
|
pytest.fail(f"j1939spy failed to start. Stderr: {err}")
|
|
|
|
# 2. Run Sender (testj1939)
|
|
# Command: ./testj1939 vcan0:90 vcan0:80 -s 10
|
|
# Source SA: 0x90, Dest SA: 0x80, Size: 10 bytes
|
|
# testj1939 generates a dummy pattern. Based on logs: "01234567 89abcdef"
|
|
src_arg = f"{can_interface}:90"
|
|
dst_arg = f"{can_interface}:80"
|
|
|
|
cmd_send = [testj1939, src_arg, dst_arg, "-s", "10"]
|
|
print(f"DEBUG: Sending data: {' '.join(cmd_send)}")
|
|
|
|
send_proc = subprocess.run(
|
|
cmd_send,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True
|
|
)
|
|
|
|
if send_proc.returncode != 0:
|
|
print(f"DEBUG: testj1939 stderr: {send_proc.stderr}")
|
|
# Note: Depending on kernel and socket state, send might fail or succeed.
|
|
# Usually sendto() succeeds on CAN even without receiver.
|
|
|
|
# 3. Verify capture
|
|
# Read from PTY
|
|
time.sleep(1.0)
|
|
|
|
output = ""
|
|
found = False
|
|
|
|
# Expected Payload: Based on error log "01234567 89abcdef"
|
|
# We search for "01234567" which is the start of the payload
|
|
expected_pattern_hex = "01234567"
|
|
|
|
try:
|
|
start_read = time.time()
|
|
# Try reading for up to 3 seconds
|
|
while time.time() - start_read < 3.0:
|
|
# Use select to check if data is available to read
|
|
r, _, _ = select.select([master_fd], [], [], 0.5)
|
|
if master_fd in r:
|
|
chunk = os.read(master_fd, 4096).decode('utf-8', errors='replace')
|
|
if chunk:
|
|
output += chunk
|
|
if expected_pattern_hex in output:
|
|
found = True
|
|
break
|
|
# Check if process died
|
|
if spy_proc.poll() is not None:
|
|
break
|
|
except OSError:
|
|
pass
|
|
|
|
print(f"DEBUG: j1939spy output:\n{output}")
|
|
|
|
assert found, f"j1939spy did not capture the expected payload pattern '{expected_pattern_hex}'. Received output:\n{output}"
|
|
|
|
finally:
|
|
if spy_proc.poll() is None:
|
|
spy_proc.terminate()
|
|
try:
|
|
spy_proc.wait(timeout=1.0)
|
|
except subprocess.TimeoutExpired:
|
|
spy_proc.kill()
|
|
|
|
if master_fd:
|
|
os.close(master_fd)
|