summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore17
-rw-r--r--tests/README.md103
-rw-r--r--tests/accel-cmd/test_cmd_basic.py26
-rw-r--r--tests/accel-cmd/test_real_commands.py53
-rw-r--r--tests/accel-pppd/ipoe/conftest.py46
-rw-r--r--tests/accel-pppd/ipoe/dhcpv4/test_ipoe_shared_session_wo_auth.py70
-rw-r--r--tests/accel-pppd/ipoe/test_ipoe_driver.py8
-rw-r--r--tests/accel-pppd/pppoe/conftest.py42
-rw-r--r--tests/accel-pppd/pppoe/test_pppoe_disc.py42
-rw-r--r--tests/accel-pppd/pppoe/test_pppoe_pado_delay.py83
-rw-r--r--tests/accel-pppd/pppoe/test_pppoe_session_wo_auth.py90
-rw-r--r--tests/accel-pppd/pppoe/test_pppoe_vlan_mon.py49
-rw-r--r--tests/accel-pppd/test_basic.py83
-rw-r--r--tests/accel-pppd/test_vlan_mon_driver.py7
-rw-r--r--tests/common/__init__.py0
-rw-r--r--tests/common/accel_pppd_process.py86
-rw-r--r--tests/common/config.py15
-rw-r--r--tests/common/dhclient_process.py45
-rw-r--r--tests/common/netns.py29
-rw-r--r--tests/common/pppd_process.py43
-rw-r--r--tests/common/process.py7
-rw-r--r--tests/common/veth.py84
-rw-r--r--tests/common/vlan.py13
-rw-r--r--tests/conftest.py99
-rw-r--r--tests/gcovr.conf2
25 files changed, 1142 insertions, 0 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..0e7ab4f
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,17 @@
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Byte-compiled
+__pycache__/
+
+# pytest
+.pytest_cache
+
+# gcovr reports
+report/
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..d062ed4
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,103 @@
+# Requirements
+
+These tests are done for Ubuntu and Debian distros. Please use latest stable Debian or Ubuntu to run the tests.
+
+## Preparations
+
+Install pytest
+
+Using apt: `sudo apt install python3-pytest python3-pytest-dependency` or using pip: `sudo pip3 install pytest pytest-dependency`.
+
+pytest-dependency version must be >= 0.5 (with 'scope' support)
+
+---
+Note: tests will be run under sudo. If you prefer install python modules using pip, then do it under sudo as described above.
+
+---
+
+Install additional tools required for tests:
+```bash
+sudo apt install iproute2 ppp pppoe isc-dhcp-client
+```
+
+Then build accel-ppp in 'build' directory (as usual)
+
+Install accel-pppd (make install or use distro package). Do not run accel-pppd using systemd or other supervisors
+```bash
+mkdir build && cd build
+cmake -DBUILD_IPOE_DRIVER=TRUE -DBUILD_VLAN_MON_DRIVER=TRUE -DCMAKE_INSTALL_PREFIX=/usr -DKDIR=/usr/src/linux-headers-`uname -r` -DLUA=TRUE -DSHAPER=TRUE -DRADIUS=TRUE -DCPACK_TYPE=Ubuntu20 ..
+make
+sudo make install # or
+# cpack -G DEB && dpkg -i accel-ppp.deb
+```
+
+If you prefer make install, then it is required to insert kernel modules:
+```bash
+# form root dir
+sudo insmod build/drivers/vlan_mon/driver/vlan_mon.ko
+sudo insmod build/drivers/ipoe/driver/ipoe.ko
+```
+
+
+## Run tests (without coverage)
+
+```bash
+# from this dir (tests)
+sudo python3 -m pytest -Wall -v
+```
+
+To skip tests related to ipoe and vlan_mon kernel modules:
+```bash
+# from this dir (tests)
+sudo python3 -m pytest -Wall -v -m "not ipoe_driver and not vlan_mon_driver"
+```
+
+## Preparations (for coverage report)
+
+Perform preparation steps for running tests without coverage
+
+Install gcovr
+
+Using apt:
+```bash
+sudo apt install gcovr
+```
+
+Using pip
+```bash
+sudo pip3 install gcovr
+```
+
+```bash
+# from root dir
+rm -rf build && mkdir build && cd build
+cmake -DBUILD_IPOE_DRIVER=TRUE -DBUILD_VLAN_MON_DRIVER=TRUE -DCMAKE_INSTALL_PREFIX=/usr -DKDIR=/usr/src/linux-headers-`uname -r` -DLUA=TRUE -DSHAPER=TRUE -DRADIUS=TRUE -DCPACK_TYPE=Ubuntu20 -DCMAKE_C_FLAGS="--coverage -O0" ..
+make
+sudo make install # or
+# cpack -G DEB && dpkg -i accel-ppp.deb
+```
+
+Then insert kernel modules (ipoe.ko and vlan-mon.ko)
+
+## Run tests and generate coverage report
+
+```bash
+# from root dir (parent for this dir)
+sudo python3 -m pytest -Wall tests -v # execute tests to collect coverage data
+mkdir tests/report
+gcovr --config=tests/gcovr.conf # default report
+gcovr --config=tests/gcovr.conf --csv # csv report
+gcovr --config=tests/gcovr.conf --html --html-details --output=tests/report/accel-ppp.html # html reports (most useful)
+```
+
+(If `gcovr` command does not exist, use `python3 -m gcovr` instead)
+
+## Remove coverage data
+
+If you want to re-run tests 'from scratch', you may want to remove coverage data. To do this:
+
+```bash
+# from root dir (parent for this dir)
+sudo gcovr -d # build report and delete
+sudo gcovr -d # check that data is deleted (any coverage = 0%)
+``` \ No newline at end of file
diff --git a/tests/accel-cmd/test_cmd_basic.py b/tests/accel-cmd/test_cmd_basic.py
new file mode 100644
index 0000000..c6cdc7a
--- /dev/null
+++ b/tests/accel-cmd/test_cmd_basic.py
@@ -0,0 +1,26 @@
+import pytest
+from common import process
+
+
+def test_accel_cmd_version(accel_cmd):
+ (exit, out, err) = process.run([accel_cmd, "--version"])
+
+ # test that accel-cmd --version exits with code 0, prints
+ # nothing to stdout and prints to stdout
+ assert exit == 0 and err == "" and "accel-cmd " in out and len(out.split(" ")) == 2
+
+
+def test_accel_cmd_non_existent_host(accel_cmd):
+ (exit, out, err) = process.run([accel_cmd, "-Hnon-existent-host", "--verbose"])
+
+ # test that accel-cmd (tried to connecto to non-existent host) exits with code != 0,
+ # prints nothing to stdout and prints an error to stderr
+ assert exit != 0 and out == "" and err != ""
+
+
+def test_accel_cmd_mcast_host(accel_cmd):
+ (exit, out, err) = process.run([accel_cmd, "-H225.0.0.1"])
+
+ # test that accel-cmd (tried to connecto to mcast host) exits with code != 0,
+ # prints nothing to stdout and prints an error to stderr
+ assert exit != 0 and out == "" and err != ""
diff --git a/tests/accel-cmd/test_real_commands.py b/tests/accel-cmd/test_real_commands.py
new file mode 100644
index 0000000..1758b4e
--- /dev/null
+++ b/tests/accel-cmd/test_real_commands.py
@@ -0,0 +1,53 @@
+import pytest
+from common import process
+
+
+@pytest.fixture()
+def accel_pppd_config():
+ return """
+ [modules]
+
+ [log]
+ log-debug=/dev/stdout
+ level=5
+
+ [cli]
+ tcp=127.0.0.1:2001
+ """
+
+
+# test accel-cmd command with started accel-pppd
+def test_accel_cmd_commands(accel_pppd_instance, accel_cmd):
+
+ # test that accel-pppd started successfully
+ assert accel_pppd_instance
+
+ (exit_sh_stat, out_sh_stat, err_sh_stat) = process.run([accel_cmd, "show stat"])
+
+ # test that 'show stat' has no errors and contains 'uptime'
+ assert (
+ exit_sh_stat == 0
+ and len(out_sh_stat) > 0
+ and err_sh_stat == ""
+ and "uptime" in out_sh_stat
+ )
+
+ (exit_sh_ses, out_sh_ses, err_sh_ses) = process.run(
+ [accel_cmd, "show sessions sid,uptime"]
+ )
+ # test that 'show sessions' has no errors and contains 'sid'
+ assert (
+ exit_sh_ses == 0
+ and len(out_sh_ses) > 0
+ and err_sh_ses == ""
+ and "sid" in out_sh_ses
+ )
+
+ (exit_help, out_help, err_help) = process.run([accel_cmd, "help"])
+ # test that 'help' has no errors and contains 'show stat'
+ assert (
+ exit_help == 0
+ and len(out_help) > 0
+ and err_help == ""
+ and "show stat" in out_help
+ )
diff --git a/tests/accel-pppd/ipoe/conftest.py b/tests/accel-pppd/ipoe/conftest.py
new file mode 100644
index 0000000..3db8dd6
--- /dev/null
+++ b/tests/accel-pppd/ipoe/conftest.py
@@ -0,0 +1,46 @@
+import pytest
+from common import dhclient_process
+import tempfile, os
+
+# dhclient executable file name
+@pytest.fixture()
+def dhclient(pytestconfig):
+ return pytestconfig.getoption("dhclient")
+
+
+# pppd configuration as command line args (might be redefined by specific test)
+# "-d" (do not daemonize) must be a part of the args
+@pytest.fixture()
+def dhclient_args():
+ # test setup:
+ #lease_file = tempfile.NamedTemporaryFile(delete=True)
+ #lease_file_name = lease_file.name
+ #lease_file.close() # just create, close and delete
+
+ # test execution:
+ yield ["-d", "-4", "--no-pid", "-lf", "/dev/null"]
+
+ # test teardown:
+ #os.unlink(lease_file_name)
+
+
+# setup and teardown for tests that required running dhclient (after accel-pppd)
+@pytest.fixture()
+def dhclient_instance(accel_pppd_instance, veth_pair_netns, dhclient, dhclient_args):
+ # test setup:
+ print("dhclient_instance: accel_pppd_instance = " + str(accel_pppd_instance))
+ is_started, dhclient_thread, dhclient_control = dhclient_process.start(
+ veth_pair_netns["netns"],
+ dhclient,
+ dhclient_args,
+ )
+
+ # test execution:
+ yield {
+ "is_started": is_started,
+ "dhclient_thread": dhclient_thread,
+ "dhclient_control": dhclient_control,
+ }
+
+ # test teardown:
+ dhclient_process.end(dhclient_thread, dhclient_control)
diff --git a/tests/accel-pppd/ipoe/dhcpv4/test_ipoe_shared_session_wo_auth.py b/tests/accel-pppd/ipoe/dhcpv4/test_ipoe_shared_session_wo_auth.py
new file mode 100644
index 0000000..104e4e9
--- /dev/null
+++ b/tests/accel-pppd/ipoe/dhcpv4/test_ipoe_shared_session_wo_auth.py
@@ -0,0 +1,70 @@
+import pytest
+from common import process
+import time
+
+
+@pytest.fixture()
+def accel_pppd_config(veth_pair_netns):
+ print("accel_pppd_config veth_pair_netns: " + str(veth_pair_netns))
+ return (
+ """
+ [modules]
+ pppoe
+ ipoe
+ ippool
+
+ [ip-pool]
+ gw-ip-address=192.0.2.1
+ 192.0.2.2-255
+
+ [cli]
+ tcp=127.0.0.1:2001
+
+ [log]
+ log-debug=/dev/stdout
+ level=5
+
+ [ipoe]
+ noauth=1
+ shared=1
+ gw-ip-address=192.0.2.1/24
+ interface="""
+ + veth_pair_netns["veth_a"]
+ )
+
+
+# test dhcpv4 shared session without auth check
+@pytest.mark.dependency(depends=["ipoe_driver_loaded"], scope = 'session')
+@pytest.mark.ipoe_driver
+def test_ipoe_shared_session_wo_auth(dhclient_instance, accel_cmd, veth_pair_netns):
+
+ # test that dhclient (with accel-pppd) started successfully
+ assert dhclient_instance["is_started"]
+
+ # wait until session is started
+ max_wait_time = 10.0
+ sleep_time = 0.0
+ is_started = False # is session started
+ while sleep_time < max_wait_time:
+ (exit, out, err) = process.run(
+ [
+ accel_cmd,
+ "show sessions called-sid,ip,state",
+ ]
+ )
+ assert exit == 0 # accel-cmd fails
+ # print(out)
+ if veth_pair_netns["veth_a"] in out and "192.0.2." in out and "active" in out:
+ # session is found
+ print(
+ "test_pppoe_session_wo_auth: session found in (sec): " + str(sleep_time)
+ )
+ is_started = True
+ break
+ time.sleep(0.1)
+ sleep_time += 0.1
+
+ print("test_ipoe_shared_session_wo_auth: last accel-cmd out: " + out)
+
+ # test that session is started
+ assert is_started == True
diff --git a/tests/accel-pppd/ipoe/test_ipoe_driver.py b/tests/accel-pppd/ipoe/test_ipoe_driver.py
new file mode 100644
index 0000000..d21e9ba
--- /dev/null
+++ b/tests/accel-pppd/ipoe/test_ipoe_driver.py
@@ -0,0 +1,8 @@
+import pytest
+import os
+
+# test that ipoe kernel module is loaded
+@pytest.mark.dependency(name = 'ipoe_driver_loaded', scope = 'session')
+@pytest.mark.ipoe_driver
+def test_ipoe_kernel_module_loaded():
+ assert os.path.isdir("/sys/module/ipoe") \ No newline at end of file
diff --git a/tests/accel-pppd/pppoe/conftest.py b/tests/accel-pppd/pppoe/conftest.py
new file mode 100644
index 0000000..8ebaaed
--- /dev/null
+++ b/tests/accel-pppd/pppoe/conftest.py
@@ -0,0 +1,42 @@
+import pytest
+from common import pppd_process
+
+# pppd executable file name
+@pytest.fixture()
+def pppd(pytestconfig):
+ return pytestconfig.getoption("pppd")
+
+
+# pppd configuration as string (should be redefined by specific test)
+# all configs should contain "nodetach" option
+@pytest.fixture()
+def pppd_config():
+ return ""
+
+
+# pppd configuration as command line args
+@pytest.fixture()
+def pppd_args(pppd_config):
+ return pppd_config.split()
+
+
+# setup and teardown for tests that required running pppd (after accel-pppd)
+@pytest.fixture()
+def pppd_instance(accel_pppd_instance, veth_pair_netns, pppd, pppd_args):
+ # test setup:
+ print("pppd_instance: accel_pppd_instance = " + str(accel_pppd_instance))
+ is_started, pppd_thread, pppd_control = pppd_process.start(
+ veth_pair_netns["netns"],
+ pppd,
+ pppd_args,
+ )
+
+ # test execution:
+ yield {
+ "is_started": is_started,
+ "pppd_thread": pppd_thread,
+ "pppd_control": pppd_control,
+ }
+
+ # test teardown:
+ pppd_process.end(pppd_thread, pppd_control)
diff --git a/tests/accel-pppd/pppoe/test_pppoe_disc.py b/tests/accel-pppd/pppoe/test_pppoe_disc.py
new file mode 100644
index 0000000..eb069c4
--- /dev/null
+++ b/tests/accel-pppd/pppoe/test_pppoe_disc.py
@@ -0,0 +1,42 @@
+import pytest
+from common import netns
+
+
+@pytest.fixture()
+def accel_pppd_config(veth_pair_netns):
+ print(veth_pair_netns)
+ return (
+ """
+ [modules]
+ pppoe
+
+ [log]
+ log-debug=/dev/stdout
+ level=5
+
+ [cli]
+ tcp=127.0.0.1:2001
+
+ [pppoe]
+ ac-name=test-accel
+ interface="""
+ + veth_pair_netns["veth_a"]
+ )
+
+
+# test pppoe discovery
+def test_pppoe_discovery(accel_pppd_instance, veth_pair_netns):
+
+ # test that accel-pppd started successfully
+ assert accel_pppd_instance
+
+ (exit_sh_stat, out_sh_stat, err_sh_stat) = netns.exec(
+ veth_pair_netns["netns"], ["pppoe-discovery", "-I", veth_pair_netns["veth_b"]]
+ )
+
+ # test that ac-name=test-accel is in pppoe-discovery reply (PADO)
+ assert (
+ exit_sh_stat == 0
+ and err_sh_stat == ""
+ and "test-accel" in out_sh_stat
+ )
diff --git a/tests/accel-pppd/pppoe/test_pppoe_pado_delay.py b/tests/accel-pppd/pppoe/test_pppoe_pado_delay.py
new file mode 100644
index 0000000..96c73bf
--- /dev/null
+++ b/tests/accel-pppd/pppoe/test_pppoe_pado_delay.py
@@ -0,0 +1,83 @@
+import pytest
+from common import netns, process
+import time
+
+# This test module requires pppoe-discovery with -a and -t options
+# Ubuntu 20.04 has not this option, Ubuntu 22.04 is ok
+
+# Check that pppoe-discover supports -a and -t (to disable some tests required these features)
+def support_pppoe_discovery_a_t():
+ try:
+ (_, out, err) = process.run(["pppoe-discovery", "-h"])
+ except: # can't run pppoe-discovery
+ return False
+
+ if "-t " in out + err and "-a " in out + err: # found -t and -a options
+ return True
+ else:
+ return False
+
+
+# skip tests in this module if pppoe-discovery doesn't support '-a' and '-t' options
+pytestmark = pytest.mark.skipif(
+ not support_pppoe_discovery_a_t(), reason="bad pppoe-discovery"
+)
+
+
+@pytest.fixture()
+def accel_pppd_config(veth_pair_netns):
+ print(veth_pair_netns)
+ return (
+ """
+ [modules]
+ pppoe
+
+ [log]
+ log-debug=/dev/stdout
+ level=5
+
+ [cli]
+ tcp=127.0.0.1:2001
+
+ [pppoe]
+ ac-name=test-accel
+ pado-delay=1500
+ interface="""
+ + veth_pair_netns["veth_a"]
+ )
+
+
+# test pado delay. accel-pppd is configured for 1.5s delay
+# first step: test that pppoe-discovery fails if wait timeout=1<1.5
+# second step: test that pppoe-discovery gets pado if wait timeout=2>1.5
+def test_pppoe_pado_delay(accel_pppd_instance, veth_pair_netns):
+
+ # test that accel-pppd started successfully
+ assert accel_pppd_instance
+
+ # send two times with wait timeout = 1
+ (exit_sh_stat, out_sh_stat, err_sh_stat) = netns.exec(
+ veth_pair_netns["netns"],
+ ["pppoe-discovery", "-a1", "-t1", "-I", veth_pair_netns["veth_b"]],
+ )
+ time.sleep(1) # sleep for one second (because accel-pppd replies in this timeslot)
+ (exit_sh_stat2, out_sh_stat2, err_sh_stat2) = netns.exec(
+ veth_pair_netns["netns"],
+ ["pppoe-discovery", "-a1", "-t1", "-I", veth_pair_netns["veth_b"]],
+ )
+ time.sleep(1) # sleep for one second (because accel-pppd replies in this timeslot)
+
+ # print(out_sh_stat + err_sh_stat)
+ # print(out_sh_stat2 + err_sh_stat2)
+
+ # test that pppoe-discovery (wait timeout 1s) fails (as expected) (two times)
+ assert exit_sh_stat != 0 and "test-accel" not in out_sh_stat
+ assert exit_sh_stat2 != 0 and "test-accel" not in out_sh_stat2
+
+ (exit_sh_stat3, out_sh_stat3, err_sh_stat3) = netns.exec(
+ veth_pair_netns["netns"],
+ ["pppoe-discovery", "-a1", "-t2", "-I", veth_pair_netns["veth_b"]],
+ )
+
+ # test that pppoe-discovery (wait timeout 2s) gets pado
+ assert exit_sh_stat3 == 0 and "test-accel" in out_sh_stat3
diff --git a/tests/accel-pppd/pppoe/test_pppoe_session_wo_auth.py b/tests/accel-pppd/pppoe/test_pppoe_session_wo_auth.py
new file mode 100644
index 0000000..0c8aa2c
--- /dev/null
+++ b/tests/accel-pppd/pppoe/test_pppoe_session_wo_auth.py
@@ -0,0 +1,90 @@
+import pytest
+from common import process
+import time
+
+
+@pytest.fixture()
+def accel_pppd_config(veth_pair_netns):
+ print("accel_pppd_config veth_pair_netns: " + str(veth_pair_netns))
+ return (
+ """
+ [modules]
+ pppoe
+ auth_pap
+ ippool
+
+ [log]
+ log-debug=/dev/stdout
+ level=5
+
+ [auth]
+ any-login=1
+
+ [ip-pool]
+ gw-ip-address=192.0.2.1
+ 192.0.2.2-255
+
+ [cli]
+ tcp=127.0.0.1:2001
+
+ [pppoe]
+ interface="""
+ + veth_pair_netns["veth_a"]
+ )
+
+
+@pytest.fixture()
+def pppd_config(veth_pair_netns):
+ print("pppd_config veth_pair_netns: " + str(veth_pair_netns))
+ return (
+ """
+ nodetach
+ noipdefault
+ defaultroute
+ connect /bin/true
+ noauth
+ persist
+ mtu 1492
+ noaccomp
+ default-asyncmap
+ plugin rp-pppoe.so
+ user loginAB
+ password pass123
+ nic-"""
+ + veth_pair_netns["veth_b"]
+ )
+
+
+# test pppoe session without auth check
+def test_pppoe_session_wo_auth(pppd_instance, accel_cmd):
+
+ # test that pppd (with accel-pppd) started successfully
+ assert pppd_instance["is_started"]
+
+ # wait until session is started
+ max_wait_time = 10.0
+ sleep_time = 0.0
+ is_started = False # is session started
+ while sleep_time < max_wait_time:
+ (exit, out, err) = process.run(
+ [
+ accel_cmd,
+ "show sessions match username loginAB username,ip,state",
+ ]
+ )
+ assert exit == 0 # accel-cmd fails
+ # print(out)
+ if "loginAB" in out and "192.0.2." in out and "active" in out:
+ # session is found
+ print(
+ "test_pppoe_session_wo_auth: session found in (sec): " + str(sleep_time)
+ )
+ is_started = True
+ break
+ time.sleep(0.1)
+ sleep_time += 0.1
+
+ print("test_pppoe_session_wo_auth: last accel-cmd out: " + out)
+
+ # test that session is started
+ assert is_started == True
diff --git a/tests/accel-pppd/pppoe/test_pppoe_vlan_mon.py b/tests/accel-pppd/pppoe/test_pppoe_vlan_mon.py
new file mode 100644
index 0000000..670abc3
--- /dev/null
+++ b/tests/accel-pppd/pppoe/test_pppoe_vlan_mon.py
@@ -0,0 +1,49 @@
+import pytest
+from common import netns
+
+
+# create vlan 15 only in netns (invisble to accel-pppd)
+@pytest.fixture()
+def veth_pair_vlans_config():
+ return {"vlans_a": [], "vlans_b": [15]}
+
+
+@pytest.fixture()
+def accel_pppd_config(veth_pair_netns):
+ print(veth_pair_netns)
+ return """
+ [modules]
+ pppoe
+
+ [log]
+ log-debug=/dev/stdout
+ level=5
+
+ [cli]
+ tcp=127.0.0.1:2001
+
+ [pppoe]
+ ac-name=test-accel
+ vlan-mon=%s,10-20
+ interface=re:%s.\\d+
+ """ % (
+ veth_pair_netns["veth_a"],
+ veth_pair_netns["veth_a"],
+ )
+
+
+# test pppoe discovery in vlan created by vlan_mon
+@pytest.mark.dependency(depends=["vlan_mon_driver_loaded"], scope="session")
+@pytest.mark.vlan_mon_driver
+def test_pppoe_vlan_mon(accel_pppd_instance, veth_pair_netns):
+
+ # test that accel-pppd started successfully
+ assert accel_pppd_instance
+
+ (exit_sh_stat, out_sh_stat, err_sh_stat) = netns.exec(
+ veth_pair_netns["netns"],
+ ["pppoe-discovery", "-I", veth_pair_netns["veth_b"] + ".15"],
+ )
+
+ # test that ac-name=test-accel is in pppoe-discovery reply (PADO)
+ assert exit_sh_stat == 0 and err_sh_stat == "" and "test-accel" in out_sh_stat
diff --git a/tests/accel-pppd/test_basic.py b/tests/accel-pppd/test_basic.py
new file mode 100644
index 0000000..2b2c6f7
--- /dev/null
+++ b/tests/accel-pppd/test_basic.py
@@ -0,0 +1,83 @@
+import pytest
+from common import process
+
+
+def test_accel_pppd_version(accel_pppd):
+ (exit, out, err) = process.run([accel_pppd, "--version"])
+
+ # test that accel-pppd --version exits with code 0, prints
+ # nothing to stdout and prints to stdout
+ assert exit == 0 and err == "" and "accel-ppp " in out and len(out.split(" ")) == 2
+
+
+@pytest.fixture()
+def accel_pppd_config():
+ return """
+ [modules]
+ log_file
+ log_syslog
+ log_tcp
+ #log_pgsql
+
+ pptp
+ l2tp
+ sstp
+ pppoe
+ ipoe
+
+ auth_mschap_v2
+ auth_mschap_v1
+ auth_chap_md5
+ auth_pap
+
+ radius
+ chap-secrets
+
+ ippool
+
+ pppd_compat
+ shaper
+ #net-snmp
+ logwtmp
+ connlimit
+
+ ipv6_nd
+ ipv6_dhcp
+ ipv6pool
+
+ [core]
+ log-error=/dev/stderr
+
+ [log]
+ log-debug=/dev/stdout
+ log-file=/dev/stdout
+ log-emerg=/dev/stderr
+ level=5
+
+ [cli]
+ tcp=127.0.0.1:2001
+
+ [pppoe]
+
+ [client-ip-range]
+ 10.0.0.0/8
+
+ [radius]
+ """
+
+
+# load all modules and check that accel-pppd replies to 'show stat' command
+def test_load_all_modules(accel_pppd_instance, accel_cmd):
+
+ # test that accel-pppd started successfully
+ assert accel_pppd_instance
+
+ (exit_sh_stat, out_sh_stat, err_sh_stat) = process.run([accel_cmd, "show stat"])
+
+ # test that 'show stat' has no errors and contains 'uptime'
+ assert (
+ exit_sh_stat == 0
+ and len(out_sh_stat) > 1
+ and err_sh_stat == ""
+ and "uptime" in out_sh_stat
+ )
diff --git a/tests/accel-pppd/test_vlan_mon_driver.py b/tests/accel-pppd/test_vlan_mon_driver.py
new file mode 100644
index 0000000..3e937f8
--- /dev/null
+++ b/tests/accel-pppd/test_vlan_mon_driver.py
@@ -0,0 +1,7 @@
+import pytest
+import os
+
+# test that vlan_mon kernel module is loaded
+@pytest.mark.dependency(name = 'vlan_mon_driver_loaded', scope = 'session')
+def test_vlan_mon_kernel_module_loaded():
+ assert os.path.isdir("/sys/module/vlan_mon") \ No newline at end of file
diff --git a/tests/common/__init__.py b/tests/common/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/common/__init__.py
diff --git a/tests/common/accel_pppd_process.py b/tests/common/accel_pppd_process.py
new file mode 100644
index 0000000..c2ee451
--- /dev/null
+++ b/tests/common/accel_pppd_process.py
@@ -0,0 +1,86 @@
+from subprocess import Popen, PIPE
+from common import process
+from threading import Thread
+import time
+
+
+def accel_pppd_thread_func(accel_pppd_control):
+ process = accel_pppd_control["process"]
+ print("accel_pppd_thread_func: before communicate")
+ (out, err) = process.communicate()
+ print(
+ "accel_pppd_thread_func: after communicate out=" + str(out) + " err=" + str(err)
+ )
+ process.wait()
+ print("accel_pppd_thread_func: after wait")
+
+
+def start(accel_pppd, args, accel_cmd, max_wait_time):
+ print("accel_pppd_start: begin")
+ accel_pppd_process = Popen([accel_pppd] + args, stdout=PIPE, stderr=PIPE)
+ accel_pppd_control = {"process": accel_pppd_process}
+ accel_pppd_thread = Thread(
+ target=accel_pppd_thread_func,
+ args=[accel_pppd_control],
+ )
+ accel_pppd_thread.start()
+
+ # wait until accel-pppd replies to 'show version'
+ # accel-pppd needs some time to be accessible
+ sleep_time = 0.0
+ is_started = False
+ while sleep_time < max_wait_time:
+ if accel_pppd_process.poll() is not None: # process is terminated
+ print(
+ "accel_pppd_start: terminated during 'show version' polling in (sec): "
+ + str(sleep_time)
+ )
+ is_started = False
+ break
+ (exit, out, err) = process.run([accel_cmd, "show version"])
+ if exit != 0: # does not reply
+ time.sleep(0.1)
+ sleep_time += 0.1
+ else: # replied
+ print("accel_pppd_start: 'show version' replied")
+ is_started = True
+ break
+
+ return (is_started, accel_pppd_thread, accel_pppd_control)
+
+
+def end(accel_pppd_thread, accel_pppd_control, accel_cmd, max_wait_time):
+ print("accel_pppd_end: begin")
+ if accel_pppd_control["process"].poll() is not None: # terminated
+ print("accel_pppd_end: already terminated. nothing to do")
+ accel_pppd_thread.join()
+ return
+
+ process.run(
+ [accel_cmd, "shutdown hard"]
+ ) # send shutdown hard command (in coverage mode it helps saving coverage data)
+ print("accel_pppd_end: after shutdown hard")
+
+ # wait until accel-pppd is finished
+ sleep_time = 0.0
+ is_finished = False
+ while sleep_time < max_wait_time:
+ if accel_pppd_control["process"].poll() is None: # not terminated yet
+ time.sleep(0.01)
+ sleep_time += 0.01
+ # print("accel_pppd_end: sleep 0.01")
+ else:
+ is_finished = True
+ print(
+ "accel_pppd_end: finished via shutdown hard in (sec): "
+ + str(sleep_time)
+ )
+ break
+
+ # accel-pppd is still alive. kill it
+ if not is_finished:
+ print("accel_pppd_end: kill process: " + str(accel_pppd_control["process"]))
+ accel_pppd_control["process"].kill() # kill -9 if 'shutdown hard' didn't help
+
+ accel_pppd_thread.join() # wait until thread is finished
+ print("accel_pppd_end: end")
diff --git a/tests/common/config.py b/tests/common/config.py
new file mode 100644
index 0000000..94ddcd8
--- /dev/null
+++ b/tests/common/config.py
@@ -0,0 +1,15 @@
+import tempfile
+import os
+
+
+def make_tmp(content):
+ f = tempfile.NamedTemporaryFile(delete=False)
+ print("make_tmp filename: " + f.name)
+ f.write(bytes(content, "utf-8"))
+ f.close()
+ return f.name
+
+
+def delete_tmp(filename):
+ print("delete_tmp filename: " + filename)
+ os.unlink(filename)
diff --git a/tests/common/dhclient_process.py b/tests/common/dhclient_process.py
new file mode 100644
index 0000000..aaea486
--- /dev/null
+++ b/tests/common/dhclient_process.py
@@ -0,0 +1,45 @@
+from subprocess import Popen, PIPE
+from threading import Thread
+
+
+def dhclient_thread_func(dhclient_control):
+ process = dhclient_control["process"]
+ print("dhclient_thread_func: before communicate")
+ (out, err) = process.communicate()
+ print(
+ "dhclient_thread_func: after communicate out=" + str(out) + " err=" + str(err)
+ )
+ process.wait()
+ print("dhclient_thread_func: after wait")
+
+
+def start(netns, dhclient, args):
+ print("dhclient_start: begin")
+ print("dhclient_start: args=" + str(args))
+ dhclient_process = Popen(
+ ["ip", "netns", "exec", netns] + [dhclient] + args, stdout=PIPE, stderr=PIPE
+ )
+ print("dhclient_start: dhclient_process=" + str(dhclient_process))
+ dhclient_control = {"process": dhclient_process}
+ dhclient_thread = Thread(
+ target=dhclient_thread_func,
+ args=[dhclient_control],
+ )
+ dhclient_thread.start()
+
+ is_started = True if dhclient_process.poll() is None else False
+
+ return (is_started, dhclient_thread, dhclient_control)
+
+
+def end(dhclient_thread, dhclient_control):
+ print("dhclient_end: begin")
+ if dhclient_control["process"].poll() is not None: # already terminated
+ print("dhclient_end: already terminated. nothing to do")
+ dhclient_thread.join()
+ return
+
+ print("dhclient_end: kill process: " + str(dhclient_control["process"]))
+ dhclient_control["process"].kill() # kill -9
+ dhclient_thread.join() # wait until thread is finished
+ print("dhclient_end: end")
diff --git a/tests/common/netns.py b/tests/common/netns.py
new file mode 100644
index 0000000..9b0d729
--- /dev/null
+++ b/tests/common/netns.py
@@ -0,0 +1,29 @@
+from common import process
+
+# creates netns and returns netns name. if ok return 0
+def create(netns_name):
+ netns, out, err = process.run(["ip", "netns", "add", netns_name])
+ print("netns.create: exit=%d out=%s err=%s" % (netns, out, err))
+
+ return netns
+
+
+# deletes netns. if ok return 0
+def delete(netns_name):
+ netns, out, err = process.run(["ip", "netns", "delete", netns_name])
+ print("netns.delete: exit=%d out=%s err=%s" % (netns, out, err))
+
+ return netns
+
+
+# execute command in netns using process.run
+# if netns_name is None, then execute in global rt
+def exec(netns_name, command):
+ if netns_name is None:
+ exit, out, err = process.run(command)
+ else:
+ exit, out, err = process.run(["ip", "netns", "exec", netns_name] + command)
+
+ print("netns.exec: netns=%s command=%s :: exit=%d out=%s err=%s" % (netns_name, str(command), exit, out, err))
+
+ return (exit, out, err)
diff --git a/tests/common/pppd_process.py b/tests/common/pppd_process.py
new file mode 100644
index 0000000..4584440
--- /dev/null
+++ b/tests/common/pppd_process.py
@@ -0,0 +1,43 @@
+from subprocess import Popen, PIPE
+from threading import Thread
+
+
+def pppd_thread_func(pppd_control):
+ process = pppd_control["process"]
+ print("pppd_thread_func: before communicate")
+ (out, err) = process.communicate()
+ print("pppd_thread_func: after communicate out=" + str(out) + " err=" + str(err))
+ process.wait()
+ print("pppd_thread_func: after wait")
+
+
+def start(netns, pppd, args):
+ print("pppd_start: begin")
+ print("pppd_start: args=" + str(args))
+ pppd_process = Popen(
+ ["ip", "netns", "exec", netns] + [pppd] + args, stdout=PIPE, stderr=PIPE
+ )
+ print("pppd_start: pppd_process=" + str(pppd_process))
+ pppd_control = {"process": pppd_process}
+ pppd_thread = Thread(
+ target=pppd_thread_func,
+ args=[pppd_control],
+ )
+ pppd_thread.start()
+
+ is_started = True if pppd_process.poll() is None else False
+
+ return (is_started, pppd_thread, pppd_control)
+
+
+def end(pppd_thread, pppd_control):
+ print("pppd_end: begin")
+ if pppd_control["process"].poll() is not None: # already terminated
+ print("pppd_end: already terminated. nothing to do")
+ pppd_thread.join()
+ return
+
+ print("pppd_end: kill process: " + str(pppd_control["process"]))
+ pppd_control["process"].kill() # kill -9
+ pppd_thread.join() # wait until thread is finished
+ print("pppd_end: end")
diff --git a/tests/common/process.py b/tests/common/process.py
new file mode 100644
index 0000000..e0c6136
--- /dev/null
+++ b/tests/common/process.py
@@ -0,0 +1,7 @@
+from subprocess import Popen, PIPE
+
+def run(command):
+ process = Popen(command, stdout=PIPE, stderr=PIPE)
+ (out, err) = process.communicate()
+ exit_code = process.wait()
+ return (exit_code, out.decode("utf-8"), err.decode("utf-8")) \ No newline at end of file
diff --git a/tests/common/veth.py b/tests/common/veth.py
new file mode 100644
index 0000000..a31d245
--- /dev/null
+++ b/tests/common/veth.py
@@ -0,0 +1,84 @@
+from common import process, netns, vlan
+import time
+import math
+
+# creates veth pair. if ok returns 0
+def create_pair(name_a, name_b):
+ veth, out, err = process.run(
+ ["ip", "link", "add", name_a, "type", "veth", "peer", "name", name_b]
+ )
+ print("veth.create: exit=%d out=%s err=%s" % (veth, out, err))
+
+ return veth
+
+
+# deletes veth pair. if ok returns 0
+def delete_veth(name_a):
+ veth, out, err = process.run(["ip", "link", "delete", name_a])
+ print("veth.delete: exit=%d out=%s err=%s" % (veth, out, err))
+
+ return veth
+
+
+# put veth to netns. if ok returns 0
+def assign_netns(veth, netns):
+ veth, out, err = process.run(["ip", "link", "set", veth, "netns", netns])
+ print("veth.assign_netns: exit=%d out=%s err=%s" % (veth, out, err))
+
+ return veth
+
+
+# up interface. if netns is None, then up in global rt. if ok returns 0
+def up_interface(iface, netns_name):
+ command = ["ip", "link", "set", iface, "up"]
+ exit, out, err = netns.exec(netns_name, command)
+ print(
+ "veth.up_interface: iface=%s netns=%s exit=%d out=%s err=%s"
+ % (iface, str(netns_name), exit, out, err)
+ )
+
+ return exit
+
+
+# creates netns, creates veth pair and place second link to netns
+# creates vlans over veth interfaces according to veth_pair_vlans_config
+# return dict with 'netns', 'veth_a', 'veth_b'
+def create_veth_pair_netns(veth_pair_vlans_config):
+
+ name = str(math.floor(time.time() * 1000) % 1000000)
+ netns_name = "N" + name
+ netns_status = netns.create(netns_name)
+ print("create_veth_pair_netns: netns_status=%d" % netns_status)
+
+ veth_a = "A" + name
+ veth_b = "B" + name
+ pair_status = create_pair(veth_a, veth_b)
+ print("create_veth_pair_netns: pair_status=%d" % pair_status)
+
+ up_interface(veth_a, None)
+
+ assign_status = assign_netns(veth_b, netns_name)
+ print("create_veth_pair_netns: assign_status=%d" % assign_status)
+
+ up_interface(veth_b, netns_name)
+
+ vlans_a = veth_pair_vlans_config["vlans_a"]
+ for vlan_num in vlans_a:
+ vlan.create(veth_a, vlan_num, None)
+ up_interface(veth_a + "." + str(vlan_num), None)
+
+ vlans_b = veth_pair_vlans_config["vlans_b"]
+ for vlan_num in vlans_b:
+ vlan.create(veth_b, vlan_num, netns_name)
+ up_interface(veth_b + "." + str(vlan_num), netns_name)
+
+ return {"netns": netns_name, "veth_a": veth_a, "veth_b": veth_b}
+
+
+# deletes veth pair and netns created by create_veth_pair_netns
+def delete_veth_pair_netns(veth_pair_netns):
+ veth_status = delete_veth(veth_pair_netns["veth_a"])
+ print("delete_veth_pair_netns: veth_status=%d" % veth_status)
+
+ netns_status = netns.delete(veth_pair_netns["netns"])
+ print("delete_veth_pair_netns: netns_status=%d" % netns_status)
diff --git a/tests/common/vlan.py b/tests/common/vlan.py
new file mode 100644
index 0000000..459efb1
--- /dev/null
+++ b/tests/common/vlan.py
@@ -0,0 +1,13 @@
+from common import netns
+
+# up interface. if netns is None, then up in global rt. if ok returns 0
+def create(parent_if, vlan, netns_name):
+ command = (
+ "ip link add link %s name %s.%d type vlan id %d"
+ % (parent_if, parent_if, vlan, vlan)
+ ).split()
+
+ vlan, out, err = netns.exec(netns_name, command)
+ print("vlan.create: exit=%d out=%s err=%s" % (vlan, out, err))
+
+ return vlan
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..d373340
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,99 @@
+import pytest
+from common import accel_pppd_process, config, veth
+
+
+def pytest_addoption(parser):
+ parser.addoption("--accel_cmd", action="store", default="accel-cmd")
+ parser.addoption("--accel_pppd", action="store", default="accel-pppd")
+ parser.addoption("--pppd", action="store", default="pppd") # pppd client
+ parser.addoption(
+ "--dhclient", action="store", default="dhclient"
+ ) # isc-dhcp-client
+ parser.addoption(
+ "--accel_pppd_max_wait_time", action="store", default=5.0
+ ) # start timeout
+ parser.addoption(
+ "--accel_pppd_max_finish_time", action="store", default=10.0
+ ) # fininsh timeout (before kill)
+
+
+def pytest_configure(config):
+ config.addinivalue_line(
+ "markers",
+ "ipoe_driver: marks tests as related to ipoe kernel module (deselect with '-m \"not ipoe_driver\"')",
+ )
+ config.addinivalue_line(
+ "markers",
+ "vlan_mon_driver: marks tests as related to ipoe kernel module (deselect with '-m \"not vlan_mon_driver\"')",
+ )
+
+
+# accel-pppd executable file name
+@pytest.fixture()
+def accel_pppd(pytestconfig):
+ return pytestconfig.getoption("accel_pppd")
+
+
+# accel-cmd executable file name
+@pytest.fixture()
+def accel_cmd(pytestconfig):
+ return pytestconfig.getoption("accel_cmd")
+
+
+# accel-pppd configuration as string (should be redefined by specific test)
+@pytest.fixture()
+def accel_pppd_config():
+ return ""
+
+
+# accel-pppd configuration file name
+@pytest.fixture()
+def accel_pppd_config_file(accel_pppd_config):
+ # test setup:
+ filename = config.make_tmp(accel_pppd_config)
+
+ # test execution
+ yield filename
+
+ # test teardown:
+ config.delete_tmp(filename)
+
+
+# setup and teardown for tests that required running accel-pppd
+@pytest.fixture()
+def accel_pppd_instance(accel_pppd, accel_pppd_config_file, accel_cmd, pytestconfig):
+ # test setup:
+ is_started, accel_pppd_thread, accel_pppd_control = accel_pppd_process.start(
+ accel_pppd,
+ ["-c" + accel_pppd_config_file],
+ accel_cmd,
+ pytestconfig.getoption("accel_pppd_max_wait_time"),
+ )
+
+ # test execution:
+ yield is_started
+
+ # test teardown:
+ accel_pppd_process.end(
+ accel_pppd_thread,
+ accel_pppd_control,
+ accel_cmd,
+ pytestconfig.getoption("accel_pppd_max_finish_time"),
+ )
+
+# defines vlans that will be created over veth pair (might be redefined by specific test)
+@pytest.fixture()
+def veth_pair_vlans_config():
+ return {"vlans_a": [], "vlans_b": []}
+
+# setup and teardown for netns and veth pair
+@pytest.fixture()
+def veth_pair_netns(veth_pair_vlans_config):
+ # test setup:
+ veth_pair_netns_instance = veth.create_veth_pair_netns(veth_pair_vlans_config)
+
+ # test execution:
+ yield veth_pair_netns_instance
+
+ # test teardown:
+ veth.delete_veth_pair_netns(veth_pair_netns_instance)
diff --git a/tests/gcovr.conf b/tests/gcovr.conf
new file mode 100644
index 0000000..144b318
--- /dev/null
+++ b/tests/gcovr.conf
@@ -0,0 +1,2 @@
+#exclude = *
+exclude = ../build/CMakeFiles