summary refs log tree commit diff
path: root/clients/tests/test-client.py
diff options
context:
space:
mode:
Diffstat (limited to 'clients/tests/test-client.py')
-rwxr-xr-xclients/tests/test-client.py320
1 files changed, 264 insertions, 56 deletions
diff --git a/clients/tests/test-client.py b/clients/tests/test-client.py
index a8e107a0..74225ccf 100755
--- a/clients/tests/test-client.py
+++ b/clients/tests/test-client.py
@@ -2,6 +2,68 @@
 
 from __future__ import print_function
 
+###############################################################################
+#
+# This test starts NetworkManager stub service in a user D-Bus session,
+# and runs nmcli against it. The output is recorded and compared to a pre-generated
+# expected output (clients/tests/test-client.check-on-disk/*.expected) which
+# is also commited to git.
+#
+###############################################################################
+#
+# HOWTO: Regenerate output
+#
+# When adjusting the tests, or when making changes to nmcli that intentionally
+# change the output, the expected output must be regenerated.
+#
+#  $ make install
+#    # (step not required every time)
+#    # The test also compare the translated output, hence, the translation
+#    # file must be installed at the configured --prefix.
+#    # You don't need to type `make install` every time, but a suitable version
+#    # of translations must be installed. In practice, the tests only care about
+#    # Polish (pl) translations.
+#    # The important part is that translations work. Test
+#    #  $ LANG=pl_PL.UTF-8 ./clients/cli/nmcli --version
+#    # also ensure that `locale -a` reports the Polish locale.
+#  $ rm -rf  clients/tests/test-client.check-on-disk/*.expected
+#    # (step seldomly required)
+#    # Sometimes, if you want to be sure that the test would generate
+#    # exactly the same .expected files, purge the previous version first.
+#    # This is only necessary, when you remove test from this file.
+#  $ NM_TEST_REGENERATE=1 make check-local-clients-tests-test-client
+#    # Set NM_TEST_REGENERATE=1 to regenerate all files.
+#  $ git diff ... ; git add ...
+#    # (optional step)
+#    # Inspect what changed, and whether it makes sense. Then commit changes
+#    # to git.
+#
+###############################################################################
+#
+# Environment variables to configure test:
+
+# (optional) The build dir. Optional, mainly used to find the nmcli binary (in case
+# ENV_NM_TEST_CLIENT_NMCLI_PATH is not set.
+ENV_NM_TEST_CLIENT_BUILDDIR   = 'NM_TEST_CLIENT_BUILDDIR'
+
+# (optional) Path to nmcli. By default, it looks for nmcli in build dir.
+# In particular, you can test also a nmcli binary installed somewhere else.
+ENV_NM_TEST_CLIENT_NMCLI_PATH = 'NM_TEST_CLIENT_NMCLI_PATH'
+
+# (optional) The test also compares tranlsated output (l10n). This requires,
+# that you first install the translation in the right place. So, by default,
+# if a test for a translation fails, it will mark the test as skipped, and not
+# fail the tests. Under the assumption, that the test cannot succeed currently.
+# By setting NM_TEST_CLIENT_CHECK_L10N=1, you can force a failure of the test.
+ENV_NM_TEST_CLIENT_CHECK_L10N = 'NM_TEST_CLIENT_CHECK_L10N'
+
+# Regenerate the .expected files. Instead of asserting, rewrite the files
+# on disk with the expected output.
+ENV_NM_TEST_REGENERATE        = 'NM_TEST_REGENERATE'
+
+#
+###############################################################################
+
 import sys
 
 try:
@@ -27,12 +89,6 @@ 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:
@@ -54,14 +110,18 @@ class PathConfiguration:
         assert os.path.exists(v), ("Cannot find test server at \"%s\"" % (v))
         return v
 
-###############################################################################
+    @staticmethod
+    def canonical_script_filename():
+        p = 'clients/tests/test-client.py'
+        assert (PathConfiguration.top_srcdir() + '/' + p) == os.path.abspath(__file__)
+        return p
 
-os.sys.path.append(os.path.abspath(PathConfiguration.top_srcdir() + '/examples/python'))
-import nmex
+###############################################################################
 
 dbus_session_inited = False
 
 _DEFAULT_ARG = object()
+_UNSTABLE_OUTPUT = object()
 
 ###############################################################################
 
@@ -101,11 +161,11 @@ class Util:
             return p.wait(timeout)
         if timeout is None:
             return p.wait()
