clients/tests: add python test script for nmcli tests

Add a test which runs nmcli against our stub NetworkManager
service and compares the output.

The output formats of nmcli are complicated and not easily understood.
For example how --mode tabular|multiline interacts with selecting
output-fields (--fields) and output modes ([default]|--terse|--pretty).
Also, there are things like `nmcli connection show --order $FIELD_SPEC`.

We need unit tests to ensure that we don't change the output
accidentally.
This commit is contained in:
Thomas Haller 2018-05-04 09:02:53 +02:00
parent 5e6b0382ad
commit d4093a3a2c
36 changed files with 1520 additions and 76 deletions

View File

@ -104,9 +104,16 @@ script:
- |
if test "$BUILD_TYPE" == 'autotools'; then
git clean -fdx &&
./autogen.sh --with-systemd-logind=no --enable-more-warnings=no --enable-ifcfg-rh --enable-config-plugin-ibft --enable-ifupdown --enable-tests &&
./autogen.sh --prefix="$PWD/INST" --with-systemd-logind=no --enable-more-warnings=no --enable-ifcfg-rh --enable-config-plugin-ibft --enable-ifupdown --enable-tests &&
make -j4 &&
./contrib/travis/travis-check.sh
if [ "$CC" == gcc ]; then
sudo locale-gen de_DE.UTF-8 &&
sudo locale-gen pl_PL.UTF-8 &&
sudo make install &&
NM_TEST_CLIENT_CHECK_L10N=1 ./contrib/travis/travis-check.sh
else
./contrib/travis/travis-check.sh
fi
fi
env:

View File

@ -3780,6 +3780,62 @@ EXTRA_DIST += \
clients/tui/meson.build \
clients/tui/newt/meson.build
###############################################################################
# clients/tests
###############################################################################
check-local-clients-tests-test-client: clients/cli/nmcli clients/tests/test-client.py
mkdir -p "$(builddir)/clients/tests/"
GI_TYPELIB_PATH="$(abs_builddir)/libnm$${GI_TYPELIB_PATH:+:$$GI_TYPELIB_PATH}" \
LD_LIBRARY_PATH="$(abs_builddir)/libnm/.libs$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH}" \
NM_TEST_CLIENT_BUILDDIR="$(abs_builddir)" \
NM_TEST_CLIENT_NMCLI_PATH=clients/cli/nmcli \
NM_TEST_CLIENT_IN_DBUS_SESSION=0 \
$(srcdir)/clients/tests/test-client.py -v &> "$(builddir)/clients/tests/test-client.log" && r=ok; \
cat "$(builddir)/clients/tests/test-client.log"; \
test "$$r" == ok
check_local += check-local-clients-tests-test-client
CLEANFILES += clients/tests/test-client.log
EXTRA_DIST += \
clients/tests/test-client.py \
\
clients/tests/test-client.check-on-disk/test_001-001.expected \
clients/tests/test-client.check-on-disk/test_001-002.expected \
clients/tests/test-client.check-on-disk/test_001-003.expected \
clients/tests/test-client.check-on-disk/test_001-004.expected \
clients/tests/test-client.check-on-disk/test_001-005.expected \
clients/tests/test-client.check-on-disk/test_001-006.expected \
clients/tests/test-client.check-on-disk/test_002-001.expected \
clients/tests/test-client.check-on-disk/test_002-002.expected \
clients/tests/test-client.check-on-disk/test_002-003.expected \
clients/tests/test-client.check-on-disk/test_002-004.expected \
clients/tests/test-client.check-on-disk/test_002-005.expected \
clients/tests/test-client.check-on-disk/test_002-006.expected \
clients/tests/test-client.check-on-disk/test_002-007.expected \
clients/tests/test-client.check-on-disk/test_002-008.expected \
clients/tests/test-client.check-on-disk/test_002-009.expected \
clients/tests/test-client.check-on-disk/test_002-010.expected \
clients/tests/test-client.check-on-disk/test_002-011.expected \
clients/tests/test-client.check-on-disk/test_002-012.expected \
clients/tests/test-client.check-on-disk/test_002-013.expected \
clients/tests/test-client.check-on-disk/test_002-014.expected \
clients/tests/test-client.check-on-disk/test_002-015.expected \
clients/tests/test-client.check-on-disk/test_002-016.expected \
clients/tests/test-client.check-on-disk/test_002-017.expected \
clients/tests/test-client.check-on-disk/test_003-001.expected \
clients/tests/test-client.check-on-disk/test_003-002.expected \
clients/tests/test-client.check-on-disk/test_003-003.expected \
clients/tests/test-client.check-on-disk/test_003-004.expected \
clients/tests/test-client.check-on-disk/test_003-005.expected \
clients/tests/test-client.check-on-disk/test_003-006.expected \
clients/tests/test-client.check-on-disk/test_003-007.expected \
clients/tests/test-client.check-on-disk/test_003-008.expected \
\
$(NULL)
###############################################################################
# data
###############################################################################

View File

@ -0,0 +1,19 @@
location: clients/tests/test-client.py:489:test_001()/1
cmd: $NMCLI
lang: C
returncode: 0
stdout: 277 bytes
>>>
DNS configuration:
servers: 1.2.3.4 5.6.7.8
Use "nmcli device show" to get complete information about known devices and
"nmcli connection show" to get an overview on active connection profiles.
Consult nmcli(1) and nmcli-examples(5) manual pages for complete usage details.
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,21 @@
location: clients/tests/test-client.py:490:test_001()/2
cmd: $NMCLI
lang: pl_PL.UTF-8
returncode: 0
stdout: 310 bytes
>>>
DNS configuration:
servers: 1.2.3.4 5.6.7.8
Polecenie „nmcli device show” wyświetli pełne informacje o znanych
urządzeniach, a „nmcli connection show” wyświetli przegląd aktywnych
profili połączeń.
Strony podręcznika nmcli(1) i nmcli-examples(5) zawierają pełne informacje
o użyciu.
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,13 @@
location: clients/tests/test-client.py:492:test_001()/3
cmd: $NMCLI -f AP -mode multiline -p d show wlan0
lang: C
returncode: 10
stdout: 0 bytes
>>>
<<<
stderr: 33 bytes
>>>
Error: Device 'wlan0' not found.
<<<

View File

@ -0,0 +1,13 @@
location: clients/tests/test-client.py:493:test_001()/4
cmd: $NMCLI -f AP -mode multiline -p d show wlan0
lang: de_DE.utf8
returncode: 10
stdout: 0 bytes
>>>
<<<
stderr: 47 bytes
>>>
Fehler: Gerät »wlan0« wurde nicht gefunden.
<<<

View File

@ -0,0 +1,13 @@
location: clients/tests/test-client.py:495:test_001()/5
cmd: $NMCLI c s
lang: C
returncode: 0
stdout: 26 bytes
>>>
NAME UUID TYPE DEVICE
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,13 @@
location: clients/tests/test-client.py:497:test_001()/6
cmd: $NMCLI bogus s
lang: C
returncode: 2
stdout: 0 bytes
>>>
<<<
stderr: 68 bytes
>>>
Error: argument 'bogus' not understood. Try passing --help instead.
<<<

View File

