Python queue, 2018-06-15
* Add avocado_qemu: functional/acceptance test infrastructure -----BEGIN PGP SIGNATURE----- iQIbBAABCAAGBQJbJA+UAAoJECgHk2+YTcWmRTQP920aopf/h3C7zF/af87rs87F 1HrVPnASBPPKPabnohxFAa2MV5NwB4VJlx4kjpxOoCV6v+kb+mV4zYdQe+d2kHvO W9IsCJVTOUxXFK4Xkl76QFTCQklk87LRz227uPjnH6YU6tlbfO/gkfeqm/ua/cIe SSndLQSpSMiMaeryuYaTzpyvrO8l9xD5lbPD0YwKGfEmdjHE5aqg0Q12ecK6Id8H 5PPWE5VEBL3WP9A9vA5lGr352+w9p/es/8Cf88iQu5fAC6fyRiBTAbZuZzyC9ido MjDtNC0HtLrCuuCLKWy5E7zd5KDah8cfYWTf9vVqNUdfY1CzbfmX49u6iADZEc4s O7I4oSHWma+uwNErAWB7ahC9YRjRbM1cuVy76UTllczLn5sP/SNvcdPzjhb8P+Hx YtEk2JhKSCBDmrHrWx0tBkPFoKfKF7z4IyWBYQ4irF0rakfNvKjvkdbnCquoqu5V /nL+CIUx1TW+6E2BXeYNpfERRra/DlFV51TfsSyS0WqBhJyLBLOCsrbHRnQFzAWA a1tjTmPFGRb8fXLqx6gABsNrCAQo339xYswNGWMVP/Flvzw8FHAg4EGnURhu4Uqd bG1Ru1nsRnfVE3WYcTYeMv11ydnHV3ty9XVaTAr9PYF59PxImFH7AuCXC4naj+WX DSso149y3PACsnaaoA8= =hk5m -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/ehabkost/tags/python-next-pull-request' into staging Python queue, 2018-06-15 * Add avocado_qemu: functional/acceptance test infrastructure # gpg: Signature made Fri 15 Jun 2018 20:12:20 BST # gpg: using RSA key 2807936F984DC5A6 # gpg: Good signature from "Eduardo Habkost <ehabkost@redhat.com>" # Primary key fingerprint: 5A32 2FD5 ABC4 D3DB ACCF D1AA 2807 936F 984D C5A6 * remotes/ehabkost/tags/python-next-pull-request: configure: Enable out-of-tree acceptance tests Acceptance tests: add Linux kernel boot and console checking test scripts/qemu.py: introduce set_console() method Acceptance tests: add quick VNC tests scripts/qemu.py: allow adding to the list of extra arguments Add functional/acceptance tests infrastructure Remove COPYING.PYTHON Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
c5ee5cd9db
270
COPYING.PYTHON
270
COPYING.PYTHON
@ -1,270 +0,0 @@
|
|||||||
A. HISTORY OF THE SOFTWARE
|
|
||||||
==========================
|
|
||||||
|
|
||||||
Python was created in the early 1990s by Guido van Rossum at Stichting
|
|
||||||
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
|
|
||||||
as a successor of a language called ABC. Guido remains Python's
|
|
||||||
principal author, although it includes many contributions from others.
|
|
||||||
|
|
||||||
In 1995, Guido continued his work on Python at the Corporation for
|
|
||||||
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
|
|
||||||
in Reston, Virginia where he released several versions of the
|
|
||||||
software.
|
|
||||||
|
|
||||||
In May 2000, Guido and the Python core development team moved to
|
|
||||||
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
|
||||||
year, the PythonLabs team moved to Digital Creations (now Zope
|
|
||||||
Corporation, see http://www.zope.com). In 2001, the Python Software
|
|
||||||
Foundation (PSF, see http://www.python.org/psf/) was formed, a
|
|
||||||
non-profit organization created specifically to own Python-related
|
|
||||||
Intellectual Property. Zope Corporation is a sponsoring member of
|
|
||||||
the PSF.
|
|
||||||
|
|
||||||
All Python releases are Open Source (see http://www.opensource.org for
|
|
||||||
the Open Source Definition). Historically, most, but not all, Python
|
|
||||||
releases have also been GPL-compatible; the table below summarizes
|
|
||||||
the various releases.
|
|
||||||
|
|
||||||
Release Derived Year Owner GPL-
|
|
||||||
from compatible? (1)
|
|
||||||
|
|
||||||
0.9.0 thru 1.2 1991-1995 CWI yes
|
|
||||||
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
|
||||||
1.6 1.5.2 2000 CNRI no
|
|
||||||
2.0 1.6 2000 BeOpen.com no
|
|
||||||
1.6.1 1.6 2001 CNRI yes (2)
|
|
||||||
2.1 2.0+1.6.1 2001 PSF no
|
|
||||||
2.0.1 2.0+1.6.1 2001 PSF yes
|
|
||||||
2.1.1 2.1+2.0.1 2001 PSF yes
|
|
||||||
2.2 2.1.1 2001 PSF yes
|
|
||||||
2.1.2 2.1.1 2002 PSF yes
|
|
||||||
2.1.3 2.1.2 2002 PSF yes
|
|
||||||
2.2.1 2.2 2002 PSF yes
|
|
||||||
2.2.2 2.2.1 2002 PSF yes
|
|
||||||
2.2.3 2.2.2 2003 PSF yes
|
|
||||||
2.3 2.2.2 2002-2003 PSF yes
|
|
||||||
2.3.1 2.3 2002-2003 PSF yes
|
|
||||||
2.3.2 2.3.1 2002-2003 PSF yes
|
|
||||||
2.3.3 2.3.2 2002-2003 PSF yes
|
|
||||||
2.3.4 2.3.3 2004 PSF yes
|
|
||||||
2.3.5 2.3.4 2005 PSF yes
|
|
||||||
2.4 2.3 2004 PSF yes
|
|
||||||
2.4.1 2.4 2005 PSF yes
|
|
||||||
2.4.2 2.4.1 2005 PSF yes
|
|
||||||
2.4.3 2.4.2 2006 PSF yes
|
|
||||||
2.5 2.4 2006 PSF yes
|
|
||||||
2.7 2.6 2010 PSF yes
|
|
||||||
|
|
||||||
Footnotes:
|
|
||||||
|
|
||||||
(1) GPL-compatible doesn't mean that we're distributing Python under
|
|
||||||
the GPL. All Python licenses, unlike the GPL, let you distribute
|
|
||||||
a modified version without making your changes open source. The
|
|
||||||
GPL-compatible licenses make it possible to combine Python with
|
|
||||||
other software that is released under the GPL; the others don't.
|
|
||||||
|
|
||||||
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
|
||||||
because its license has a choice of law clause. According to
|
|
||||||
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
|
||||||
is "not incompatible" with the GPL.
|
|
||||||
|
|
||||||
Thanks to the many outside volunteers who have worked under Guido's
|
|
||||||
direction to make these releases possible.
|
|
||||||
|
|
||||||
|
|
||||||
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
|
||||||
===============================================================
|
|
||||||
|
|
||||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
|
||||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
|
||||||
otherwise using this software ("Python") in source or binary form and
|
|
||||||
its associated documentation.
|
|
||||||
|
|
||||||
2. Subject to the terms and conditions of this License Agreement, PSF
|
|
||||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
|
||||||
license to reproduce, analyze, test, perform and/or display publicly,
|
|
||||||
prepare derivative works, distribute, and otherwise use Python
|
|
||||||
alone or in any derivative version, provided, however, that PSF's
|
|
||||||
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
|
|
||||||
2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights
|
|
||||||
Reserved" are retained in Python alone or in any derivative version
|
|
||||||
prepared by Licensee.
|
|
||||||
|
|
||||||
3. In the event Licensee prepares a derivative work that is based on
|
|
||||||
or incorporates Python or any part thereof, and wants to make
|
|
||||||
the derivative work available to others as provided herein, then
|
|
||||||
Licensee hereby agrees to include in any such work a brief summary of
|
|
||||||
the changes made to Python.
|
|
||||||
|
|
||||||
4. PSF is making Python available to Licensee on an "AS IS"
|
|
||||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
|
||||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
|
||||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
|
||||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
|
||||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
|
||||||
|
|
||||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
|
||||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
|
||||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
|
||||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
|
||||||
|
|
||||||
6. This License Agreement will automatically terminate upon a material
|
|
||||||
breach of its terms and conditions.
|
|
||||||
|
|
||||||
7. Nothing in this License Agreement shall be deemed to create any
|
|
||||||
relationship of agency, partnership, or joint venture between PSF and
|
|
||||||
Licensee. This License Agreement does not grant permission to use PSF
|
|
||||||
trademarks or trade name in a trademark sense to endorse or promote
|
|
||||||
products or services of Licensee, or any third party.
|
|
||||||
|
|
||||||
8. By copying, installing or otherwise using Python, Licensee
|
|
||||||
agrees to be bound by the terms and conditions of this License
|
|
||||||
Agreement.
|
|
||||||
|
|
||||||
|
|
||||||
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
|
||||||
|
|
||||||
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
|
||||||
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
|
||||||
Individual or Organization ("Licensee") accessing and otherwise using
|
|
||||||
this software in source or binary form and its associated
|
|
||||||
documentation ("the Software").
|
|
||||||
|
|
||||||
2. Subject to the terms and conditions of this BeOpen Python License
|
|
||||||
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
|
||||||
royalty-free, world-wide license to reproduce, analyze, test, perform
|
|
||||||
and/or display publicly, prepare derivative works, distribute, and
|
|
||||||
otherwise use the Software alone or in any derivative version,
|
|
||||||
provided, however, that the BeOpen Python License is retained in the
|
|
||||||
Software, alone or in any derivative version prepared by Licensee.
|
|
||||||
|
|
||||||
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
|
||||||
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
|
||||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
|
||||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
|
||||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
|
||||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
|
||||||
|
|
||||||
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
|
||||||
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
|
||||||
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
|
||||||
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
|
||||||
|
|
||||||
5. This License Agreement will automatically terminate upon a material
|
|
||||||
breach of its terms and conditions.
|
|
||||||
|
|
||||||
6. This License Agreement shall be governed by and interpreted in all
|
|
||||||
respects by the law of the State of California, excluding conflict of
|
|
||||||
law provisions. Nothing in this License Agreement shall be deemed to
|
|
||||||
create any relationship of agency, partnership, or joint venture
|
|
||||||
between BeOpen and Licensee. This License Agreement does not grant
|
|
||||||
permission to use BeOpen trademarks or trade names in a trademark
|
|
||||||
sense to endorse or promote products or services of Licensee, or any
|
|
||||||
third party. As an exception, the "BeOpen Python" logos available at
|
|
||||||
http://www.pythonlabs.com/logos.html may be used according to the
|
|
||||||
permissions granted on that web page.
|
|
||||||
|
|
||||||
7. By copying, installing or otherwise using the software, Licensee
|
|
||||||
agrees to be bound by the terms and conditions of this License
|
|
||||||
Agreement.
|
|
||||||
|
|
||||||
|
|
||||||
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
1. This LICENSE AGREEMENT is between the Corporation for National
|
|
||||||
Research Initiatives, having an office at 1895 Preston White Drive,
|
|
||||||
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
|
||||||
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
|
||||||
source or binary form and its associated documentation.
|
|
||||||
|
|
||||||
2. Subject to the terms and conditions of this License Agreement, CNRI
|
|
||||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
|
||||||
license to reproduce, analyze, test, perform and/or display publicly,
|
|
||||||
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
|
||||||
alone or in any derivative version, provided, however, that CNRI's
|
|
||||||
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
|
||||||
1995-2001 Corporation for National Research Initiatives; All Rights
|
|
||||||
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
|
||||||
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
|
||||||
Agreement, Licensee may substitute the following text (omitting the
|
|
||||||
quotes): "Python 1.6.1 is made available subject to the terms and
|
|
||||||
conditions in CNRI's License Agreement. This Agreement together with
|
|
||||||
Python 1.6.1 may be located on the Internet using the following
|
|
||||||
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
|
||||||
Agreement may also be obtained from a proxy server on the Internet
|
|
||||||
using the following URL: http://hdl.handle.net/1895.22/1013".
|
|
||||||
|
|
||||||
3. In the event Licensee prepares a derivative work that is based on
|
|
||||||
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
|
||||||
the derivative work available to others as provided herein, then
|
|
||||||
Licensee hereby agrees to include in any such work a brief summary of
|
|
||||||
the changes made to Python 1.6.1.
|
|
||||||
|
|
||||||
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
|
||||||
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
|
||||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
|
||||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
|
||||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
|
||||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
|
||||||
|
|
||||||
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
|
||||||
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
|
||||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
|
||||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
|
||||||
|
|
||||||
6. This License Agreement will automatically terminate upon a material
|
|
||||||
breach of its terms and conditions.
|
|
||||||
|
|
||||||
7. This License Agreement shall be governed by the federal
|
|
||||||
intellectual property law of the United States, including without
|
|
||||||
limitation the federal copyright law, and, to the extent such
|
|
||||||
U.S. federal law does not apply, by the law of the Commonwealth of
|
|
||||||
Virginia, excluding Virginia's conflict of law provisions.
|
|
||||||
Notwithstanding the foregoing, with regard to derivative works based
|
|
||||||
on Python 1.6.1 that incorporate non-separable material that was
|
|
||||||
previously distributed under the GNU General Public License (GPL), the
|
|
||||||
law of the Commonwealth of Virginia shall govern this License
|
|
||||||
Agreement only as to issues arising under or with respect to
|
|
||||||
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
|
||||||
License Agreement shall be deemed to create any relationship of
|
|
||||||
agency, partnership, or joint venture between CNRI and Licensee. This
|
|
||||||
License Agreement does not grant permission to use CNRI trademarks or
|
|
||||||
trade name in a trademark sense to endorse or promote products or
|
|
||||||
services of Licensee, or any third party.
|
|
||||||
|
|
||||||
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
|
||||||
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
|
||||||
bound by the terms and conditions of this License Agreement.
|
|
||||||
|
|
||||||
ACCEPT
|
|
||||||
|
|
||||||
|
|
||||||
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
|
||||||
--------------------------------------------------
|
|
||||||
|
|
||||||
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
|
||||||
The Netherlands. All rights reserved.
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software and its
|
|
||||||
documentation for any purpose and without fee is hereby granted,
|
|
||||||
provided that the above copyright notice appear in all copies and that
|
|
||||||
both that copyright notice and this permission notice appear in
|
|
||||||
supporting documentation, and that the name of Stichting Mathematisch
|
|
||||||
Centrum or CWI not be used in advertising or publicity pertaining to
|
|
||||||
distribution of the software without specific, written prior
|
|
||||||
permission.
|
|
||||||
|
|
||||||
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
|
||||||
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
|
||||||
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
|
||||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
8
configure
vendored
8
configure
vendored
@ -7239,9 +7239,11 @@ for rom in seabios vgabios ; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
# set up tests data directory
|
# set up tests data directory
|
||||||
if [ ! -e tests/data ]; then
|
for tests_subdir in acceptance data; do
|
||||||
symlink "$source_path/tests/data" tests/data
|
if [ ! -e tests/$tests_subdir ]; then
|
||||||
fi
|
symlink "$source_path/tests/$tests_subdir" tests/$tests_subdir
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# set up qemu-iotests in this build directory
|
# set up qemu-iotests in this build directory
|
||||||
iotests_common_env="tests/qemu-iotests/common.env"
|
iotests_common_env="tests/qemu-iotests/common.env"
|
||||||
|
@ -484,3 +484,195 @@ supported. To start the fuzzer, run
|
|||||||
|
|
||||||
Alternatively, some command different from "qemu-img info" can be tested, by
|
Alternatively, some command different from "qemu-img info" can be tested, by
|
||||||
changing the ``-c`` option.
|
changing the ``-c`` option.
|
||||||
|
|
||||||
|
Acceptance tests using the Avocado Framework
|
||||||
|
============================================
|
||||||
|
|
||||||
|
The ``tests/acceptance`` directory hosts functional tests, also known
|
||||||
|
as acceptance level tests. They're usually higher level tests, and
|
||||||
|
may interact with external resources and with various guest operating
|
||||||
|
systems.
|
||||||
|
|
||||||
|
These tests are written using the Avocado Testing Framework (which must
|
||||||
|
be installed separately) in conjunction with a the ``avocado_qemu.Test``
|
||||||
|
class, implemented at ``tests/acceptance/avocado_qemu``.
|
||||||
|
|
||||||
|
Tests based on ``avocado_qemu.Test`` can easily:
|
||||||
|
|
||||||
|
* Customize the command line arguments given to the convenience
|
||||||
|
``self.vm`` attribute (a QEMUMachine instance)
|
||||||
|
|
||||||
|
* Interact with the QEMU monitor, send QMP commands and check
|
||||||
|
their results
|
||||||
|
|
||||||
|
* Interact with the guest OS, using the convenience console device
|
||||||
|
(which may be useful to assert the effectiveness and correctness of
|
||||||
|
command line arguments or QMP commands)
|
||||||
|
|
||||||
|
* Interact with external data files that accompany the test itself
|
||||||
|
(see ``self.get_data()``)
|
||||||
|
|
||||||
|
* Download (and cache) remote data files, such as firmware and kernel
|
||||||
|
images
|
||||||
|
|
||||||
|
* Have access to a library of guest OS images (by means of the
|
||||||
|
``avocado.utils.vmimage`` library)
|
||||||
|
|
||||||
|
* Make use of various other test related utilities available at the
|
||||||
|
test class itself and at the utility library:
|
||||||
|
|
||||||
|
- http://avocado-framework.readthedocs.io/en/latest/api/test/avocado.html#avocado.Test
|
||||||
|
- http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
To install Avocado and its dependencies, run:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
pip install --user avocado-framework
|
||||||
|
|
||||||
|
Alternatively, follow the instructions on this link:
|
||||||
|
|
||||||
|
http://avocado-framework.readthedocs.io/en/latest/GetStartedGuide.html#installing-avocado
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
This directory provides the ``avocado_qemu`` Python module, containing
|
||||||
|
the ``avocado_qemu.Test`` class. Here's a simple usage example:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
from avocado_qemu import Test
|
||||||
|
|
||||||
|
|
||||||
|
class Version(Test):
|
||||||
|
"""
|
||||||
|
:avocado: enable
|
||||||
|
:avocado: tags=quick
|
||||||
|
"""
|
||||||
|
def test_qmp_human_info_version(self):
|
||||||
|
self.vm.launch()
|
||||||
|
res = self.vm.command('human-monitor-command',
|
||||||
|
command_line='info version')
|
||||||
|
self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)')
|
||||||
|
|
||||||
|
To execute your test, run:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
avocado run version.py
|
||||||
|
|
||||||
|
Tests may be classified according to a convention by using docstring
|
||||||
|
directives such as ``:avocado: tags=TAG1,TAG2``. To run all tests
|
||||||
|
in the current directory, tagged as "quick", run:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
avocado run -t quick .
|
||||||
|
|
||||||
|
The ``avocado_qemu.Test`` base test class
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
The ``avocado_qemu.Test`` class has a number of characteristics that
|
||||||
|
are worth being mentioned right away.
|
||||||
|
|
||||||
|
First of all, it attempts to give each test a ready to use QEMUMachine
|
||||||
|
instance, available at ``self.vm``. Because many tests will tweak the
|
||||||
|
QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``)
|
||||||
|
is left to the test writer.
|
||||||
|
|
||||||
|
At test "tear down", ``avocado_qemu.Test`` handles the QEMUMachine
|
||||||
|
shutdown.
|
||||||
|
|
||||||
|
QEMUMachine
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
The QEMUMachine API is already widely used in the Python iotests,
|
||||||
|
device-crash-test and other Python scripts. It's a wrapper around the
|
||||||
|
execution of a QEMU binary, giving its users:
|
||||||
|
|
||||||
|
* the ability to set command line arguments to be given to the QEMU
|
||||||
|
binary
|
||||||
|
|
||||||
|
* a ready to use QMP connection and interface, which can be used to
|
||||||
|
send commands and inspect its results, as well as asynchronous
|
||||||
|
events
|
||||||
|
|
||||||
|
* convenience methods to set commonly used command line arguments in
|
||||||
|
a more succinct and intuitive way
|
||||||
|
|
||||||
|
QEMU binary selection
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The QEMU binary used for the ``self.vm`` QEMUMachine instance will
|
||||||
|
primarily depend on the value of the ``qemu_bin`` parameter. If it's
|
||||||
|
not explicitly set, its default value will be the result of a dynamic
|
||||||
|
probe in the same source tree. A suitable binary will be one that
|
||||||
|
targets the architecture matching host machine.
|
||||||
|
|
||||||
|
Based on this description, test writers will usually rely on one of
|
||||||
|
the following approaches:
|
||||||
|
|
||||||
|
1) Set ``qemu_bin``, and use the given binary
|
||||||
|
|
||||||
|
2) Do not set ``qemu_bin``, and use a QEMU binary named like
|
||||||
|
"${arch}-softmmu/qemu-system-${arch}", either in the current
|
||||||
|
working directory, or in the current source tree.
|
||||||
|
|
||||||
|
The resulting ``qemu_bin`` value will be preserved in the
|
||||||
|
``avocado_qemu.Test`` as an attribute with the same name.
|
||||||
|
|
||||||
|
Attribute reference
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Besides the attributes and methods that are part of the base
|
||||||
|
``avocado.Test`` class, the following attributes are available on any
|
||||||
|
``avocado_qemu.Test`` instance.
|
||||||
|
|
||||||
|
vm
|
||||||
|
~~
|
||||||
|
|
||||||
|
A QEMUMachine instance, initially configured according to the given
|
||||||
|
``qemu_bin`` parameter.
|
||||||
|
|
||||||
|
qemu_bin
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
The preserved value of the ``qemu_bin`` parameter or the result of the
|
||||||
|
dynamic probe for a QEMU binary in the current working directory or
|
||||||
|
source tree.
|
||||||
|
|
||||||
|
Parameter reference
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
To understand how Avocado parameters are accessed by tests, and how
|
||||||
|
they can be passed to tests, please refer to::
|
||||||
|
|
||||||
|
http://avocado-framework.readthedocs.io/en/latest/WritingTests.html#accessing-test-parameters
|
||||||
|
|
||||||
|
Parameter values can be easily seen in the log files, and will look
|
||||||
|
like the following:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
PARAMS (key=qemu_bin, path=*, default=x86_64-softmmu/qemu-system-x86_64) => 'x86_64-softmmu/qemu-system-x86_64
|
||||||
|
|
||||||
|
qemu_bin
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
The exact QEMU binary to be used on QEMUMachine.
|
||||||
|
|
||||||
|
Uninstalling Avocado
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
If you've followed the installation instructions above, you can easily
|
||||||
|
uninstall Avocado. Start by listing the packages you have installed::
|
||||||
|
|
||||||
|
pip list --user
|
||||||
|
|
||||||
|
And remove any package you want with::
|
||||||
|
|
||||||
|
pip uninstall <package_name>
|
||||||
|
103
scripts/qemu.py
103
scripts/qemu.py
@ -17,19 +17,41 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import qmp.qmp
|
import qmp.qmp
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
import socket
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
#: Maps machine types to the preferred console device types
|
||||||
|
CONSOLE_DEV_TYPES = {
|
||||||
|
r'^clipper$': 'isa-serial',
|
||||||
|
r'^malta': 'isa-serial',
|
||||||
|
r'^(pc.*|q35.*|isapc)$': 'isa-serial',
|
||||||
|
r'^(40p|powernv|prep)$': 'isa-serial',
|
||||||
|
r'^pseries.*': 'spapr-vty',
|
||||||
|
r'^s390-ccw-virtio.*': 'sclpconsole',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class QEMUMachineError(Exception):
|
class QEMUMachineError(Exception):
|
||||||
"""
|
"""
|
||||||
Exception called when an error in QEMUMachine happens.
|
Exception called when an error in QEMUMachine happens.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class QEMUMachineAddDeviceError(QEMUMachineError):
|
||||||
|
"""
|
||||||
|
Exception raised when a request to add a device can not be fulfilled
|
||||||
|
|
||||||
|
The failures are caused by limitations, lack of information or conflicting
|
||||||
|
requests on the QEMUMachine methods. This exception does not represent
|
||||||
|
failures reported by the QEMU binary itself.
|
||||||
|
"""
|
||||||
|
|
||||||
class MonitorResponseError(qmp.qmp.QMPError):
|
class MonitorResponseError(qmp.qmp.QMPError):
|
||||||
'''
|
'''
|
||||||
Represents erroneous QMP monitor reply
|
Represents erroneous QMP monitor reply
|
||||||
@ -91,6 +113,10 @@ class QEMUMachine(object):
|
|||||||
self._test_dir = test_dir
|
self._test_dir = test_dir
|
||||||
self._temp_dir = None
|
self._temp_dir = None
|
||||||
self._launched = False
|
self._launched = False
|
||||||
|
self._machine = None
|
||||||
|
self._console_device_type = None
|
||||||
|
self._console_address = None
|
||||||
|
self._console_socket = None
|
||||||
|
|
||||||
# just in case logging wasn't configured by the main script:
|
# just in case logging wasn't configured by the main script:
|
||||||
logging.basicConfig()
|
logging.basicConfig()
|
||||||
@ -175,9 +201,19 @@ class QEMUMachine(object):
|
|||||||
self._monitor_address[1])
|
self._monitor_address[1])
|
||||||
else:
|
else:
|
||||||
moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
|
moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
|
||||||
return ['-chardev', moncdev,
|
args = ['-chardev', moncdev,
|
||||||
'-mon', 'chardev=mon,mode=control',
|
'-mon', 'chardev=mon,mode=control',
|
||||||
'-display', 'none', '-vga', 'none']
|
'-display', 'none', '-vga', 'none']
|
||||||
|
if self._machine is not None:
|
||||||
|
args.extend(['-machine', self._machine])
|
||||||
|
if self._console_device_type is not None:
|
||||||
|
self._console_address = os.path.join(self._temp_dir,
|
||||||
|
self._name + "-console.sock")
|
||||||
|
chardev = ('socket,id=console,path=%s,server,nowait' %
|
||||||
|
self._console_address)
|
||||||
|
device = '%s,chardev=console' % self._console_device_type
|
||||||
|
args.extend(['-chardev', chardev, '-device', device])
|
||||||
|
return args
|
||||||
|
|
||||||
def _pre_launch(self):
|
def _pre_launch(self):
|
||||||
self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
|
self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
|
||||||
@ -202,6 +238,10 @@ class QEMUMachine(object):
|
|||||||
|
|
||||||
self._qemu_log_path = None
|
self._qemu_log_path = None
|
||||||
|
|
||||||
|
if self._console_socket is not None:
|
||||||
|
self._console_socket.close()
|
||||||
|
self._console_socket = None
|
||||||
|
|
||||||
if self._temp_dir is not None:
|
if self._temp_dir is not None:
|
||||||
shutil.rmtree(self._temp_dir)
|
shutil.rmtree(self._temp_dir)
|
||||||
self._temp_dir = None
|
self._temp_dir = None
|
||||||
@ -359,3 +399,64 @@ class QEMUMachine(object):
|
|||||||
of the qemu process.
|
of the qemu process.
|
||||||
'''
|
'''
|
||||||
return self._iolog
|
return self._iolog
|
||||||
|
|
||||||
|
def add_args(self, *args):
|
||||||
|
'''
|
||||||
|
Adds to the list of extra arguments to be given to the QEMU binary
|
||||||
|
'''
|
||||||
|
self._args.extend(args)
|
||||||
|
|
||||||
|
def set_machine(self, machine_type):
|
||||||
|
'''
|
||||||
|
Sets the machine type
|
||||||
|
|
||||||
|
If set, the machine type will be added to the base arguments
|
||||||
|
of the resulting QEMU command line.
|
||||||
|
'''
|
||||||
|
self._machine = machine_type
|
||||||
|
|
||||||
|
def set_console(self, device_type=None):
|
||||||
|
'''
|
||||||
|
Sets the device type for a console device
|
||||||
|
|
||||||
|
If set, the console device and a backing character device will
|
||||||
|
be added to the base arguments of the resulting QEMU command
|
||||||
|
line.
|
||||||
|
|
||||||
|
This is a convenience method that will either use the provided
|
||||||
|
device type, of if not given, it will used the device type set
|
||||||
|
on CONSOLE_DEV_TYPES.
|
||||||
|
|
||||||
|
The actual setting of command line arguments will be be done at
|
||||||
|
machine launch time, as it depends on the temporary directory
|
||||||
|
to be created.
|
||||||
|
|
||||||
|
@param device_type: the device type, such as "isa-serial"
|
||||||
|
@raises: QEMUMachineAddDeviceError if the device type is not given
|
||||||
|
and can not be determined.
|
||||||
|
'''
|
||||||
|
if device_type is None:
|
||||||
|
if self._machine is None:
|
||||||
|
raise QEMUMachineAddDeviceError("Can not add a console device:"
|
||||||
|
" QEMU instance without a "
|
||||||
|
"defined machine type")
|
||||||
|
for regex, device in CONSOLE_DEV_TYPES.items():
|
||||||
|
if re.match(regex, self._machine):
|
||||||
|
device_type = device
|
||||||
|
break
|
||||||
|
if device_type is None:
|
||||||
|
raise QEMUMachineAddDeviceError("Can not add a console device:"
|
||||||
|
" no matching console device "
|
||||||
|
"type definition")
|
||||||
|
self._console_device_type = device_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def console_socket(self):
|
||||||
|
"""
|
||||||
|
Returns a socket connected to the console
|
||||||
|
"""
|
||||||
|
if self._console_socket is None:
|
||||||
|
self._console_socket = socket.socket(socket.AF_UNIX,
|
||||||
|
socket.SOCK_STREAM)
|
||||||
|
self._console_socket.connect(self._console_address)
|
||||||
|
return self._console_socket
|
||||||
|
10
tests/acceptance/README.rst
Normal file
10
tests/acceptance/README.rst
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
============================================
|
||||||
|
Acceptance tests using the Avocado Framework
|
||||||
|
============================================
|
||||||
|
|
||||||
|
This directory contains functional tests, also known as acceptance
|
||||||
|
level tests. They're usually higher level, and may interact with
|
||||||
|
external resources and with various guest operating systems.
|
||||||
|
|
||||||
|
For more information, please refer to ``docs/devel/testing.rst``,
|
||||||
|
section "Acceptance tests using the Avocado Framework".
|
54
tests/acceptance/avocado_qemu/__init__.py
Normal file
54
tests/acceptance/avocado_qemu/__init__.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Test class and utilities for functional tests
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# Author:
|
||||||
|
# Cleber Rosa <crosa@redhat.com>
|
||||||
|
#
|
||||||
|
# This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
|
# later. See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import avocado
|
||||||
|
|
||||||
|
SRC_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||||
|
SRC_ROOT_DIR = os.path.abspath(os.path.dirname(SRC_ROOT_DIR))
|
||||||
|
sys.path.append(os.path.join(SRC_ROOT_DIR, 'scripts'))
|
||||||
|
|
||||||
|
from qemu import QEMUMachine
|
||||||
|
|
||||||
|
def is_readable_executable_file(path):
|
||||||
|
return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK)
|
||||||
|
|
||||||
|
|
||||||
|
def pick_default_qemu_bin():
|
||||||
|
"""
|
||||||
|
Picks the path of a QEMU binary, starting either in the current working
|
||||||
|
directory or in the source tree root directory.
|
||||||
|
"""
|
||||||
|
arch = os.uname()[4]
|
||||||
|
qemu_bin_relative_path = os.path.join("%s-softmmu" % arch,
|
||||||
|
"qemu-system-%s" % arch)
|
||||||
|
if is_readable_executable_file(qemu_bin_relative_path):
|
||||||
|
return qemu_bin_relative_path
|
||||||
|
|
||||||
|
qemu_bin_from_src_dir_path = os.path.join(SRC_ROOT_DIR,
|
||||||
|
qemu_bin_relative_path)
|
||||||
|
if is_readable_executable_file(qemu_bin_from_src_dir_path):
|
||||||
|
return qemu_bin_from_src_dir_path
|
||||||
|
|
||||||
|
|
||||||
|
class Test(avocado.Test):
|
||||||
|
def setUp(self):
|
||||||
|
self.vm = None
|
||||||
|
self.qemu_bin = self.params.get('qemu_bin',
|
||||||
|
default=pick_default_qemu_bin())
|
||||||
|
if self.qemu_bin is None:
|
||||||
|
self.cancel("No QEMU binary defined or found in the source tree")
|
||||||
|
self.vm = QEMUMachine(self.qemu_bin)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if self.vm is not None:
|
||||||
|
self.vm.shutdown()
|
47
tests/acceptance/boot_linux_console.py
Normal file
47
tests/acceptance/boot_linux_console.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Functional test that boots a Linux kernel and checks the console
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# Author:
|
||||||
|
# Cleber Rosa <crosa@redhat.com>
|
||||||
|
#
|
||||||
|
# This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
|
# later. See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from avocado_qemu import Test
|
||||||
|
|
||||||
|
|
||||||
|
class BootLinuxConsole(Test):
|
||||||
|
"""
|
||||||
|
Boots a x86_64 Linux kernel and checks that the console is operational
|
||||||
|
and the kernel command line is properly passed from QEMU to the kernel
|
||||||
|
|
||||||
|
:avocado: enable
|
||||||
|
:avocado: tags=x86_64
|
||||||
|
"""
|
||||||
|
|
||||||
|
timeout = 60
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
kernel_url = ('https://mirrors.kernel.org/fedora/releases/28/'
|
||||||
|
'Everything/x86_64/os/images/pxeboot/vmlinuz')
|
||||||
|
kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a'
|
||||||
|
kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
|
||||||
|
|
||||||
|
self.vm.set_machine('pc')
|
||||||
|
self.vm.set_console()
|
||||||
|
kernel_command_line = 'console=ttyS0'
|
||||||
|
self.vm.add_args('-kernel', kernel_path,
|
||||||
|
'-append', kernel_command_line)
|
||||||
|
self.vm.launch()
|
||||||
|
console = self.vm.console_socket.makefile()
|
||||||
|
console_logger = logging.getLogger('console')
|
||||||
|
while True:
|
||||||
|
msg = console.readline()
|
||||||
|
console_logger.debug(msg.strip())
|
||||||
|
if 'Kernel command line: %s' % kernel_command_line in msg:
|
||||||
|
break
|
||||||
|
if 'Kernel panic - not syncing' in msg:
|
||||||
|
self.fail("Kernel panic reached")
|
24
tests/acceptance/version.py
Normal file
24
tests/acceptance/version.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Version check example test
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# Author:
|
||||||
|
# Cleber Rosa <crosa@redhat.com>
|
||||||
|
#
|
||||||
|
# This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
|
# later. See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
|
||||||
|
from avocado_qemu import Test
|
||||||
|
|
||||||
|
|
||||||
|
class Version(Test):
|
||||||
|
"""
|
||||||
|
:avocado: enable
|
||||||
|
:avocado: tags=quick
|
||||||
|
"""
|
||||||
|
def test_qmp_human_info_version(self):
|
||||||
|
self.vm.launch()
|
||||||
|
res = self.vm.command('human-monitor-command',
|
||||||
|
command_line='info version')
|
||||||
|
self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)')
|
60
tests/acceptance/vnc.py
Normal file
60
tests/acceptance/vnc.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Simple functional tests for VNC functionality
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# Author:
|
||||||
|
# Cleber Rosa <crosa@redhat.com>
|
||||||
|
#
|
||||||
|
# This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
|
# later. See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
from avocado_qemu import Test
|
||||||
|
|
||||||
|
|
||||||
|
class Vnc(Test):
|
||||||
|
"""
|
||||||
|
:avocado: enable
|
||||||
|
:avocado: tags=vnc,quick
|
||||||
|
"""
|
||||||
|
def test_no_vnc(self):
|
||||||
|
self.vm.add_args('-nodefaults', '-S')
|
||||||
|
self.vm.launch()
|
||||||
|
self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled'])
|
||||||
|
|
||||||
|
def test_no_vnc_change_password(self):
|
||||||
|
self.vm.add_args('-nodefaults', '-S')
|
||||||
|
self.vm.launch()
|
||||||
|
self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled'])
|
||||||
|
set_password_response = self.vm.qmp('change',
|
||||||
|
device='vnc',
|
||||||
|
target='password',
|
||||||
|
arg='new_password')
|
||||||
|
self.assertIn('error', set_password_response)
|
||||||
|
self.assertEqual(set_password_response['error']['class'],
|
||||||
|
'GenericError')
|
||||||
|
self.assertEqual(set_password_response['error']['desc'],
|
||||||
|
'Could not set password')
|
||||||
|
|
||||||
|
def test_vnc_change_password_requires_a_password(self):
|
||||||
|
self.vm.add_args('-nodefaults', '-S', '-vnc', ':0')
|
||||||
|
self.vm.launch()
|
||||||
|
self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled'])
|
||||||
|
set_password_response = self.vm.qmp('change',
|
||||||
|
device='vnc',
|
||||||
|
target='password',
|
||||||
|
arg='new_password')
|
||||||
|
self.assertIn('error', set_password_response)
|
||||||
|
self.assertEqual(set_password_response['error']['class'],
|
||||||
|
'GenericError')
|
||||||
|
self.assertEqual(set_password_response['error']['desc'],
|
||||||
|
'Could not set password')
|
||||||
|
|
||||||
|
def test_vnc_change_password(self):
|
||||||
|
self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password')
|
||||||
|
self.vm.launch()
|
||||||
|
self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled'])
|
||||||
|
set_password_response = self.vm.qmp('change',
|
||||||
|
device='vnc',
|
||||||
|
target='password',
|
||||||
|
arg='new_password')
|
||||||
|
self.assertEqual(set_password_response['return'], {})
|
Loading…
x
Reference in New Issue
Block a user