-        start = nmex.nm_boot_time_ns()
+        start = NM.utils_get_timestamp_msec()
         while True:
             if p.poll() is not None:
                 return p.returncode
-            if start + (timeout * 1000000000) < nmex.nm_boot_time_ns():
+            if start + (timeout * 1000) < NM.utils_get_timestamp_msec():
                 raise Exception("timeout expired")
             time.sleep(0.05)
 
@@ -162,6 +222,21 @@ class Util:
             text = text2
         return b''.join([(t[0] if isinstance(t, tuple) else t) for t in text])
 
+    @staticmethod
+    def debug_dbus_interface():
+        # this is for printf debugging, not used in actual code.
+        os.system('busctl --user --verbose call org.freedesktop.NetworkManager /org/freedesktop org.freedesktop.DBus.ObjectManager GetManagedObjects | cat')
+
+    @staticmethod
+    def iter_nmcli_output_modes():
+        for mode in [[],
+                     ['--mode', 'tabular'],
+                     ['--mode', 'multiline']]:
+            for fmt in [[],
+                        ['--pretty'],
+                        ['--terse']]:
+                yield mode + fmt
+
 ###############################################################################
 
 class Configuration:
@@ -219,13 +294,16 @@ class NMStubServer:
         except:
             return None
 
-    def __init__(self):
+    def __init__(self, seed):
         service_path = PathConfiguration.test_networkmanager_service_path()
         self._conn = dbus.SessionBus()
+        env = os.environ.copy()
+        env['NM_TEST_NETWORKMANAGER_SERVICE_SEED'] = seed
         p = subprocess.Popen([sys.executable, service_path],
-                             stdin = subprocess.PIPE)
+                             stdin = subprocess.PIPE,
+                             env = env)
 
-        start = nmex.nm_boot_time_ns()
+        start = NM.utils_get_timestamp_msec()
         while True:
             if p.poll() is not None:
                 p.stdin.close()
@@ -235,7 +313,7 @@ class NMStubServer:
             nmobj = self._conn_get_main_object(self._conn)
             if nmobj is not None:
                 break
-            if (nmex.nm_boot_time_ns() - start) / 1000000 >= 2000:
+            if (NM.utils_get_timestamp_msec() - start) >= 2000:
                 p.stdin.close()
                 p.kill()
                 Util.popen_wait(p, 1000)
@@ -280,14 +358,16 @@ class NMStubServer:
             raise AttributeError(member)
         return self._MethodProxy(self, member[3:])
 
-    def addConnection(self, connection, verify_connection = True):
-        return self.op_AddConnection(connection, verify_connection)
+    def addConnection(self, connection, do_verify_strict = True):
+        return self.op_AddConnection(connection, do_verify_strict)
 
-    def findConnectionUuid(self, con_id):
+    def findConnectionUuid(self, con_id, required = True):
         try:
             u = Util.iter_single(self.op_FindConnections(con_id = con_id))[1]
             assert u, ("Invalid uuid %s" % (u))
         except Exception as e:
+            if not required:
+                return None
             raise AssertionError("Unexpectedly not found connection %s: %s" % (con_id, str(e)))
         return u
 
@@ -347,6 +427,7 @@ class TestNmcli(NmTestBase):
     def call_nmcli_l(self,
                      args,
                      check_on_disk = _DEFAULT_ARG,
+                     fatal_warnings = _DEFAULT_ARG,
                      expected_returncode = _DEFAULT_ARG,
                      expected_stdout = _DEFAULT_ARG,
                      expected_stderr = _DEFAULT_ARG,
@@ -360,6 +441,7 @@ class TestNmcli(NmTestBase):
             self._call_nmcli(args,
                              lang,
                              check_on_disk,
+                             fatal_warnings,
                              expected_returncode,
                              expected_stdout,
                              expected_stderr,
@@ -376,6 +458,7 @@ class TestNmcli(NmTestBase):
                    langs = None,
                    lang = None,
                    check_on_disk = _DEFAULT_ARG,
+                   fatal_warnings = _DEFAULT_ARG,
                    expected_returncode = _DEFAULT_ARG,
                    expected_stdout = _DEFAULT_ARG,
                    expected_stderr = _DEFAULT_ARG,
@@ -401,6 +484,7 @@ class TestNmcli(NmTestBase):
             self._call_nmcli(args,
                              lang,
                              check_on_disk,
+                             fatal_warnings,
                              expected_returncode,
                              expected_stdout,
                              expected_stderr,
@@ -415,6 +499,7 @@ class TestNmcli(NmTestBase):
                     args,
                     lang,
                     check_on_disk,
+                    fatal_warnings,
                     expected_returncode,
                     expected_stdout,
                     expected_stderr,
@@ -437,10 +522,9 @@ class TestNmcli(NmTestBase):
         # 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))
+        self.assertTrue(os.path.abspath(frame.f_code.co_filename).endswith('/'+PathConfiguration.canonical_script_filename()))
 
-        calling_location = '%s:%d:%s()/%d' % (script_filename, frame.f_lineno, frame.f_code.co_name, calling_num)
+        calling_location = '%s:%d:%s()/%d' % (PathConfiguration.canonical_script_filename(), frame.f_lineno, frame.f_code.co_name, calling_num)
 
         if lang is None or lang == 'C':
             lang = 'C'
@@ -468,6 +552,8 @@ class TestNmcli(NmTestBase):
         env['LIBNM_USE_SESSION_BUS'] = '1'
         env['LIBNM_USE_NO_UDEV'] = '1'
         env['TERM'] = 'linux'
+        if fatal_warnings is _DEFAULT_ARG or fatal_warnings:
+            env['G_DEBUG'] = 'fatal-warnings'
 
         args = [conf.get(ENV_NM_TEST_CLIENT_NMCLI_PATH)] + list(args)
 
@@ -478,8 +564,8 @@ class TestNmcli(NmTestBase):
 
         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)
+                             and (expected_stdout is _DEFAULT_ARG or expected_stdout is _UNSTABLE_OUTPUT)
+                             and (expected_stderr is _DEFAULT_ARG or expected_stderr is _UNSTABLE_OUTPUT))
         if expected_returncode is _DEFAULT_ARG:
             expected_returncode = None
         if expected_stdout is _DEFAULT_ARG:
@@ -492,8 +578,15 @@ class TestNmcli(NmTestBase):
                         stdout,
                         stderr):
 
-            stdout = Util.replace_text(stdout, replace_stdout)
-            stderr = Util.replace_text(stderr, replace_stderr)
+            if expected_stdout is _UNSTABLE_OUTPUT:
+                stdout = '<UNSTABLE OUTPUT>'.encode('utf-8')
+            else:
+                stdout = Util.replace_text(stdout, replace_stdout)
+
+            if expected_stderr is _UNSTABLE_OUTPUT:
+                stderr = '<UNSTABLE OUTPUT>'.encode('utf-8')
+            else:
+                stderr = Util.replace_text(stderr, replace_stderr)
 
             if sort_lines_stdout:
                 stdout = b'\n'.join(sorted(stdout.split(b'\n')))
@@ -501,13 +594,13 @@ class TestNmcli(NmTestBase):
             ignore_l10n_diff = (    lang != 'C'
                                 and not conf.get(ENV_NM_TEST_CLIENT_CHECK_L10N))
 
-            if expected_stderr is not None:
+            if expected_stderr is not None and expected_stderr is not _UNSTABLE_OUTPUT:
                 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 is not None and expected_stdout is not _UNSTABLE_OUTPUT:
                 if expected_stdout != stdout:
                     if ignore_l10n_diff:
                         self._skip_test_for_l10n_diff.append(test_name)
@@ -516,6 +609,13 @@ class TestNmcli(NmTestBase):
             if expected_returncode is not None:
                 self.assertEqual(expected_returncode, returncode)
 
+            if fatal_warnings is _DEFAULT_ARG:
+                if expected_returncode != -5:
+                    self.assertNotEqual(returncode, -5)
+            elif fatal_warnings:
+                if expected_returncode is None:
+                   self.assertEqual(returncode, -5)
+
             dirname = PathConfiguration.srcdir() + '/test-client.check-on-disk'
             basename = test_name + '.expected'
             filename = os.path.abspath(dirname + '/' + basename)
@@ -552,7 +652,8 @@ class TestNmcli(NmTestBase):
                     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")
+                    print("Let the test write the file by rerunning with NM_TEST_REGENERATE=1")
+                    print("See howto in %s for details.\n" % (PathConfiguration.canonical_script_filename()))
                     raise AssertionError("Unexpected output of command, expected %s. Rerun test with NM_TEST_REGENERATE=1 to regenerate files" % (filename))
             else:
                 if not w:
@@ -579,7 +680,7 @@ class TestNmcli(NmTestBase):
 
     def async_start(self):
         # limit number parallel running jobs
-        for async_job in self._async_jobs[0:10]:
+        for async_job in self._async_jobs[0:15]:
             async_job.start()
 
     def async_wait(self):
@@ -592,7 +693,7 @@ class TestNmcli(NmTestBase):
             self.skipTest("Own D-Bus session for testing is not initialized. Do you have dbus-run-session available?")
         if NM is None:
             self.skipTest("gi.NM is not available. Did you build with introspection?")
-        self.srv = NMStubServer()
+        self.srv = NMStubServer(self._testMethodName)
         self._calling_num = {}
         self._skip_test_for_l10n_diff = []
         self._async_jobs = []
@@ -656,13 +757,8 @@ class TestNmcli(NmTestBase):
 
         self.call_nmcli_l(['bogus', 's'])
 
-        for mode in [[],
-                     ['--mode', 'tabular'],
-                     ['--mode', 'multiline']]:
-            for fmt in [[],
-                        ['--pretty'],
-                        ['--terse']]:
-                self.call_nmcli_l(mode + fmt + ['general', 'permissions'])
+        for mode in Util.iter_nmcli_output_modes():
+            self.call_nmcli_l(mode + ['general', 'permissions'])
 
     def test_002(self):
         self.init_001()
@@ -733,9 +829,21 @@ class TestNmcli(NmTestBase):
             self.call_nmcli_l(['-f', 'ALL', 'con'],
                               replace_stdout = replace_stdout)
 
+            self.call_nmcli_l(['-f', 'ALL', 'con', 's', '-a'],
+                              replace_stdout = replace_stdout)
+
+            self.call_nmcli_l(['-f', 'ACTIVE-PATH,DEVICE,UUID', 'con', 's', '-act'],
+                              replace_stdout = replace_stdout)
+
+            self.call_nmcli_l(['-f', 'UUID,NAME', 'con', 's', '--active'],
+                              replace_stdout = replace_stdout)
+
             self.call_nmcli_l(['-f', 'ALL', 'con', 's', 'ethernet'],
                               replace_stdout = replace_stdout)
 
+            self.call_nmcli_l(['-f', 'GENERAL.STATE', 'con', 's', 'ethernet'],
+                              replace_stdout = replace_stdout)
+
             self.call_nmcli_l(['con', 's', 'ethernet'],
                               replace_stdout = replace_stdout)
 
@@ -745,38 +853,52 @@ class TestNmcli(NmTestBase):
             self.call_nmcli_l(['-f', 'ALL', 'dev', 'show', 'eth0'],
                               replace_stdout = replace_stdout)
 
+            self.call_nmcli_l(['-f', 'ALL', '-t', 'dev', 'show', 'eth0'],
+                              replace_stdout = replace_stdout)
+
         self.async_wait()
 
         self.srv.setProperty('/org/freedesktop/NetworkManager/ActiveConnection/1',
                              'State',
                              dbus.UInt32(NM.ActiveConnectionState.DEACTIVATING))
 
-        self.call_nmcli_l(['-f', 'ALL', 'con'],
-                          replace_stdout = replace_stdout)
+        for i in [0, 1]:
+            if i == 1:
+                self.async_wait()
+                self.srv.op_ConnectionSetVisible(False, con_id = 'ethernet')
 
-        self.call_nmcli_l(['-f', 'UUID,TYPE', 'con'],
-                          replace_stdout = replace_stdout)
+            self.call_nmcli_l(['-f', 'ALL', 'con'],
+                              replace_stdout = replace_stdout)
 
-        self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'multiline', 'con'],
-                          replace_stdout = replace_stdout)
+            self.call_nmcli_l(['-f', 'UUID,TYPE', 'con'],
+                              replace_stdout = replace_stdout)
 
-        self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'multiline', '--terse', 'con'],
-                          replace_stdout = replace_stdout)
+            self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'multiline', 'con'],
+                              replace_stdout = replace_stdout)
 
-        self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'multiline', '--pretty', 'con'],
-                          replace_stdout = replace_stdout)
+            self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'multiline', '--terse', 'con'],
+                              replace_stdout = replace_stdout)
 
-        self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'tabular', 'con'],
-                          replace_stdout = replace_stdout)
+            self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'multiline', '--pretty', 'con'],
+                              replace_stdout = replace_stdout)
 
-        self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'tabular', '--terse', 'con'],
-                          replace_stdout = replace_stdout)
+            self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'tabular', 'con'],
+                              replace_stdout = replace_stdout)
 
-        self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'tabular', '--pretty', 'con'],
-                          replace_stdout = replace_stdout)
+            self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'tabular', '--terse', 'con'],
+                              replace_stdout = replace_stdout)
 
-        self.call_nmcli_l(['con', 's', 'ethernet'],
-                          replace_stdout = replace_stdout)
+            self.call_nmcli_l(['-f', 'UUID,TYPE', '--mode', 'tabular', '--pretty', 'con'],
+                              replace_stdout = replace_stdout)
+
+            self.call_nmcli_l(['con', 's', 'ethernet'],
+                              replace_stdout = replace_stdout)
+
+            self.call_nmcli_l(['c', 's', '/org/freedesktop/NetworkManager/ActiveConnection/1'],
+                              replace_stdout = replace_stdout)
+
+            self.call_nmcli_l(['-f', 'all', 'dev', 'show', 'eth0'],
+                              replace_stdout = replace_stdout)
 
     def test_004(self):
         self.init_001()
@@ -789,10 +911,95 @@ class TestNmcli(NmTestBase):
                         replace_stdout = replace_stdout)
 
         self.call_nmcli(['connection', 'mod', 'con-xx1', 'ip.gateway', ''])
-        self.call_nmcli(['connection', 'mod', 'con-xx1', 'ipv4.gateway', '172.16.0.1'])
+        self.call_nmcli(['connection', 'mod', 'con-xx1', 'ipv4.gateway', '172.16.0.1'], lang = 'pl')
         self.call_nmcli(['connection', 'mod', 'con-xx1', 'ipv6.gateway', '::99'])
         self.call_nmcli(['connection', 'mod', 'con-xx1', '802.abc', ''])
         self.call_nmcli(['connection', 'mod', 'con-xx1', '802-11-wireless.band', 'a'])