@ -0,0 +1,17 @@
location: clients/tests/test-client.py:502:test_002()/1
cmd: $NMCLI d
lang: C
returncode: 0
stdout: 215 bytes
>>>
DEVICE TYPE STATE CONNECTION
eth0 ethernet unavailable --
wlan0 wifi unavailable --
wlan1 wifi unavailable --
wlan1 wifi unavailable --
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,17 @@
location: clients/tests/test-client.py:504:test_002()/2
cmd: $NMCLI -f all d
lang: C
returncode: 0
stdout: 530 bytes
>>>
DEVICE TYPE STATE DBUS-PATH CONNECTION CON-UUID CON-PATH
eth0 ethernet unavailable /org/freedesktop/NetworkManager/Devices/1 -- -- --
wlan0 wifi unavailable /org/freedesktop/NetworkManager/Devices/2 -- -- --
wlan1 wifi unavailable /org/freedesktop/NetworkManager/Devices/3 -- -- --
wlan1 wifi unavailable /org/freedesktop/NetworkManager/Devices/4 -- -- --
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,35 @@
location: clients/tests/test-client.py:506:test_002()/3
cmd: $NMCLI
lang: C
returncode: 0
stdout: 551 bytes
>>>
eth0: unavailable
"eth0"
ethernet (virtual), 72:41:AB:90:41:5D, hw
wlan0: unavailable
"wlan0"
wifi (virtual), 5A:88:5E:B6:90:40, hw
wlan1: unavailable
"wlan1"
wifi (virtual), 7C:D4:69:31:67:0B, hw
wlan1: unavailable
"wlan1"
wifi (virtual), 41:21:6B:F3:C9:4A, hw
DNS configuration:
servers: 1.2.3.4 5.6.7.8
Use "nmcli device show" to get complete information about known devices and
"nmcli connection show" to get an overview on active connection profiles.
Consult nmcli(1) and nmcli-examples(5) manual pages for complete usage details.
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,36 @@
location: clients/tests/test-client.py:508:test_002()/4
cmd: $NMCLI -f AP -mode multiline d show wlan0
lang: C
returncode: 0
stdout: 1107 bytes
>>>
AP[1].IN-USE:
AP[1].SSID: wlan0-ap-3
AP[1].MODE: Infra
AP[1].CHAN: 1
AP[1].RATE: 54 Mbit/s
AP[1].SIGNAL: 61
AP[1].BARS: ***
AP[1].SECURITY: WPA1 WPA2
AP[2].IN-USE:
AP[2].SSID: wlan0-ap-1
AP[2].MODE: Infra
AP[2].CHAN: 1
AP[2].RATE: 54 Mbit/s
AP[2].SIGNAL: 34
AP[2].BARS: **
AP[2].SECURITY: WPA1 WPA2
AP[3].IN-USE:
AP[3].SSID: wlan0-ap-2
AP[3].MODE: Infra
AP[3].CHAN: 1
AP[3].RATE: 54 Mbit/s
AP[3].SIGNAL: 29
AP[3].BARS: *
AP[3].SECURITY: WPA1 WPA2
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,42 @@
location: clients/tests/test-client.py:509:test_002()/5
cmd: $NMCLI -f AP -mode multiline -p d show wlan0
lang: C
returncode: 0
stdout: 1558 bytes
>>>
===============================================================================
Device details (wlan0)
===============================================================================
AP[1].IN-USE:
AP[1].SSID: wlan0-ap-3
AP[1].MODE: Infra
AP[1].CHAN: 1
AP[1].RATE: 54 Mbit/s
AP[1].SIGNAL: 61
AP[1].BARS: ***
AP[1].SECURITY: WPA1 WPA2
-------------------------------------------------------------------------------
AP[2].IN-USE:
AP[2].SSID: wlan0-ap-1
AP[2].MODE: Infra
AP[2].CHAN: 1
AP[2].RATE: 54 Mbit/s
AP[2].SIGNAL: 34
AP[2].BARS: **
AP[2].SECURITY: WPA1 WPA2
-------------------------------------------------------------------------------
AP[3].IN-USE:
AP[3].SSID: wlan0-ap-2
AP[3].MODE: Infra
AP[3].CHAN: 1
AP[3].RATE: 54 Mbit/s
AP[3].SIGNAL: 29
AP[3].BARS: *
AP[3].SECURITY: WPA1 WPA2
-------------------------------------------------------------------------------
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,36 @@
location: clients/tests/test-client.py:510:test_002()/6
cmd: $NMCLI -f AP -mode multiline -t d show wlan0
lang: C
returncode: 0
stdout: 435 bytes
>>>
AP[1].IN-USE:
AP[1].SSID:wlan0-ap-3
AP[1].MODE:Infra
AP[1].CHAN:1
AP[1].RATE:54 Mbit/s
AP[1].SIGNAL:61
AP[1].BARS:***
AP[1].SECURITY:WPA1 WPA2
AP[2].IN-USE:
AP[2].SSID:wlan0-ap-1
AP[2].MODE:Infra
AP[2].CHAN:1
AP[2].RATE:54 Mbit/s
AP[2].SIGNAL:34
AP[2].BARS:**
AP[2].SECURITY:WPA1 WPA2
AP[3].IN-USE:
AP[3].SSID:wlan0-ap-2
AP[3].MODE:Infra
AP[3].CHAN:1
AP[3].RATE:54 Mbit/s
AP[3].SIGNAL:29
AP[3].BARS:*
AP[3].SECURITY:WPA1 WPA2
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,16 @@
location: clients/tests/test-client.py:511:test_002()/7
cmd: $NMCLI -f AP -mode tabular d show wlan0
lang: C
returncode: 0
stdout: 304 bytes
>>>
NAME IN-USE SSID MODE CHAN RATE SIGNAL BARS SECURITY
AP[1] wlan0-ap-3 Infra 1 54 Mbit/s 61 *** WPA1 WPA2
AP[2] wlan0-ap-1 Infra 1 54 Mbit/s 34 ** WPA1 WPA2
AP[3] wlan0-ap-2 Infra 1 54 Mbit/s 29 * WPA1 WPA2
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,20 @@
location: clients/tests/test-client.py:512:test_002()/8
cmd: $NMCLI -f AP -mode tabular -p d show wlan0
lang: C
returncode: 0
stdout: 460 bytes
>>>
==========================
Device details (wlan0)
==========================
NAME IN-USE SSID MODE CHAN RATE SIGNAL BARS SECURITY
----------------------------------------------------------------------------
AP[1] wlan0-ap-3 Infra 1 54 Mbit/s 61 *** WPA1 WPA2
AP[2] wlan0-ap-1 Infra 1 54 Mbit/s 34 ** WPA1 WPA2
AP[3] wlan0-ap-2 Infra 1 54 Mbit/s 29 * WPA1 WPA2
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,15 @@
location: clients/tests/test-client.py:513:test_002()/9
cmd: $NMCLI -f AP -mode tabular -t d show wlan0
lang: C
returncode: 0
stdout: 165 bytes
>>>
AP[1]: :wlan0-ap-3:Infra:1:54 Mbit/s:61:*** :WPA1 WPA2
AP[2]: :wlan0-ap-1:Infra:1:54 Mbit/s:34:** :WPA1 WPA2
AP[3]: :wlan0-ap-2:Infra:1:54 Mbit/s:29:* :WPA1 WPA2
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,36 @@
location: clients/tests/test-client.py:515:test_002()/10
cmd: $NMCLI -f AP -mode multiline d show wlan0
lang: pl_PL.UTF-8
returncode: 0
stdout: 1134 bytes
>>>
AP[1].IN-USE:
AP[1].SSID: wlan0-ap-3
AP[1].MODE: Infrastruktura
AP[1].CHAN: 1
AP[1].RATE: 54Mb/s
AP[1].SIGNAL: 61
AP[1].BARS: ***
AP[1].SECURITY: WPA1 WPA2
AP[2].IN-USE:
AP[2].SSID: wlan0-ap-1
AP[2].MODE: Infrastruktura
AP[2].CHAN: 1
AP[2].RATE: 54Mb/s
AP[2].SIGNAL: 34
AP[2].BARS: **
AP[2].SECURITY: WPA1 WPA2
AP[3].IN-USE:
AP[3].SSID: wlan0-ap-2
AP[3].MODE: Infrastruktura
AP[3].CHAN: 1
AP[3].RATE: 54Mb/s
AP[3].SIGNAL: 29
AP[3].BARS: *
AP[3].SECURITY: WPA1 WPA2
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,42 @@
location: clients/tests/test-client.py:516:test_002()/11
cmd: $NMCLI -f AP -mode multiline -p d show wlan0
lang: pl_PL.UTF-8
returncode: 0
stdout: 1592 bytes
>>>
===============================================================================
Informacje o urządzeniu (wlan0)
===============================================================================
AP[1].IN-USE:
AP[1].SSID: wlan0-ap-3
AP[1].MODE: Infrastruktura
AP[1].CHAN: 1
AP[1].RATE: 54Mb/s
AP[1].SIGNAL: 61
AP[1].BARS: ***
AP[1].SECURITY: WPA1 WPA2
-------------------------------------------------------------------------------
AP[2].IN-USE:
AP[2].SSID: wlan0-ap-1
AP[2].MODE: Infrastruktura
AP[2].CHAN: 1
AP[2].RATE: 54Mb/s
AP[2].SIGNAL: 34
AP[2].BARS: **
AP[2].SECURITY: WPA1 WPA2
-------------------------------------------------------------------------------
AP[3].IN-USE:
AP[3].SSID: wlan0-ap-2
AP[3].MODE: Infrastruktura
AP[3].CHAN: 1
AP[3].RATE: 54Mb/s
AP[3].SIGNAL: 29
AP[3].BARS: *
AP[3].SECURITY: WPA1 WPA2
-------------------------------------------------------------------------------
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,36 @@
location: clients/tests/test-client.py:517:test_002()/12
cmd: $NMCLI -f AP -mode multiline -t d show wlan0
lang: pl_PL.UTF-8
returncode: 0
stdout: 462 bytes
>>>
AP[1].IN-USE:
AP[1].SSID:wlan0-ap-3
AP[1].MODE:Infrastruktura
AP[1].CHAN:1
AP[1].RATE:54Mb/s
AP[1].SIGNAL:61
AP[1].BARS:***
AP[1].SECURITY:WPA1 WPA2
AP[2].IN-USE:
AP[2].SSID:wlan0-ap-1
AP[2].MODE:Infrastruktura
AP[2].CHAN:1
AP[2].RATE:54Mb/s
AP[2].SIGNAL:34
AP[2].BARS:**
AP[2].SECURITY:WPA1 WPA2
AP[3].IN-USE:
AP[3].SSID:wlan0-ap-2
AP[3].MODE:Infrastruktura
AP[3].CHAN:1
AP[3].RATE:54Mb/s
AP[3].SIGNAL:29
AP[3].BARS:*
AP[3].SECURITY:WPA1 WPA2
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,16 @@
location: clients/tests/test-client.py:518:test_002()/13
cmd: $NMCLI -f AP -mode tabular d show wlan0
lang: pl_PL.UTF-8
returncode: 0
stdout: 338 bytes
>>>
NAME IN-USE SSID MODE CHAN RATE SIGNAL BARS SECURITY
AP[1] wlan0-ap-3 Infrastruktura 1 54Mb/s 61 *** WPA1 WPA2
AP[2] wlan0-ap-1 Infrastruktura 1 54Mb/s 34 ** WPA1 WPA2
AP[3] wlan0-ap-2 Infrastruktura 1 54Mb/s 29 * WPA1 WPA2
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,20 @@
location: clients/tests/test-client.py:519:test_002()/14
cmd: $NMCLI -f AP -mode tabular -p d show wlan0
lang: pl_PL.UTF-8
returncode: 0
stdout: 530 bytes
>>>
===================================
Informacje o urządzeniu (wlan0)
===================================
NAME IN-USE SSID MODE CHAN RATE SIGNAL BARS SECURITY
-----------------------------------------------------------------------------------
AP[1] wlan0-ap-3 Infrastruktura 1 54Mb/s 61 *** WPA1 WPA2
AP[2] wlan0-ap-1 Infrastruktura 1 54Mb/s 34 ** WPA1 WPA2
AP[3] wlan0-ap-2 Infrastruktura 1 54Mb/s 29 * WPA1 WPA2
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,15 @@
location: clients/tests/test-client.py:520:test_002()/15
cmd: $NMCLI -f AP -mode tabular -t d show wlan0
lang: pl_PL.UTF-8
returncode: 0
stdout: 192 bytes
>>>
AP[1]: :wlan0-ap-3:Infrastruktura:1:54Mb/s:61:*** :WPA1 WPA2
AP[2]: :wlan0-ap-1:Infrastruktura:1:54Mb/s:34:** :WPA1 WPA2
AP[3]: :wlan0-ap-2:Infrastruktura:1:54Mb/s:29:* :WPA1 WPA2
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,14 @@
location: clients/tests/test-client.py:522:test_002()/16
cmd: $NMCLI c
lang: C
returncode: 0
stdout: 126 bytes
>>>
NAME UUID TYPE DEVICE
con-1 5fcfd6d7-1e63-3332-8826-a7eda103792d ethernet --
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,33 @@
location: clients/tests/test-client.py:524:test_002()/17
cmd: $NMCLI c s con-1
lang: C
returncode: 0
stdout: 990 bytes
>>>
connection.id: con-1
connection.uuid: 5fcfd6d7-1e63-3332-8826-a7eda103792d
connection.stable-id: --
connection.type: 802-3-ethernet
connection.interface-name: --
connection.autoconnect: yes
connection.autoconnect-priority: 0
connection.autoconnect-retries: -1 (default)
connection.auth-retries: -1
connection.timestamp: 0
connection.read-only: no
connection.permissions: --
connection.zone: --
connection.master: --
connection.slave-type: --
connection.autoconnect-slaves: -1 (default)
connection.secondaries: --
connection.gateway-ping-timeout: 0
connection.metered: unknown
connection.lldp: default
connection.mdns: -1 (default)
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,13 @@
location: clients/tests/test-client.py:534:test_003()/1
cmd: $NMCLI c add type ethernet ifname '*' con-name con-xx1
lang: C
returncode: 0
stdout: 80 bytes
>>>
Connection 'con-xx1' (UUID-con-xx1-REPLACED-REPLACED-REPLA) successfully added.
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,15 @@
location: clients/tests/test-client.py:537:test_003()/2
cmd: $NMCLI c s
lang: C
returncode: 0
stdout: 195 bytes
>>>
NAME UUID TYPE DEVICE
con-1 5fcfd6d7-1e63-3332-8826-a7eda103792d ethernet --
con-xx1 UUID-con-xx1-REPLACED-REPLACED-REPLA ethernet --
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,13 @@
location: clients/tests/test-client.py:542:test_003()/3
cmd: $NMCLI c add type ethernet ifname '*'
lang: C
returncode: 0
stdout: 81 bytes
>>>
Connection 'ethernet' (UUID-ethernet-REPLACED-REPLACED-REPL) successfully added.
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,16 @@
location: clients/tests/test-client.py:545:test_003()/4
cmd: $NMCLI c s
lang: C
returncode: 0
stdout: 264 bytes
>>>
NAME UUID TYPE DEVICE
con-1 5fcfd6d7-1e63-3332-8826-a7eda103792d ethernet --
con-xx1 UUID-con-xx1-REPLACED-REPLACED-REPLA ethernet --
ethernet UUID-ethernet-REPLACED-REPLACED-REPL ethernet --
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,16 @@
location: clients/tests/test-client.py:547:test_003()/5
cmd: $NMCLI c s
lang: pl_PL.UTF-8
returncode: 0
stdout: 264 bytes
>>>
NAME UUID TYPE DEVICE
con-1 5fcfd6d7-1e63-3332-8826-a7eda103792d ethernet --
con-xx1 UUID-con-xx1-REPLACED-REPLACED-REPLA ethernet --
ethernet UUID-ethernet-REPLACED-REPLACED-REPL ethernet --
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,16 @@
location: clients/tests/test-client.py:550:test_003()/6
cmd: $NMCLI -f ALL c s
lang: C
returncode: 0
stdout: 912 bytes
>>>
NAME UUID TYPE TIMESTAMP TIMESTAMP-REAL AUTOCONNECT AUTOCONNECT-PRIORITY READONLY DBUS-PATH ACTIVE DEVICE STATE ACTIVE-PATH SLAVE
con-1 5fcfd6d7-1e63-3332-8826-a7eda103792d ethernet 0 never yes 0 no /org/freedesktop/NetworkManager/Settings/Connection/1 no -- -- -- --
con-xx1 UUID-con-xx1-REPLACED-REPLACED-REPLA ethernet 0 never yes 0 no /org/freedesktop/NetworkManager/Settings/Connection/2 no -- -- -- --
ethernet UUID-ethernet-REPLACED-REPLACED-REPL ethernet 0 never yes 0 no /org/freedesktop/NetworkManager/Settings/Connection/3 no -- -- -- --
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,16 @@
location: clients/tests/test-client.py:552:test_003()/7
cmd: $NMCLI -f ALL c s
lang: pl_PL.UTF-8
returncode: 0
stdout: 912 bytes
>>>
NAME UUID TYPE TIMESTAMP TIMESTAMP-REAL AUTOCONNECT AUTOCONNECT-PRIORITY READONLY DBUS-PATH ACTIVE DEVICE STATE ACTIVE-PATH SLAVE
con-1 5fcfd6d7-1e63-3332-8826-a7eda103792d ethernet 0 nigdy tak 0 nie /org/freedesktop/NetworkManager/Settings/Connection/1 nie -- -- -- --
con-xx1 UUID-con-xx1-REPLACED-REPLACED-REPLA ethernet 0 nigdy tak 0 nie /org/freedesktop/NetworkManager/Settings/Connection/2 nie -- -- -- --
ethernet UUID-ethernet-REPLACED-REPLACED-REPL ethernet 0 nigdy tak 0 nie /org/freedesktop/NetworkManager/Settings/Connection/3 nie -- -- -- --
<<<
stderr: 0 bytes
>>>
<<<

View File

@ -0,0 +1,22 @@
location: clients/tests/test-client.py:556:test_003()/8
cmd: $NMCLI --complete-args -f ALL c s ''
lang: pl_PL.UTF-8
returncode: 0
stdout: 64 bytes
>>>
--active
--order
apath
con-1
con-xx1
ethernet
help
id
path
uuid
<<<
stderr: 0 bytes
>>>
<<<

603
clients/tests/test-client.py Executable file
View File

@ -0,0 +1,603 @@
#!/usr/bin/env python
from __future__ import print_function
import sys
import os
import errno
import unittest
import socket
import itertools
import subprocess
import shlex
import re
import dbus
import time
import dbus.service
import dbus.mainloop.glib
# The test can be configured via the following environment variables:
ENV_NM_TEST_CLIENT_BUILDDIR = 'NM_TEST_CLIENT_BUILDDIR'
ENV_NM_TEST_CLIENT_NMCLI_PATH = 'NM_TEST_CLIENT_NMCLI_PATH'
ENV_NM_TEST_CLIENT_CHECK_L10N = 'NM_TEST_CLIENT_CHECK_L10N'
ENV_NM_TEST_REGENERATE = 'NM_TEST_REGENERATE'
###############################################################################
class PathConfiguration:
@staticmethod
def srcdir():
# this is the directory where the test script itself lies.
# Based on this directory, we find other parts that we expect
# in the source repository.
return os.path.dirname(os.path.abspath(__file__))
@staticmethod
def top_srcdir():
return os.path.abspath(PathConfiguration.srcdir() + "/../..")
@staticmethod
def test_networkmanager_service_path():
v = os.path.abspath(PathConfiguration.top_srcdir() + "/tools/test-networkmanager-service.py")
assert os.path.exists(v), ("Cannot find test server at \"%s\"" % (v))
return v
###############################################################################
os.sys.path.append(os.path.abspath(PathConfiguration.top_srcdir() + '/examples/python'))
import nmex
dbus_session_inited = False
_DEFAULT_ARG = object()
###############################################################################
class Util:
@staticmethod
def python_has_version(major, minor = 0):
return sys.version_info[0] > major \
or ( sys.version_info[0] == major \
and sys.version_info[1] >= minor)
@staticmethod
def is_string(s):
if Util.python_has_version(3):
t = str
else:
t = basestring
return isinstance(s, t)
_find_unsafe = re.compile(r'[^\w@%+=:,./-]',
re.ASCII if sys.version_info[0] >= 3 else 0).search
@staticmethod
def quote(s):
if Util.python_has_version(3, 3):
return shlex.quote(s)
if not s:
return "''"
if Util._find_unsafe(s) is None:
return s
return "'" + s.replace("'", "'\"'\"'") + "'"
@staticmethod
def popen_wait(p, timeout = None):
# wait() has a timeout argument only since 3.3
if Util.python_has_version(3, 3):
return p.wait(timeout)
if timeout is None:
return p.wait()
start = nmex.nm_boot_time_ns()
while True:
if p.poll() is not None:
return p.returncode
if start + (timeout * 1000000000) < nmex.nm_boot_time_ns():
raise Exception("timeout expired")
time.sleep(0.05)
@staticmethod
def iter_single(itr, min_num = 1, max_num = 1):
itr = list(itr)
n = 0
v = None
for c in itr:
n += 1
if n > 1:
break
v = c
if n < min_num:
raise AssertionError("Expected at least %s elements, but %s found" % (min_num, n))
if n > max_num:
raise AssertionError("Expected at most %s elements, but %s found" % (max_num, n))
return v
###############################################################################
class Configuration:
def __init__(self):
self._values = {}
def get(self, name):
v = self._values.get(name, None)
if name in self._values:
return v
if name == ENV_NM_TEST_CLIENT_BUILDDIR:
v = os.environ.get(ENV_NM_TEST_CLIENT_BUILDDIR, PathConfiguration.top_srcdir())
if not os.path.isdir(v):
raise Exception("Missing builddir. Set NM_TEST_CLIENT_BUILDDIR?")
elif name == ENV_NM_TEST_CLIENT_NMCLI_PATH:
v = os.environ.get(ENV_NM_TEST_CLIENT_NMCLI_PATH, None)
if v is None:
try:
v = os.path.abspath(self.get(ENV_NM_TEST_CLIENT_BUILDDIR) + "/clients/cli/nmcli")
except:
pass
if not os.path.exists(v):
raise Exception("Missing nmcli binary. Set NM_TEST_CLIENT_NMCLI_PATH?")
elif name == ENV_NM_TEST_CLIENT_CHECK_L10N:
# if we test locales other than 'C', the output of nmcli depends on whether
# nmcli can load the translations. Unfortunately, I cannot find a way to
# make gettext use the po/*.gmo files from the build-dir.
#
# hence, such tests only work, if you also issue `make-install`
#
# Only by setting NM_TEST_CLIENT_CHECK_L10N=1, these tests are included
# as well.
v = (os.environ.get(ENV_NM_TEST_CLIENT_CHECK_L10N, '0') == '1')
elif name == ENV_NM_TEST_REGENERATE:
# in the "regenerate" mode, the tests will rewrite the files on disk against
# which we assert. That is useful, if there are intentional changes and
# we want to regenerate the expected output.
v = (os.environ.get(ENV_NM_TEST_REGENERATE, '0') == '1')
else:
raise Exception()
self._values[name] = v
return v
conf = Configuration()
###############################################################################
class NMStubServer:
@staticmethod
def _conn_get_main_object(conn):
try:
return conn.get_object('org.freedesktop.NetworkManager', '/org/freedesktop/NetworkManager')
except:
return None
def __init__(self):
service_path = PathConfiguration.test_networkmanager_service_path()
self._conn = dbus.SessionBus()
p = subprocess.Popen([sys.executable, service_path],
stdin = subprocess.PIPE)
start = nmex.nm_boot_time_ns()
while True:
if p.poll() is not None:
p.stdin.close()
if p.returncode == 77:
raise unittest.SkipTest('the stub service %s exited with status 77' % (service_path))
raise Exception('the stub service %s exited unexpectedly' % (service_path))
nmobj = self._conn_get_main_object(self._conn)
if nmobj is not None:
break
if (nmex.nm_boot_time_ns() - start) / 1000000 >= 2000:
p.stdin.close()
p.kill()
Util.popen_wait(p, 1000)
raise Exception("after starting stub service the D-Bus name was not claimed in time")
self._nmobj = nmobj
self._nmiface = dbus.Interface(nmobj, "org.freedesktop.NetworkManager.LibnmGlibTest")
self._p = p
def shutdown(self):
self._nmobj = None
self._nmiface = None
self._conn = None
self._p.stdin.close()
self._p.kill()
Util.popen_wait(self._p, 1000)
self._p = None
if self._conn_get_main_object(self._conn) is not None:
raise Exception("Stub service is not still here although it should shut down")
class _MethodProxy:
def __init__(self, parent, method_name):
self._parent = parent
self._method_name = method_name
def __call__(self, *args, **kwargs):
dbus_iface = kwargs.pop('dbus_iface', None)
if dbus_iface is None:
dbus_iface = self._parent._nmiface
method = dbus_iface.get_dbus_method(self._method_name)
if kwargs:
# for convenience, we allow the caller to specify arguments
# as kwargs. In this case, we construct a a{sv} array as last argument.
kwargs2 = {}
args = list(args)
args.append(kwargs2)
for k in kwargs.keys():
kwargs2[k] = kwargs[k]
return method(*args)
def __getattr__(self, member):
if not member.startswith("op_"):
raise AttributeError(member)
return self._MethodProxy(self, member[3:])
def addConnection(self, connection, verify_connection = True):
return self.op_AddConnection(connection, verify_connection)
def findConnectionUuid(self, con_id):
try:
u = Util.iter_single(self.op_FindConnections(con_id = con_id))[1]
assert u, ("Invalid uuid %s" % (u))
except Exception as e:
raise AssertionError("Unexpectedly not found connection %s: %s" % (con_id, str(e)))
return u
###############################################################################
class NmTestBase(unittest.TestCase):
pass
class TestNmcli(NmTestBase):
@staticmethod
def _replace(text, replace_arr):
if not replace_arr:
return text
text = [text]
for replace in replace_arr:
try:
v_search = replace[0]()
except TypeError:
v_search = replace[0]
assert v_search is None or Util.is_string(v_search)
if not v_search:
continue
v_replace = replace[1]
v_search = v_search.encode('utf-8')
v_replace = v_replace.encode('utf-8')
text2 = []
for t in text:
if isinstance(t, tuple):
text2.append(t)
continue
t2 = t.split(v_search)
text2.append(t2[0])
for t3 in t2[1:]:
text2.append( (v_replace,) )
text2.append(t3)
text = text2
return b''.join([(t[0] if isinstance(t, tuple) else t) for t in text])
def call_nmcli(self,
args,
lang = None,
check_on_disk = _DEFAULT_ARG,
expected_returncode = _DEFAULT_ARG,
expected_stdout = _DEFAULT_ARG,
expected_stderr = _DEFAULT_ARG,
replace_stdout = None,
replace_stderr = None,
sort_lines_stdout = False):
frame = sys._getframe(1)
calling_fcn = frame.f_code.co_name
calling_num = self._calling_num.get(calling_fcn, 0) + 1
self._calling_num[calling_fcn] = calling_num
if lang is None or lang == 'C':
lang = 'C'
language = ''
elif lang is 'de':
lang = 'de_DE.utf8'
language = 'de'
elif lang is 'pl':
lang = 'pl_PL.UTF-8'
language = 'pl'
else:
self.fail('invalid language %s' % (lang))
env = {}
for k in ['LD_LIBRARY_PATH',
'DBUS_SESSION_BUS_ADDRESS']:
val = os.environ.get(k, None)
if val is not None:
env[k] = val
env['LANG'] = lang
env['LANGUAGE'] = language
env['LIBNM_USE_SESSION_BUS'] = '1'
env['LIBNM_USE_NO_UDEV'] = '1'
env['TERM'] = 'linux'
args = [conf.get(ENV_NM_TEST_CLIENT_NMCLI_PATH)] + list(args)
p = subprocess.Popen(args,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
env = env)
Util.popen_wait(p, 2000)
(returncode, stdout, stderr) = (p.returncode, p.stdout.read(), p.stderr.read())
p.stdout.close()
p.stderr.close()
stdout = self._replace(stdout, replace_stdout)
stderr = self._replace(stderr, replace_stderr)
if sort_lines_stdout:
stdout = b'\n'.join(sorted(stdout.split(b'\n')))
ignore_l10n_diff = ( lang != 'C'
and not conf.get(ENV_NM_TEST_CLIENT_CHECK_L10N))
test_name = '%s-%03d' % (calling_fcn, calling_num)
if check_on_disk is _DEFAULT_ARG:
check_on_disk = ( expected_returncode is _DEFAULT_ARG
and expected_stdout is _DEFAULT_ARG
and expected_stderr is _DEFAULT_ARG)
if expected_returncode is _DEFAULT_ARG:
expected_returncode = None
if expected_stdout is _DEFAULT_ARG:
expected_stdout = None
if expected_stderr is _DEFAULT_ARG:
expected_stderr = None
if expected_stderr is not None:
if expected_stderr != stderr:
if ignore_l10n_diff:
self._skip_test_for_l10n_diff.append(test_name)
else:
self.assertEqual(expected_stderr, stderr)
if expected_stdout is not None:
if expected_stdout != stdout:
if ignore_l10n_diff:
self._skip_test_for_l10n_diff.append(test_name)
else:
self.assertEqual(expected_stdout, stdout)
if expected_returncode is not None:
self.assertEqual(expected_returncode, returncode)
dirname = PathConfiguration.srcdir() + '/test-client.check-on-disk'
filename = os.path.abspath(dirname + '/' + test_name + '.expected')
if not check_on_disk:
if os.path.exists(filename):
self.fail("The file '%s' exists, although we expect it not to." % (filename))
return
try:
with open(filename, 'rb') as content_file:
content_old = content_file.read()
except:
content_old = None
# we cannot use frame.f_code.co_filename directly, because it might be different depending
# on where the file lies and which is CWD. We still want to give the location of
# the file, so that the user can easier find the source (when looking at the .expected files)
script_filename = 'clients/tests/test-client.py'
self.assertTrue(os.path.abspath(frame.f_code.co_filename).endswith(script_filename))
calling_location = '%s:%d:%s()/%d' % (script_filename, frame.f_lineno, frame.f_code.co_name, calling_num)
content_new = ('location: %s\n' % (calling_location)).encode('utf8') + \
('cmd: $NMCLI %s\n' % (' '.join([Util.quote(a) for a in args[1:]]))).encode('utf8') + \
('lang: %s\n' % (lang)).encode('utf8') + \
('returncode: %d\n' % (returncode)).encode('utf8') + \
('stdout: %d bytes\n>>>\n' % (len(stdout))).encode('utf8') + \
stdout + \
('\n<<<\nstderr: %d bytes\n>>>\n' % (len(stderr))).encode('utf8') + \
stderr + \
'\n<<<\n'.encode('utf8')
w = conf.get(ENV_NM_TEST_REGENERATE)
if content_old is not None:
if content_old == content_new:
return
if not w:
if ignore_l10n_diff:
self._skip_test_for_l10n_diff.append(test_name)
return
print("\n\n\nThe file '%s' does not have the expected content:" % (filename))
print("ACTUAL OUTPUT:\n[[%s]]\n" % (content_new))
print("EXPECT OUTPUT:\n[[%s]]\n" % (content_old))
print("Let the test write the file by rerunning with NM_TEST_REGENERATE=1\n\n")
raise AssertionError("Unexpected output of command, expected %s. Rerun test with NM_TEST_REGENERATE=1 to regenerate files" % (filename))
else:
if not w:
self.fail("The file '%s' does not exist. Let the test write the file by rerunning with NM_TEST_REGENERATE=1" % (filename))
try:
if not os.path.exists(dirname):
os.makedirs(dirname)
with open(filename, 'wb') as content_file:
content_file.write(content_new)
except Exception as e:
self.fail("Failure to write '%s': %s" % (filename, e))
def setUp(self):
if not dbus_session_inited:
self.skipTest("Own D-Bus session for testing is not initialized. Do you have dbus-run-session available?")
self.srv = NMStubServer()
self._calling_num = {}
self._skip_test_for_l10n_diff = []
def tearDown(self):
self.srv.shutdown()
self.srv = None
self._calling_num = None
if self._skip_test_for_l10n_diff:
# nmcli loads translations from the installation path. This failure commonly
# happens because you did not install the binary in the --prefix, before
# running the test. Hence, translations are not available or differ.
msg = "Skipped asserting for localized tests %s. Set NM_TEST_CLIENT_CHECK_L10N=1 to force fail." % (','.join(self._skip_test_for_l10n_diff))
if Util.python_has_version(3):
# python2 does not suppot skipping the test during tearDown()
self.skipTest(msg)
print(msg + "\n")
self._skip_test_for_l10n_diff = None
def init_001(self):
self.srv.op_AddObj('WiredDevice',
iface = 'eth0')
self.srv.op_AddObj('WifiDevice',
iface = 'wlan0')
self.srv.op_AddObj('WifiDevice',
iface = 'wlan1')
# add another device with an identical ifname. The D-Bus API itself
# does not enforce the ifnames are unique.
self.srv.op_AddObj('WifiDevice',
ident = 'wlan1/x',
iface = 'wlan1')
self.srv.op_AddObj('WifiAp',
device = 'wlan0')
self.srv.op_AddObj('WifiAp',
device = 'wlan0')
self.srv.op_AddObj('WifiAp',
device = 'wlan0')
self.srv.op_AddObj('WifiAp',
device = 'wlan1')
self.srv.addConnection( {
'connection': {
'type': '802-3-ethernet',
'id': 'con-1',
},
})
def test_001(self):
self.call_nmcli([])
self.call_nmcli([], lang = 'pl')
self.call_nmcli(['-f', 'AP', '-mode', 'multiline', '-p', 'd', 'show', 'wlan0'])
self.call_nmcli(['-f', 'AP', '-mode', 'multiline', '-p', 'd', 'show', 'wlan0'], lang = 'de')
self.call_nmcli(['c', 's'])
self.call_nmcli(['bogus', 's'])
def test_002(self):
self.init_001()
self.call_nmcli(['d'])
self.call_nmcli(['-f', 'all', 'd'])
self.call_nmcli([])
self.call_nmcli(['-f', 'AP', '-mode', 'multiline', 'd', 'show', 'wlan0'])
self.call_nmcli(['-f', 'AP', '-mode', 'multiline', '-p', 'd', 'show', 'wlan0'])
self.call_nmcli(['-f', 'AP', '-mode', 'multiline', '-t', 'd', 'show', 'wlan0'])
self.call_nmcli(['-f', 'AP', '-mode', 'tabular', 'd', 'show', 'wlan0'])
self.call_nmcli(['-f', 'AP', '-mode', 'tabular', '-p', 'd', 'show', 'wlan0'])
self.call_nmcli(['-f', 'AP', '-mode', 'tabular', '-t', 'd', 'show', 'wlan0'])
self.call_nmcli(['-f', 'AP', '-mode', 'multiline', 'd', 'show', 'wlan0'], lang = 'pl')
self.call_nmcli(['-f', 'AP', '-mode', 'multiline', '-p', 'd', 'show', 'wlan0'], lang = 'pl')
self.call_nmcli(['-f', 'AP', '-mode', 'multiline', '-t', 'd', 'show', 'wlan0'], lang = 'pl')
self.call_nmcli(['-f', 'AP', '-mode', 'tabular', 'd', 'show', 'wlan0'], lang = 'pl')
self.call_nmcli(['-f', 'AP', '-mode', 'tabular', '-p', 'd', 'show', 'wlan0'], lang = 'pl')
self.call_nmcli(['-f', 'AP', '-mode', 'tabular', '-t', 'd', 'show', 'wlan0'], lang = 'pl')
self.call_nmcli(['c'])
self.call_nmcli(['c', 's', 'con-1'])
def test_003(self):
self.init_001()
replace_stdout = []
replace_stdout.append((lambda: self.srv.findConnectionUuid('con-xx1'), 'UUID-con-xx1-REPLACED-REPLACED-REPLA'))
self.call_nmcli(['c', 'add', 'type', 'ethernet', 'ifname', '*', 'con-name', 'con-xx1'],
replace_stdout = replace_stdout)
self.call_nmcli(['c', 's'],
replace_stdout = replace_stdout)
replace_stdout.append((lambda: self.srv.findConnectionUuid('ethernet'), 'UUID-ethernet-REPLACED-REPLACED-REPL'))
self.call_nmcli(['c', 'add', 'type', 'ethernet', 'ifname', '*'],
replace_stdout = replace_stdout)
self.call_nmcli(['c', 's'],
replace_stdout = replace_stdout)
self.call_nmcli(['c', 's'], lang = 'pl',
replace_stdout = replace_stdout)
self.call_nmcli(['-f', 'ALL', 'c', 's'],
replace_stdout = replace_stdout)
self.call_nmcli(['-f', 'ALL', 'c', 's'], lang = 'pl',
replace_stdout = replace_stdout)
self.call_nmcli(['--complete-args', '-f', 'ALL', 'c', 's', ''], lang = 'pl',
replace_stdout = replace_stdout,
sort_lines_stdout = True)
###############################################################################
def main():
global dbus_session_inited
if len(sys.argv) >= 2 and sys.argv[1] == '--started-with-dbus-session':
dbus_session_inited = True
del sys.argv[1]
if not dbus_session_inited:
# we don't have yet our own dbus-session. Reexec ourself with
# a new dbus-session.
try:
try:
os.execlp('dbus-run-session', 'dbus-run-session', '--', sys.executable, __file__, '--started-with-dbus-session', *sys.argv[1:])
except OSError as e:
if e.errno != errno.ENOENT:
raise
# we have no dbus-run-session in path? Fall-through
# to skip tests gracefully
else:
raise Exception('unknown error during exec')
except Exception as e:
assert False, ("Failure to re-exec dbus-run-session: %s" % (str(e)))
if not dbus_session_inited:
# we still don't have a D-Bus session. Probably dbus-run-session is not available.
# retry with dbus-launch
if os.system('type dbus-launch 1>/dev/null') == 0:
try:
os.execlp('bash', 'bash', '-e', '-c',
'eval `dbus-launch --sh-syntax`;\n' + \
'trap "kill $DBUS_SESSION_BUS_PID" EXIT;\n' + \
'\n' + \
' '.join([Util.quote(a) for a in [sys.executable, __file__, '--started-with-dbus-session'] + sys.argv[1:]]) + ' \n' + \
'')
except Exception as e:
m = str(e)
else:
m = 'unknown error'
assert False, ('Failure to re-exec to start script with dbus-launch: %s' % (m))
unittest.main()
if __name__ == '__main__':
main()