+        self.call_nmcli(['connection', 'mod', 'con-xx1', 'ipv4.addresses', '192.168.77.5/24', 'ipv4.routes', '2.3.4.5/32 192.168.77.1', 'ipv6.addresses', '1:2:3:4::6/64', 'ipv6.routes', '1:2:3:4:5:6::5/128'])
+        self.call_nmcli_l(['con', 's', 'con-xx1'],
+                          replace_stdout = replace_stdout)
+
+        self.async_wait()
+
+        replace_stdout.append((lambda: self.srv.findConnectionUuid('con-vpn-1'), 'UUID-con-vpn-1-REPLACED-REPLACED-REP'))
+
+        self.call_nmcli(['connection', 'add', 'type', 'vpn', 'con-name', 'con-vpn-1', 'ifname', '*', 'vpn-type', 'openvpn', 'vpn.data', 'key1 = val1,   key2  = val2, key3=val3'],
+                        replace_stdout = replace_stdout)
+
+        self.call_nmcli_l(['con', 's'],
+                          replace_stdout = replace_stdout)
+        self.call_nmcli_l(['con', 's', 'con-vpn-1'],
+                          replace_stdout = replace_stdout)
+
+        self.call_nmcli(['con', 'up', 'con-xx1'])
+        self.call_nmcli_l(['con', 's'],
+                          replace_stdout = replace_stdout)
+
+        self.call_nmcli(['con', 'up', 'con-vpn-1'])
+        self.call_nmcli_l(['con', 's'],
+                          replace_stdout = replace_stdout)
+        self.call_nmcli_l(['con', 's', 'con-vpn-1'],
+                          replace_stdout = replace_stdout)
+
+        self.async_wait()
+
+        self.srv.setProperty('/org/freedesktop/NetworkManager/ActiveConnection/2',
+                             'VpnState',
+                             dbus.UInt32(NM.VpnConnectionState.ACTIVATED))
+
+        self.call_nmcli_l(['con', 's', 'con-vpn-1'],
+                          replace_stdout = replace_stdout)
+        self.call_nmcli_l(['-t', 'con', 's', 'con-vpn-1'],
+                          replace_stdout = replace_stdout)
+
+        self.call_nmcli_l(['-f', 'ALL', 'con', 's', 'con-vpn-1'],
+                          replace_stdout = replace_stdout)
+
+        # This only filters 'vpn' settings from the connection profile.
+        # Contrary to '-f GENERAL' below, it does not show the properties of
+        # the activated VPN connection. This is a nmcli bug.
+        self.call_nmcli_l(['-f', 'VPN', 'con', 's', 'con-vpn-1'],
+                          replace_stdout = replace_stdout)
+
+        self.call_nmcli_l(['-f', 'GENERAL', 'con', 's', 'con-vpn-1'],
+                          replace_stdout = replace_stdout)
+
+        self.call_nmcli_l(['dev', 'show', 'wlan0'],
+                          replace_stdout = replace_stdout)
+
+        self.call_nmcli_l(['-f', 'all', 'dev', 'show', 'wlan0'],
+                          replace_stdout = replace_stdout)
+
+        self.call_nmcli_l(['-f', 'GENERAL,GENERAL.HWADDR,WIFI-PROPERTIES', 'dev', 'show', 'wlan0'],
+                          replace_stdout = replace_stdout)
+
+        self.call_nmcli_l(['-f', 'GENERAL,GENERAL.HWADDR,WIFI-PROPERTIES', '-t', 'dev', 'show', 'wlan0'],
+                          replace_stdout = replace_stdout)
+
+        self.call_nmcli_l(['-f', 'DEVICE,TYPE,DBUS-PATH', 'dev'],
+                          replace_stdout = replace_stdout)
+
+        for mode in Util.iter_nmcli_output_modes():
+             self.call_nmcli_l(mode + ['-f', 'ALL', 'device', 'wifi', 'list' ],
+                               replace_stdout = replace_stdout)
+             self.call_nmcli_l(mode + ['-f', 'COMMON', 'device', 'wifi', 'list' ],
+                               replace_stdout = replace_stdout)
+             self.call_nmcli_l(mode + ['-f', 'NAME,SSID,SSID-HEX,BSSID,MODE,CHAN,FREQ,RATE,SIGNAL,BARS,SECURITY,WPA-FLAGS,RSN-FLAGS,DEVICE,ACTIVE,IN-USE,DBUS-PATH',
+                               'device', 'wifi', 'list'],
+                               replace_stdout = replace_stdout)
+             self.call_nmcli_l(mode + ['-f', 'ALL', 'device', 'wifi', 'list', 'bssid', 'C0:E2:BE:E8:EF:B6'],
+                               replace_stdout = replace_stdout)
+             self.call_nmcli_l(mode + ['-f', 'COMMON', 'device', 'wifi', 'list', 'bssid', 'C0:E2:BE:E8:EF:B6'],
+                               replace_stdout = replace_stdout)
+             self.call_nmcli_l(mode + ['-f', 'NAME,SSID,SSID-HEX,BSSID,MODE,CHAN,FREQ,RATE,SIGNAL,BARS,SECURITY,WPA-FLAGS,RSN-FLAGS,DEVICE,ACTIVE,IN-USE,DBUS-PATH',
+                               'device', 'wifi', 'list', 'bssid', 'C0:E2:BE:E8:EF:B6'],
+                               replace_stdout = replace_stdout)
+             self.call_nmcli_l(mode + ['-f', 'ALL', 'device', 'show', 'wlan0' ],
+                               replace_stdout = replace_stdout)
+             self.call_nmcli_l(mode + ['-f', 'COMMON', 'device', 'show', 'wlan0' ],
+                               replace_stdout = replace_stdout)
+             self.call_nmcli_l(mode + ['-f', 'GENERAL,CAPABILITIES,WIFI-PROPERTIES,AP,WIRED-PROPERTIES,WIMAX-PROPERTIES,NSP,IP4,DHCP4,IP6,DHCP6,BOND,TEAM,BRIDGE,VLAN,BLUETOOTH,CONNECTIONS', 'device', 'show', 'wlan0' ],
+                               replace_stdout = replace_stdout)
 
 ###############################################################################
 
@@ -841,6 +1048,7 @@ def main():
     if conf.get(ENV_NM_TEST_REGENERATE):
         make_filename = PathConfiguration.srcdir() + '/test-client.check-on-disk/Makefile.am'
         s_new = '# generated with `NM_TEST_REGENERATE=1 make check`\n' + \
+                '# See howto in "' + PathConfiguration.canonical_script_filename() + '"\n' + \
                 '\n' + \
                 'clients_tests_expected_files = \\\n' + \
                 ''.join([('\tclients/tests/test-client.check-on-disk/%s \\\n' % f) for f in sorted(file_list)]) + \