View File

@ -110,6 +110,7 @@ typedef struct {
GDBusObjectManager *object_manager;
GCancellable *new_object_manager_cancellable;
struct udev *udev;
bool udev_inited:1;
} NMClientPrivate;
enum {
@ -2603,9 +2604,14 @@ obj_nm_for_gdbus_object (NMClient *self, GDBusObject *object, GDBusObjectManager
NULL);
if (NM_IS_DEVICE (obj_nm)) {
priv = NM_CLIENT_GET_PRIVATE (self);
if (!priv->udev)
priv->udev = udev_new ();
_nm_device_set_udev (NM_DEVICE (obj_nm), priv->udev);
if (G_UNLIKELY (!priv->udev_inited)) {
priv->udev_inited = TRUE;
/* for testing, we don't want to use udev in libnm. */
if (!nm_streq0 (g_getenv ("LIBNM_USE_NO_UDEV"), "1"))
priv->udev = udev_new ();
}
if (priv->udev)
_nm_device_set_udev (NM_DEVICE (obj_nm), priv->udev);
}
g_object_set_qdata_full (G_OBJECT (object), _nm_object_obj_nm_quark (),
obj_nm, g_object_unref);

View File

@ -21,6 +21,40 @@ import dbus.mainloop.glib
import random
import collections
import uuid
import hashlib
#########################################################
class TestError(AssertionError):
def __init__(self, message = 'Unspecified error', errors = None):
AssertionError.__init__(self, message)
self.errors = errors
def pseudorandom_stream(seed, length = None):
seed = str(seed)
v = None
i = 0
while length is None or length > 0:
if not v:
s = seed + str(i)
s = s.encode('utf8')
v = hashlib.sha256(s).hexdigest()
i += 1
yield int(v[0:2], 16)
v = v[2:]
if length is not None:
length -= 1
def pseudorandom_num(seed, v_end, v_start = 0):
n = 0
span = v_end - v_start
for r in pseudorandom_stream(seed):
n = n * 256 + r
if n > span:
break
return v_start + (n % span)
#########################################################
mainloop = GLib.MainLoop()
@ -75,6 +109,7 @@ NM_ACTIVE_CONNECTION_STATE_DEACTIVATING = 3
NM_ACTIVE_CONNECTION_STATE_DEACTIVATED = 4
#########################################################
IFACE_DBUS = 'org.freedesktop.DBus'
class UnknownInterfaceException(dbus.DBusException):
@ -98,9 +133,21 @@ class ExportedObj(dbus.service.Object):
DBusInterface = collections.namedtuple('DBusInterface', ['dbus_iface', 'get_props_func', 'prop_changed_func'])
def __init__(self, bus, object_path):
def __init__(self, bus, object_path, ident = None):
dbus.service.Object.__init__(self, bus, object_path)
self._bus = bus
# ident is an optional (unique) identifier for the instance.
# The test driver may set it to reference to the object by
# this identifier. For NetworkManager, the real ID of an
# object on D-Bus is the object_path. But that is generated
# by the stub server only after the test user created the
# object. The ident parameter may be specified by the user
# and thus can be hard-coded in the test.
if ident is None:
ident = object_path
self.ident = ident
self.path = object_path
self.__ensure_dbus_ifaces()
object_manager.add_object(self)
@ -176,7 +223,11 @@ PD_AVAILABLE_CONNECTIONS = "AvailableConnections"
class Device(ExportedObj):
counter = 1
def __init__(self, bus, iface, devtype):
def __init__(self, bus, iface, devtype, ident = None):
if ident is None:
ident = iface
object_path = "/org/freedesktop/NetworkManager/Devices/%d" % Device.counter
Device.counter = Device.counter + 1
@ -192,7 +243,7 @@ class Device(ExportedObj):
self.available_connections = []
self.add_dbus_interface(IFACE_DEVICE, self.__get_props, Device.PropertiesChanged)
ExportedObj.__init__(self, bus, object_path)
ExportedObj.__init__(self, bus, object_path, ident)
# Properties interface
def __get_props(self):
@ -236,11 +287,12 @@ class Device(ExportedObj):
###################################################################
def random_mac():
return '%02X:%02X:%02X:%02X:%02X:%02X' % (
random.randint(0, 255), random.randint(0, 255), random.randint(0, 255),
random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
)
def random_mac(seed = None):
if seed is None:
r = tuple([random.randint(0, 255) for x in range(6)])
else:
r = tuple(pseudorandom_stream(seed, 6))
return '%02X:%02X:%02X:%02X:%02X:%02X' % r
###################################################################
IFACE_WIRED = 'org.freedesktop.NetworkManager.Device.Wired'
@ -252,17 +304,17 @@ PE_CARRIER = "Carrier"
PE_S390_SUBCHANNELS = "S390Subchannels"
class WiredDevice(Device):
def __init__(self, bus, iface, mac, subchannels):
def __init__(self, bus, iface, mac = None, subchannels = None, ident = None):
if mac is None:
self.mac = random_mac()
else:
self.mac = mac
mac = random_mac(iface if ident is None else ident)
if subchannels is None:
subchannels = dbus.Array(signature = 's')
self.mac = mac
self.carrier = False
self.s390_subchannels = subchannels
self.add_dbus_interface(IFACE_WIRED, self.__get_props, WiredDevice.PropertiesChanged)
Device.__init__(self, bus, iface, NM_DEVICE_TYPE_ETHERNET)
Device.__init__(self, bus, iface, NM_DEVICE_TYPE_ETHERNET, ident)
# Properties interface
def __get_props(self):
@ -289,13 +341,13 @@ PV_CARRIER = "Carrier"
PV_VLAN_ID = "VlanId"
class VlanDevice(Device):
def __init__(self, bus, iface):
self.mac = random_mac()
def __init__(self, bus, iface, ident = None):
self.mac = random_mac(iface if ident is None else ident)
self.carrier = False
self.vlan_id = 1
self.add_dbus_interface(IFACE_VLAN, self.__get_props, VlanDevice.PropertiesChanged)
Device.__init__(self, bus, iface, NM_DEVICE_TYPE_VLAN)
Device.__init__(self, bus, iface, NM_DEVICE_TYPE_VLAN, ident)
# Properties interface
def __get_props(self):
@ -325,24 +377,35 @@ PP_STRENGTH = "Strength"
class WifiAp(ExportedObj):
counter = 0
def __init__(self, bus, ssid, mac, flags, wpaf, rsnf, freq):
def __init__(self, bus, ssid, bssid = None, flags = None, wpaf = None, rsnf = None, freq = None, strength = None, ident = None):
path = "/org/freedesktop/NetworkManager/AccessPoint/%d" % WifiAp.counter
WifiAp.counter = WifiAp.counter + 1
if flags is None:
flags = 0x1
if wpaf is None:
wpaf = 0x1cc
if rsnf is None:
rsnf = 0x1cc
if freq is None:
freq = 2412
if bssid is None:
bssid = random_mac(path)
if strength is None:
strength = pseudorandom_num(path, 100)
self.ssid = ssid
if mac:
self.bssid = mac
else:
self.bssid = random_mac()
self.bssid = bssid
self.flags = flags
self.wpaf = wpaf
self.rsnf = rsnf
self.freq = freq
self.strength = random.randint(0, 100)
self.strength = strength
self.strength_counter = 0
self.strength_id = GLib.timeout_add_seconds(10, self.strength_cb, None)
self.add_dbus_interface(IFACE_WIFI_AP, self.__get_props, WifiAp.PropertiesChanged)
ExportedObj.__init__(self, bus, path)
ExportedObj.__init__(self, bus, path, ident)
def __del__(self):
if self.strength_id > 0:
@ -350,7 +413,8 @@ class WifiAp(ExportedObj):
self.strength_id = 0
def strength_cb(self, ignored):
self.strength = random.randint(0, 100)
self.strength_counter += 1
self.strength = pseudorandom_num(self.path + str(self.strength_counter), 100)
self.__notify(PP_STRENGTH)
return True
@ -390,13 +454,15 @@ PW_ACTIVE_ACCESS_POINT = "ActiveAccessPoint"
PW_WIRELESS_CAPABILITIES = "WirelessCapabilities"
class WifiDevice(Device):
def __init__(self, bus, iface):
self.mac = random_mac()
def __init__(self, bus, iface, mac = None, ident = None):
if mac is None:
mac = random_mac(iface if ident is None else ident)
self.mac = mac
self.aps = []
self.active_ap = None
self.add_dbus_interface(IFACE_WIFI, self.__get_props, WifiDevice.PropertiesChanged)
Device.__init__(self, bus, iface, NM_DEVICE_TYPE_WIFI)
Device.__init__(self, bus, iface, NM_DEVICE_TYPE_WIFI, ident)
# methods
@dbus.service.method(dbus_interface=IFACE_WIFI, in_signature='', out_signature='ao')
@ -421,6 +487,7 @@ class WifiDevice(Device):
self.aps.append(ap)
self.__notify(PW_ACCESS_POINTS)
self.AccessPointAdded(to_path(ap))
return ap
@dbus.service.signal(IFACE_WIFI, signature='o')
def AccessPointRemoved(self, ap_path):
@ -450,12 +517,6 @@ class WifiDevice(Device):
def PropertiesChanged(self, changed):
pass
# test functions
def add_test_ap(self, ssid, mac):
ap = WifiAp(self._bus, ssid, mac, 0x1, 0x1cc, 0x1cc, 2412)
self.add_ap(ap)
return ap
def remove_ap_by_path(self, path):
for ap in self.aps:
if ap.path == path:
@ -526,14 +587,14 @@ PX_BSID = "Bsid"
PX_ACTIVE_NSP = "ActiveNsp"
class WimaxDevice(Device):
def __init__(self, bus, iface):
self.mac = random_mac()
self.bsid = random_mac()
def __init__(self, bus, iface, ident = None):
self.mac = random_mac(iface if ident is None else ident)
self.bsid = random_mac(iface if ident is None else ident)
self.nsps = []
self.active_nsp = None
self.add_dbus_interface(IFACE_WIMAX, self.__get_props, WimaxDevice.PropertiesChanged)
Device.__init__(self, bus, iface, NM_DEVICE_TYPE_WIMAX)
Device.__init__(self, bus, iface, NM_DEVICE_TYPE_WIMAX, ident)
# methods
@dbus.service.method(dbus_interface=IFACE_WIMAX, in_signature='', out_signature='ao')
@ -841,11 +902,20 @@ class NetworkManager(ExportedObj):
def DeviceAdded(self, devpath):
pass
def find_device(self, ident):
for d in self.devices:
if d.ident == ident:
return d
def add_device(self, device):
d = self.find_device(device.ident)
if d:
raise TestError("Device with ident=%s already added (%s)" % (device.ident, d.path))
self.devices.append(device)
self.__notify(PM_DEVICES)
self.__notify(PM_ALL_DEVICES)
self.DeviceAdded(to_path(device))
return device
@dbus.service.signal(IFACE_NM, signature='o')
def DeviceRemoved(self, devpath):
@ -890,32 +960,43 @@ class NetworkManager(ExportedObj):
def Quit(self):
mainloop.quit()
@dbus.service.method(IFACE_TEST, in_signature='a{ss}', out_signature='a(sss)')
def FindConnections(self, args):
return [(c.path, c.get_uuid(), c.get_id()) for c in settings.find_connections(**args)]
@dbus.service.method(IFACE_TEST, in_signature='sa{sv}', out_signature='o')
def AddObj(self, class_name, args):
if class_name in ['WiredDevice', 'WifiDevice']:
py_class = globals()[class_name]
d = py_class(self._bus, **args)
return to_path(self.add_device(d))
elif class_name in ['WifiAp']:
if 'device' not in args:
raise TestError('missing "device" paramter')
d = self.find_device(args['device'])
if not d:
raise TestError('no device "%s" found' % args['device'])
del args['device']
if 'ssid' not in args:
args['ssid'] = d.ident + '-ap-' + str(WifiAp.counter + 1)
ap = WifiAp(self._bus, **args)
return to_path(d.add_ap(ap))
raise TestError("Invalid python type \"%s\"" % (class_name))
@dbus.service.method(IFACE_TEST, in_signature='ssas', out_signature='o')
def AddWiredDevice(self, ifname, mac, subchannels):
for d in self.devices:
if d.iface == ifname:
raise PermissionDeniedException("Device already added")
dev = WiredDevice(self._bus, ifname, mac, subchannels)
self.add_device(dev)
return to_path(dev)
return to_path(self.add_device(dev))
@dbus.service.method(IFACE_TEST, in_signature='s', out_signature='o')
def AddWifiDevice(self, ifname):
for d in self.devices:
if d.iface == ifname:
raise PermissionDeniedException("Device already added")
dev = WifiDevice(self._bus, ifname)
self.add_device(dev)
return to_path(dev)
return to_path(self.add_device(dev))
@dbus.service.method(IFACE_TEST, in_signature='s', out_signature='o')
def AddWimaxDevice(self, ifname):
for d in self.devices:
if d.iface == ifname:
raise PermissionDeniedException("Device already added")
dev = WimaxDevice(self._bus, ifname)
self.add_device(dev)
return to_path(dev)
return to_path(self.add_device(dev))
@dbus.service.method(IFACE_TEST, in_signature='o', out_signature='')
def RemoveDevice(self, path):
@ -926,10 +1007,11 @@ class NetworkManager(ExportedObj):
raise UnknownDeviceException("Device not found")
@dbus.service.method(IFACE_TEST, in_signature='sss', out_signature='o')
def AddWifiAp(self, ifname, ssid, mac):
for d in self.devices:
if d.iface == ifname:
return to_path(d.add_test_ap(ssid, mac))
def AddWifiAp(self, ifname, ssid, bssid):
d = self.find_device(ifname)
if d:
ap = WifiAp(self._bus, ssid, bssid)
return to_path(d.add_ap(ap))
raise UnknownDeviceException("Device not found")
@dbus.service.method(IFACE_TEST, in_signature='so', out_signature='')
@ -989,15 +1071,19 @@ class MissingSettingException(dbus.DBusException):
_dbus_error_name = IFACE_CONNECTION + '.MissingSetting'
class Connection(ExportedObj):
def __init__(self, bus, object_path, settings, remove_func, verify_connection=True):
def __init__(self, bus, path_counter, settings, remove_func, verify_connection=True):
path = "/org/freedesktop/NetworkManager/Settings/Connection/%s" % (path_counter)
if 'connection' not in settings:
settings['connection'] = { }
if self.get_id(settings) is None:
settings['connection']['id'] = 'connection-%s' % (path_counter)
if self.get_uuid(settings) is None:
if 'connection' not in settings:
settings['connection'] = { }
settings['connection']['uuid'] = uuid.uuid4()
settings['connection']['uuid'] = str(uuid.uuid3(uuid.NAMESPACE_URL, path))
self.verify(settings, verify_strict=verify_connection)
self.path = object_path
self.path = path
self.settings = settings
self.remove_func = remove_func
self.visible = True
@ -1005,7 +1091,16 @@ class Connection(ExportedObj):
self.props['Unsaved'] = False
self.add_dbus_interface(IFACE_CONNECTION, self.__get_props, None)
ExportedObj.__init__(self, bus, object_path)
ExportedObj.__init__(self, bus, path)
def get_id(self, settings=None):
if settings is None:
settings = self.settings
if 'connection' in settings:
s_con = settings['connection']
if 'id' in s_con:
return s_con['id']
return None
def get_uuid(self, settings=None):
if settings is None:
@ -1092,7 +1187,7 @@ class Settings(ExportedObj):
def __init__(self, bus, object_path):
self.connections = {}
self.bus = bus
self.counter = 1
self.counter = 0
self.remove_next_connection = False
self.props = {}
self.props['Hostname'] = "foobar.baz"
@ -1108,6 +1203,19 @@ class Settings(ExportedObj):
def get_connection(self, path):
return self.connections[path]
def find_connections(self, path = None, con_id = None, con_uuid = None):
for c in self.connections.values():
if path is not None:
if c.path != path:
continue
if con_id is not None:
if c.get_id() != con_id:
continue
if con_uuid is not None:
if c.get_uuid() != con_uuid:
continue
yield c
@dbus.service.method(dbus_interface=IFACE_SETTINGS, in_signature='', out_signature='ao')
def ListConnections(self):
return self.connections.keys()
@ -1117,24 +1225,23 @@ class Settings(ExportedObj):
return self.add_connection(settings)
def add_connection(self, settings, verify_connection=True):
path = "/org/freedesktop/NetworkManager/Settings/Connection/{0}".format(self.counter)
con = Connection(self.bus, path, settings, self.delete_connection, verify_connection)
self.counter += 1
con = Connection(self.bus, self.counter, settings, self.delete_connection, verify_connection)
uuid = con.get_uuid()
if uuid in [c.get_uuid() for c in self.connections.values()]:
raise InvalidSettingException('cannot add duplicate connection with uuid %s' % (uuid))
self.counter = self.counter + 1
self.connections[path] = con
self.connections[con.path] = con
self.props['Connections'] = dbus.Array(self.connections.keys(), 'o')
self.NewConnection(path)
self.NewConnection(con.path)
self.__notify('Connections')
if self.remove_next_connection:
self.remove_next_connection = False
self.connections[path].Delete()
self.connections[con.path].Delete()
return path
return con.path
def update_connection(self, connection, path=None, verify_connection=True):
if path is None: