add vpp debugging support to test framework 41/3641/5
authorKlement Sekera <ksekera@cisco.com>
Fri, 28 Oct 2016 11:20:27 +0000 (13:20 +0200)
committerDamjan Marion <dmarion.lists@gmail.com>
Mon, 31 Oct 2016 23:48:25 +0000 (23:48 +0000)
improve test documentation

Change-Id: Ia9678aa2532ecb4cb33736aedb4a31aa3f2a3f93
Signed-off-by: Klement Sekera <ksekera@cisco.com>
.gitignore
Makefile
test/Makefile
test/doc/Makefile
test/doc/conf.py
test/framework.py
test/hook.py
test/log.py [new file with mode: 0644]
test/test_l2bd.py
test/test_l2xc.py
test/vpp_papi_provider.py

index e88652c..126780b 100644 (file)
@@ -13,6 +13,8 @@
 /build-root/*.deb
 /build-root/*.rpm
 /build-root/*.changes
+/build-root/test-doc/
+/build-roit/python/
 /build-config.mk
 /dpdk/*.tar.gz
 /dpdk/*.tar.xz
index 54b0f29..3a6c783 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@ endif
 DEB_DEPENDS  = curl build-essential autoconf automake bison libssl-dev ccache
 DEB_DEPENDS += debhelper dkms git libtool libganglia1-dev libapr1-dev dh-systemd
 DEB_DEPENDS += libconfuse-dev git-review exuberant-ctags cscope
-DEB_DEPENDS += python-dev
+DEB_DEPENDS += python-dev python-virtualenv
 ifeq ($(OS_VERSION_ID),14.04)
        DEB_DEPENDS += openjdk-8-jdk-headless
 else
@@ -58,7 +58,7 @@ endif
 .PHONY: help bootstrap wipe wipe-release build build-release rebuild rebuild-release
 .PHONY: run run-release debug debug-release build-vat run-vat pkg-deb pkg-rpm
 .PHONY: ctags cscope plugins plugins-release build-vpp-api
-.PHONY: test test-debug retest retest-debug
+.PHONY: test test-debug retest retest-debug test-doc test-wipe-doc test-help test-wipe
 
 help:
        @echo "Make Targets:"
@@ -78,8 +78,10 @@ help:
        @echo " debug-release       - run release binary with debugger"
        @echo " test                - build and run functional tests"
        @echo " test-debug          - build and run functional tests (debug build)"
+       @echo " test-wipe           - wipe files generated by unit tests"
        @echo " retest              - run functional tests"
        @echo " retest-debug        - run functional tests (debug build)"
+       @echo " test-help           - show help on test framework"
        @echo " build-vat           - build vpp-api-test tool"
        @echo " build-vpp-api       - build vpp-api"
        @echo " run-vat             - run vpp-api-test tool"
@@ -93,6 +95,8 @@ help:
        @echo " doxygen             - (re)generate documentation"
        @echo " bootstrap-doxygen   - setup Doxygen dependencies"
        @echo " wipe-doxygen        - wipe all generated documentation"
+       @echo " test-doc            - generate documentation for test framework"
+       @echo " test-wipe-doc       - wipe documentation for test framework"
        @echo ""
        @echo "Make Arguments:"
        @echo " V=[0|1]             - set build verbosity level"
@@ -105,7 +109,7 @@ help:
        @echo " PLATFORM=<name>     - target platform. default is vpp"
        @echo " TEST=<name>         - only run specific test"
        @echo ""
-       @echo "Current Argumernt Values:"
+       @echo "Current Argument Values:"
        @echo " V            = $(V)"
        @echo " STARTUP_CONF = $(STARTUP_CONF)"
        @echo " STARTUP_DIR  = $(STARTUP_DIR)"
@@ -188,7 +192,7 @@ plugins-release: $(BR)/.bootstrap.ok
 build-vpp-api: $(BR)/.bootstrap.ok
        $(call make,$(PLATFORM)_debug,vpp-api-install)
 
-PYTHON_PATH=$(BR)/python
+VPP_PYTHON_PREFIX=$(BR)/python
 
 define test
        $(if $(filter-out $(3),retest),make -C $(BR) PLATFORM=$(1) TAG=$(2) vpp-api-install plugins-install vpp-install vpp-api-test-install,)
@@ -197,7 +201,7 @@ define test
          VPP_TEST_API_TEST_BIN=$(BR)/install-$(2)-native/vpp-api-test/bin/vpp_api_test \
          VPP_TEST_PLUGIN_PATH=$(BR)/install-$(2)-native/plugins/lib64/vpp_plugins \
          LD_LIBRARY_PATH=$(BR)/install-$(2)-native/vpp-api/lib64/ \
-         WS_ROOT=$(WS_ROOT) I=$(I) V=$(V) TEST=$(TEST) PYTHON_PATH=$(PYTHON_PATH) $(3)
+         WS_ROOT=$(WS_ROOT) V=$(V) TEST=$(TEST) VPP_PYTHON_PREFIX=$(VPP_PYTHON_PREFIX) $(3)
 endef
 
 test: bootstrap
@@ -206,12 +210,17 @@ test: bootstrap
 test-debug: bootstrap
        $(call test,vpp_lite,vpp_lite_debug,test)
 
+test-help:
+       @make -C test help
+
+test-wipe:
+       @make -C test wipe
+
 test-doc:
-       make -C $(BR) PLATFORM=vpp_lite TAG=vpp_lite vpp-api-install plugins-install vpp-install vpp-api-test-install
-       make -C test PYTHON_PATH=$(PYTHON_PATH) LD_LIBRARY_PATH=$(BR)/install-vpp_lite-native/vpp-api/lib64/ doc
+       @make -C test WS_ROOT=$(WS_ROOT) BR=$(BR) VPP_PYTHON_PREFIX=$(VPP_PYTHON_PREFIX) doc
 
-test-clean:
-       make -C test clean
+test-wipe-doc:
+       @make -C test wipe-doc BR=$(BR)
 
 retest:
        $(call test,vpp_lite,vpp_lite,retest)
index c90679e..63cee20 100644 (file)
@@ -1,22 +1,58 @@
-PYTHON_VENV_PATH=$(PYTHON_PATH)/virtualenv
+.PHONY: verify-python-path
 
-test: clean
+verify-python-path:
+ifndef VPP_PYTHON_PREFIX
+       $(error VPP_PYTHON_PREFIX is not set)
+endif
+
+PYTHON_VENV_PATH=$(VPP_PYTHON_PREFIX)/virtualenv
+
+test: wipe verify-python-path
        @virtualenv $(PYTHON_VENV_PATH)
        @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && pip install scapy"
        @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && pip install pexpect"
        @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && cd $(WS_ROOT)/vpp-api/python && python setup.py install"
        @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && python run_tests.py discover -p test_$(TEST)\"*.py\""
 
-retest: clean
+retest: wipe verify-python-path
        @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && python run_tests.py discover -p test_$(TEST)\"*.py\""
 
-.PHONY: clean doc
+.PHONY: wipe doc
 
-clean:
+wipe: verify-python-path
        @rm -f /dev/shm/vpp-unittest-*
        @rm -rf /tmp/vpp-unittest-*
 
-doc:
+doc: verify-python-path
        @virtualenv $(PYTHON_VENV_PATH)
        @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && pip install sphinx"
-       @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && make -C doc html"
+       @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && make -C doc WS_ROOT=$(WS_ROOT) BR=$(BR) NO_VPP_PAPI=1 html"
+
+wipe-doc:
+       @make -C doc wipe BR=$(BR)
+
+help:
+       @echo "Running tests:"
+       @echo ""
+       @echo " test                - build and run functional tests"
+       @echo " test-debug          - build and run functional tests (debug build)"
+       @echo " retest              - run functional tests"
+       @echo " retest-debug        - run functional tests (debug build)"
+       @echo " wipe-test           - wipe (temporary) files generated by unit tests"
+       @echo ""
+       @echo "Arguments controlling test runs:"
+       @echo " V=[0|1|2]            - set test verbosity level"
+       @echo " DEBUG=<type>         - set VPP debugging kind"
+       @echo "    DEBUG=core        - detect coredump and load it in gdb on crash"
+       @echo "    DEBUG=gdb         - allow easy debugging by printing VPP PID "
+       @echo "                        and waiting for user input before running "
+       @echo "                        and tearing down a testcase"
+       @echo "    DEBUG=gdbserver   - run gdb inside a gdb server, otherwise "
+       @echo "                        same as above"
+       @echo " STEP=[yes|no]        - ease debugging by stepping through a testcase "
+       @echo " TEST=<name>          - only run specific test"
+       @echo ""
+       @echo "Creating test documentation"
+       @echo " test-doc            - generate documentation for test framework"
+       @echo " wipe-test-doc       - wipe documentation for test framework"
+       @echo ""
index 0065538..809abef 100644 (file)
@@ -3,20 +3,34 @@
 
 # You can set these variables from the command line.
 SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
+SRC_DOC_DIR = $(WS_ROOT)/test/doc
+SPHINXBUILD = sphinx-build
 PAPER         =
-BUILDDIR      = _build
+BUILD_DOC_ROOT = $(BR)/test-doc
+BUILD_DOC_DIR = $(BUILD_DOC_ROOT)/build
+API_DOC_GEN_DIR = $(BUILD_DOC_ROOT)/apidoc
 
 # Internal variables.
 PAPEROPT_a4     = -D latex_paper_size=a4
 PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+ALLSPHINXOPTS   = -d $(BUILD_DOC_DIR)/.sphinx-cache $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(API_DOC_GEN_DIR) -c $(SRC_DOC_DIR)
 # the i18n builder cannot share the environment and doctrees with the others
 I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+INDEX_REL_PATH:=$(shell realpath --relative-to=$(API_DOC_GEN_DIR) $(SRC_DOC_DIR)/index.rst)
+IN_VENV:=$(shell if pip -V | grep "virtualenv" 2>&1 > /dev/null; then echo 1; else echo 0; fi)
+
+.PHONY: verify-virtualenv
+verify-virtualenv:
+ifeq ($(IN_VENV),0)
+       $(error "Not running inside virtualenv (are you running 'make test-doc' from root?)")
+endif
 
 .PHONY: regen-api-doc
-regen-api-doc:
-       sphinx-apidoc -o . ..
+regen-api-doc: verify-virtualenv
+       @mkdir -p $(API_DOC_GEN_DIR)
+       #@echo ".. include:: $(INDEX_REL_PATH)" > $(API_DOC_GEN_DIR)/index.rst
+       @cp $(SRC_DOC_DIR)/index.rst $(API_DOC_GEN_DIR)
+       sphinx-apidoc -o $(API_DOC_GEN_DIR) ..
 
 .PHONY: help
 help:
@@ -48,182 +62,182 @@ help:
        @echo "  coverage   to run coverage check of the documentation (if enabled)"
        @echo "  dummy      to check syntax errors of document sources"
 
-.PHONY: clean
-clean:
-       rm -rf $(BUILDDIR)/*
+.PHONY: wipe
+wipe:
+       rm -rf $(BUILD_DOC_ROOT)
 
 .PHONY: html
-html: regen-api-doc
-       $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+html: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/html
        @echo
-       @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+       @echo "Build finished. The HTML pages are in $(BUILD_DOC_DIR)/html."
 
 .PHONY: dirhtml
-dirhtml: regen-api-doc
-       $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+dirhtml: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/dirhtml
        @echo
-       @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+       @echo "Build finished. The HTML pages are in $(BUILD_DOC_DIR)/dirhtml."
 
 .PHONY: singlehtml
-singlehtml: regen-api-doc
-       $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+singlehtml: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/singlehtml
        @echo
-       @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+       @echo "Build finished. The HTML page is in $(BUILD_DOC_DIR)/singlehtml."
 
 .PHONY: pickle
-pickle: regen-api-doc
-       $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+pickle: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/pickle
        @echo
        @echo "Build finished; now you can process the pickle files."
 
 .PHONY: json
-json: regen-api-doc
-       $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+json: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/json
        @echo
        @echo "Build finished; now you can process the JSON files."
 
 .PHONY: htmlhelp
-htmlhelp: regen-api-doc
-       $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+htmlhelp: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/htmlhelp
        @echo
        @echo "Build finished; now you can run HTML Help Workshop with the" \
-             ".hhp project file in $(BUILDDIR)/htmlhelp."
+             ".hhp project file in $(BUILD_DOC_DIR)/htmlhelp."
 
 .PHONY: qthelp
-qthelp: regen-api-doc
-       $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+qthelp: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/qthelp
        @echo
        @echo "Build finished; now you can run "qcollectiongenerator" with the" \
-             ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-       @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/VPPtestframework.qhcp"
+             ".qhcp project file in $(BUILD_DOC_DIR)/qthelp, like this:"
+       @echo "# qcollectiongenerator $(BUILD_DOC_DIR)/qthelp/VPPtestframework.qhcp"
        @echo "To view the help file:"
-       @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/VPPtestframework.qhc"
+       @echo "# assistant -collectionFile $(BUILD_DOC_DIR)/qthelp/VPPtestframework.qhc"
 
 .PHONY: applehelp
-applehelp: regen-api-doc
-       $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+applehelp: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/applehelp
        @echo
-       @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+       @echo "Build finished. The help book is in $(BUILD_DOC_DIR)/applehelp."
        @echo "N.B. You won't be able to view it unless you put it in" \
              "~/Library/Documentation/Help or install it in your application" \
              "bundle."
 
 .PHONY: devhelp
-devhelp: regen-api-doc
-       $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+devhelp: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/devhelp
        @echo
        @echo "Build finished."
        @echo "To view the help file:"
        @echo "# mkdir -p $$HOME/.local/share/devhelp/VPPtestframework"
-       @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/VPPtestframework"
+       @echo "# ln -s $(BUILD_DOC_DIR)/devhelp $$HOME/.local/share/devhelp/VPPtestframework"
        @echo "# devhelp"
 
 .PHONY: epub
-epub: regen-api-doc
-       $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+epub: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/epub
        @echo
-       @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+       @echo "Build finished. The epub file is in $(BUILD_DOC_DIR)/epub."
 
 .PHONY: epub3
 epub3:
-       $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
+       $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/epub3
        @echo
-       @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
+       @echo "Build finished. The epub3 file is in $(BUILD_DOC_DIR)/epub3."
 
 .PHONY: latex
-latex: regen-api-doc
-       $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+latex: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/latex
        @echo
-       @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+       @echo "Build finished; the LaTeX files are in $(BUILD_DOC_DIR)/latex."
        @echo "Run \`make' in that directory to run these through (pdf)latex" \
              "(use \`make latexpdf' here to do that automatically)."
 
 .PHONY: latexpdf
-latexpdf: regen-api-doc
-       $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+latexpdf: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/latex
        @echo "Running LaTeX files through pdflatex..."
-       $(MAKE) -C $(BUILDDIR)/latex all-pdf
-       @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+       $(MAKE) -C $(BUILD_DOC_DIR)/latex all-pdf
+       @echo "pdflatex finished; the PDF files are in $(BUILD_DOC_DIR)/latex."
 
 .PHONY: latexpdfja
-latexpdfja: regen-api-doc
-       $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+latexpdfja: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/latex
        @echo "Running LaTeX files through platex and dvipdfmx..."
-       $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
-       @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+       $(MAKE) -C $(BUILD_DOC_DIR)/latex all-pdf-ja
+       @echo "pdflatex finished; the PDF files are in $(BUILD_DOC_DIR)/latex."
 
 .PHONY: text
-text: regen-api-doc
-       $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+text: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/text
        @echo
-       @echo "Build finished. The text files are in $(BUILDDIR)/text."
+       @echo "Build finished. The text files are in $(BUILD_DOC_DIR)/text."
 
 .PHONY: man
-man: regen-api-doc
-       $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+man: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/man
        @echo
-       @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+       @echo "Build finished. The manual pages are in $(BUILD_DOC_DIR)/man."
 
 .PHONY: texinfo
-texinfo: regen-api-doc
-       $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+texinfo: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/texinfo
        @echo
-       @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+       @echo "Build finished. The Texinfo files are in $(BUILD_DOC_DIR)/texinfo."
        @echo "Run \`make' in that directory to run these through makeinfo" \
              "(use \`make info' here to do that automatically)."
 
 .PHONY: info
-info: regen-api-doc
-       $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+info: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/texinfo
        @echo "Running Texinfo files through makeinfo..."
-       make -C $(BUILDDIR)/texinfo info
-       @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+       make -C $(BUILD_DOC_DIR)/texinfo info
+       @echo "makeinfo finished; the Info files are in $(BUILD_DOC_DIR)/texinfo."
 
 .PHONY: gettext
-gettext: regen-api-doc
-       $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+gettext: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILD_DOC_DIR)/locale
        @echo
-       @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+       @echo "Build finished. The message catalogs are in $(BUILD_DOC_DIR)/locale."
 
 .PHONY: changes
-changes: regen-api-doc
-       $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+changes: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/changes
        @echo
-       @echo "The overview file is in $(BUILDDIR)/changes."
+       @echo "The overview file is in $(BUILD_DOC_DIR)/changes."
 
 .PHONY: linkcheck
-linkcheck: regen-api-doc
-       $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+linkcheck: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/linkcheck
        @echo
        @echo "Link check complete; look for any errors in the above output " \
-             "or in $(BUILDDIR)/linkcheck/output.txt."
+             "or in $(BUILD_DOC_DIR)/linkcheck/output.txt."
 
 .PHONY: doctest
-doctest: regen-api-doc
-       $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+doctest: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/doctest
        @echo "Testing of doctests in the sources finished, look at the " \
-             "results in $(BUILDDIR)/doctest/output.txt."
+             "results in $(BUILD_DOC_DIR)/doctest/output.txt."
 
 .PHONY: coverage
-coverage: regen-api-doc
-       $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+coverage: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/coverage
        @echo "Testing of coverage in the sources finished, look at the " \
-             "results in $(BUILDDIR)/coverage/python.txt."
+             "results in $(BUILD_DOC_DIR)/coverage/python.txt."
 
 .PHONY: xml
-xml: regen-api-doc
-       $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+xml: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/xml
        @echo
-       @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+       @echo "Build finished. The XML files are in $(BUILD_DOC_DIR)/xml."
 
 .PHONY: pseudoxml
-pseudoxml: regen-api-doc
-       $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+pseudoxml: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/pseudoxml
        @echo
-       @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+       @echo "Build finished. The pseudo-XML files are in $(BUILD_DOC_DIR)/pseudoxml."
 
 .PHONY: dummy
-dummy: regen-api-doc
-       $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
+dummy: regen-api-doc verify-virtualenv
+       $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILD_DOC_DIR)/dummy
        @echo
        @echo "Build finished. Dummy builder generates no files."
index aa84171..cec9dde 100644 (file)
@@ -20,6 +20,7 @@ import os
 import sys
 sys.path.insert(0, os.path.abspath('..'))
 
+
 # -- General configuration ------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
index 02ffb7a..5f75e01 100644 (file)
@@ -2,20 +2,20 @@
 
 from abc import *
 import os
-import sys
 import subprocess
 import unittest
 import tempfile
+import time
 import resource
 from time import sleep
+from Queue import Queue
+from threading import Thread
 from inspect import getdoc
-from hook import PollHook
+from hook import StepHook, PollHook
 from vpp_pg_interface import VppPGInterface
 from vpp_papi_provider import VppPapiProvider
-
 from scapy.packet import Raw
-
-from logging import *
+from log import *
 
 """
   Test framework module.
@@ -24,41 +24,6 @@ from logging import *
   representing the results.
 """
 
-handler = StreamHandler(sys.stdout)
-getLogger().addHandler(handler)
-try:
-    verbose = int(os.getenv("V", 0))
-except:
-    verbose = 0
-# 40 = ERROR, 30 = WARNING, 20 = INFO, 10 = DEBUG, 0 = NOTSET (all messages)
-getLogger().setLevel(40 - 10 * verbose)
-getLogger("scapy.runtime").addHandler(handler)
-getLogger("scapy.runtime").setLevel(ERROR)
-
-# Static variables to store color formatting strings.
-#
-# These variables (RED, GREEN, YELLOW and LPURPLE) are used to configure
-# the color of the text to be printed in the terminal. Variable COLOR_RESET
-# is used to revert the text color to the default one.
-if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
-    RED = '\033[91m'
-    GREEN = '\033[92m'
-    YELLOW = '\033[93m'
-    LPURPLE = '\033[94m'
-    COLOR_RESET = '\033[0m'
-else:
-    RED = ''
-    GREEN = ''
-    YELLOW = ''
-    LPURPLE = ''
-    COLOR_RESET = ''
-
-
-""" @var formatting delimiter consisting of '=' characters """
-double_line_delim = '=' * 70
-""" @var formatting delimiter consisting of '-' characters """
-single_line_delim = '-' * 70
-
 
 class _PacketInfo(object):
     """Private class to create packet info object.
@@ -84,6 +49,11 @@ class _PacketInfo(object):
     data = None
 
 
+def pump_output(out, queue):
+    for line in iter(out.readline, b''):
+        queue.put(line)
+
+
 class VppTestCase(unittest.TestCase):
     """
     Subclass of the python unittest.TestCase class.
@@ -107,24 +77,86 @@ class VppTestCase(unittest.TestCase):
         """Return the instance of this testcase"""
         return cls.test_instance
 
+    @classmethod
+    def set_debug_flags(cls, d):
+        cls.debug_core = False
+        cls.debug_gdb = False
+        cls.debug_gdbserver = False
+        if d is None:
+            return
+        dl = d.lower()
+        if dl == "core":
+            if resource.getrlimit(resource.RLIMIT_CORE)[0] <= 0:
+                # give a heads up if this is actually useless
+                cls.logger.critical("WARNING: core size limit is set 0, core "
+                                    "files will NOT be created")
+            cls.debug_core = True
+        elif dl == "gdb":
+            cls.debug_gdb = True
+        elif dl == "gdbserver":
+            cls.debug_gdbserver = True
+        else:
+            raise Exception("Unrecognized DEBUG option: '%s'" % d)
+
     @classmethod
     def setUpConstants(cls):
         """ Set-up the test case class based on environment variables """
         try:
-            cls.interactive = True if int(os.getenv("I")) > 0 else False
+            s = os.getenv("STEP")
+            cls.step = True if s.lower() in ("y", "yes", "1") else False
         except:
-            cls.interactive = False
-        if cls.interactive and resource.getrlimit(resource.RLIMIT_CORE)[0] <= 0:
-            # give a heads up if this is actually useless
-            critical("WARNING: core size limit is set 0, core files will NOT "
-                     "be created")
+            cls.step = False
+        try:
+            d = os.getenv("DEBUG")
+        except:
+            d = None
+        cls.set_debug_flags(d)
         cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp")
         cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
         cls.vpp_cmdline = [cls.vpp_bin, "unix", "nodaemon",
                            "api-segment", "{", "prefix", cls.shm_prefix, "}"]
         if cls.plugin_path is not None:
             cls.vpp_cmdline.extend(["plugin_path", cls.plugin_path])
-        info("vpp_cmdline: %s" % cls.vpp_cmdline)
+        cls.logger.info("vpp_cmdline: %s" % cls.vpp_cmdline)
+
+    @classmethod
+    def wait_for_enter(cls):
+        if cls.debug_gdbserver:
+            print(double_line_delim)
+            print("Spawned GDB server with PID: %d" % cls.vpp.pid)
+        elif cls.debug_gdb:
+            print(double_line_delim)
+            print("Spawned VPP with PID: %d" % cls.vpp.pid)
+        else:
+            cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
+            return
+        print(single_line_delim)
+        print("You can debug the VPP using e.g.:")
+        if cls.debug_gdbserver:
+            print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
+            print("Now is the time to attach a gdb by running the above "
+                  "command, set up breakpoints etc. and then resume VPP from "
+                  "within gdb by issuing the 'continue' command")
+        elif cls.debug_gdb:
+            print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
+            print("Now is the time to attach a gdb by running the above "
+                  "command and set up breakpoints etc.")
+        print(single_line_delim)
+        raw_input("Press ENTER to continue running the testcase...")
+
+    @classmethod
+    def run_vpp(cls):
+        cmdline = cls.vpp_cmdline
+
+        if cls.debug_gdbserver:
+            cmdline = ['gdbserver', 'localhost:7777'] + cls.vpp_cmdline
+            cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
+
+        cls.vpp = subprocess.Popen(cmdline,
+                                   stdout=subprocess.PIPE,
+                                   stderr=subprocess.PIPE,
+                                   bufsize=1)
+        cls.wait_for_enter()
 
     @classmethod
     def setUpClass(cls):
@@ -132,37 +164,66 @@ class VppTestCase(unittest.TestCase):
         Perform class setup before running the testcase
         Remove shared memory files, start vpp and connect the vpp-api
         """
+        cls.logger = getLogger(cls.__name__)
         cls.tempdir = tempfile.mkdtemp(
             prefix='vpp-unittest-' + cls.__name__ + '-')
         cls.shm_prefix = cls.tempdir.split("/")[-1]
         os.chdir(cls.tempdir)
-        info("Temporary dir is %s, shm prefix is %s",
-             cls.tempdir, cls.shm_prefix)
+        cls.logger.info("Temporary dir is %s, shm prefix is %s",
+                        cls.tempdir, cls.shm_prefix)
         cls.setUpConstants()
         cls.pg_streams = []
         cls.packet_infos = {}
         cls.verbose = 0
         print(double_line_delim)
-        print(YELLOW + getdoc(cls) + COLOR_RESET)
+        print(colorize(getdoc(cls), YELLOW))
         print(double_line_delim)
         # need to catch exceptions here because if we raise, then the cleanup
         # doesn't get called and we might end with a zombie vpp
         try:
-            cls.vpp = subprocess.Popen(cls.vpp_cmdline, stderr=subprocess.PIPE)
-            debug("Spawned VPP with PID: %d" % cls.vpp.pid)
+            cls.run_vpp()
             cls.vpp_dead = False
             cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix)
-            cls.vapi.register_hook(PollHook(cls))
-            cls.vapi.connect()
+            if cls.step:
+                cls.vapi.register_hook(StepHook(cls))
+            else:
+                cls.vapi.register_hook(PollHook(cls))
+            time.sleep(0.1)
+            try:
+                cls.vapi.connect()
+            except:
+                if cls.debug_gdbserver:
+                    print(colorize("You're running VPP inside gdbserver but "
+                                   "VPP-API connection failed, did you forget "
+                                   "to 'continue' VPP from within gdb?", RED))
+                raise
+            cls.vpp_stdout_queue = Queue()
+            cls.vpp_stdout_reader_thread = Thread(
+                target=pump_output, args=(cls.vpp.stdout, cls.vpp_stdout_queue))
+            cls.vpp_stdout_reader_thread.start()
+            cls.vpp_stderr_queue = Queue()
+            cls.vpp_stderr_reader_thread = Thread(
+                target=pump_output, args=(cls.vpp.stderr, cls.vpp_stderr_queue))
+            cls.vpp_stderr_reader_thread.start()
         except:
             cls.vpp.terminate()
             del cls.vpp
+            raise
 
     @classmethod
     def quit(cls):
         """
         Disconnect vpp-api, kill vpp and cleanup shared memory files
         """
+        if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
+            cls.vpp.poll()
+            if cls.vpp.returncode is None:
+                print(double_line_delim)
+                print("VPP or GDB server is still running")
+                print(single_line_delim)
+                raw_input("When done debugging, press ENTER to kill the process"
+                          " and finish running the testcase...")
+
         if hasattr(cls, 'vpp'):
             cls.vapi.disconnect()
             cls.vpp.poll()
@@ -170,6 +231,29 @@ class VppTestCase(unittest.TestCase):
                 cls.vpp.terminate()
             del cls.vpp
 
+        if hasattr(cls, 'vpp_stdout_queue'):
+            cls.logger.info(single_line_delim)
+            cls.logger.info('VPP output to stdout while running %s:',
+                            cls.__name__)
+            cls.logger.info(single_line_delim)
+            f = open(cls.tempdir + '/vpp_stdout.txt', 'w')
+            while not cls.vpp_stdout_queue.empty():
+                line = cls.vpp_stdout_queue.get_nowait()
+                f.write(line)
+                cls.logger.info('VPP stdout: %s' % line.rstrip('\n'))
+
+        if hasattr(cls, 'vpp_stderr_queue'):
+            cls.logger.info(single_line_delim)
+            cls.logger.info('VPP output to stderr while running %s:',
+                            cls.__name__)
+            cls.logger.info(single_line_delim)
+            f = open(cls.tempdir + '/vpp_stderr.txt', 'w')
+            while not cls.vpp_stderr_queue.empty():
+                line = cls.vpp_stderr_queue.get_nowait()
+                f.write(line)
+                cls.logger.info('VPP stderr: %s' % line.rstrip('\n'))
+            cls.logger.info(single_line_delim)
+
     @classmethod
     def tearDownClass(cls):
         """ Perform final cleanup after running all tests in this test-case """
@@ -178,11 +262,11 @@ class VppTestCase(unittest.TestCase):
     def tearDown(self):
         """ Show various debug prints after each test """
         if not self.vpp_dead:
-            info(self.vapi.cli("show int"))
-            info(self.vapi.cli("show trace"))
-            info(self.vapi.cli("show hardware"))
-            info(self.vapi.cli("show error"))
-            info(self.vapi.cli("show run"))
+            self.logger.info(self.vapi.cli("show int"))
+            self.logger.info(self.vapi.cli("show trace"))
+            self.logger.info(self.vapi.cli("show hardware"))
+            self.logger.info(self.vapi.cli("show error"))
+            self.logger.info(self.vapi.cli("show run"))
 
     def setUp(self):
         """ Clear trace before running each test"""
@@ -392,7 +476,7 @@ class VppTestResult(unittest.TestResult):
 
         """
         unittest.TestResult.addSuccess(self, test)
-        self.result_string = GREEN + "OK" + COLOR_RESET
+        self.result_string = colorize("OK", GREEN)
 
     def addSkip(self, test, reason):
         """
@@ -403,7 +487,7 @@ class VppTestResult(unittest.TestResult):
 
         """
         unittest.TestResult.addSkip(self, test, reason)
-        self.result_string = YELLOW + "SKIP" + COLOR_RESET
+        self.result_string = colorize("SKIP", YELLOW)
 
     def addFailure(self, test, err):
         """
@@ -415,10 +499,10 @@ class VppTestResult(unittest.TestResult):
         """
         unittest.TestResult.addFailure(self, test, err)
         if hasattr(test, 'tempdir'):
-            self.result_string = RED + "FAIL" + COLOR_RESET + \
+            self.result_string = colorize("FAIL", RED) + \
                 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
         else:
-            self.result_string = RED + "FAIL" + COLOR_RESET + ' [no temp dir]'
+            self.result_string = colorize("FAIL", RED) + ' [no temp dir]'
 
     def addError(self, test, err):
         """
@@ -430,10 +514,10 @@ class VppTestResult(unittest.TestResult):
         """
         unittest.TestResult.addError(self, test, err)
         if hasattr(test, 'tempdir'):
-            self.result_string = RED + "ERROR" + COLOR_RESET + \
+            self.result_string = colorize("ERROR", RED) + \
                 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
         else:
-            self.result_string = RED + "ERROR" + COLOR_RESET + ' [no temp dir]'
+            self.result_string = colorize("ERROR", RED) + ' [no temp dir]'
 
     def getDescription(self, test):
         """
index 9489aa9..3ae1473 100644 (file)
@@ -1,7 +1,8 @@
 import signal
 import os
 import pexpect
-from logging import *
+import traceback
+from log import *
 
 
 class Hook(object):
@@ -9,6 +10,9 @@ class Hook(object):
     Generic hooks before/after API/CLI calls
     """
 
+    def __init__(self, logger):
+        self.logger = logger
+
     def before_api(self, api_name, api_args):
         """
         Function called before API call
@@ -17,7 +21,8 @@ class Hook(object):
         @param api_name: name of the API
         @param api_args: tuple containing the API arguments
         """
-        debug("API: %s (%s)" % (api_name, api_args))
+        self.logger.debug("API: %s (%s)" %
+                          (api_name, api_args), extra={'color': RED})
 
     def after_api(self, api_name, api_args):
         """
@@ -35,7 +40,7 @@ class Hook(object):
 
         @param cli: CLI string
         """
-        debug("CLI: %s" % (cli))
+        self.logger.debug("CLI: %s" % (cli), extra={'color': RED})
 
     def after_cli(self, cli):
         """
@@ -54,6 +59,7 @@ class PollHook(Hook):
     def __init__(self, testcase):
         self.vpp_dead = False
         self.testcase = testcase
+        self.logger = testcase.logger
 
     def spawn_gdb(self, gdb_path, core_path):
         gdb_cmdline = gdb_path + ' ' + self.testcase.vpp_bin + ' ' + core_path
@@ -74,11 +80,12 @@ class PollHook(Hook):
                 self.spawn_gdb(gdb_path, core_path)
                 return
             else:
-                error("Debugger '%s' does not exist or is not an executable.." %
-                      gdb_path)
+                self.logger.error(
+                    "Debugger '%s' does not exist or is not an executable.." %
+                    gdb_path)
 
-        critical('core file present, debug with: gdb ' +
-                 self.testcase.vpp_bin + ' ' + core_path)
+        self.logger.critical('core file present, debug with: gdb ' +
+                             self.testcase.vpp_bin + ' ' + core_path)
 
     def poll_vpp(self):
         """
@@ -97,7 +104,7 @@ class PollHook(Hook):
             msg = "VPP subprocess died unexpectedly with returncode %d [%s]" % (
                 self.testcase.vpp.returncode,
                 signaldict[abs(self.testcase.vpp.returncode)])
-            critical(msg)
+            self.logger.critical(msg)
             core_path = self.testcase.tempdir + '/core'
             if os.path.isfile(core_path):
                 self.on_crash(core_path)
@@ -126,3 +133,92 @@ class PollHook(Hook):
         """
         super(PollHook, self).after_cli(cli)
         self.poll_vpp()
+
+
+class StepHook(PollHook):
+    """ Hook which requires user to press ENTER before doing any API/CLI """
+
+    def __init__(self, testcase):
+        self.skip_stack = None
+        self.skip_num = None
+        self.skip_count = 0
+        super(StepHook, self).__init__(testcase)
+
+    def skip(self):
+        if self.skip_stack is None:
+            return False
+        stack = traceback.extract_stack()
+        counter = 0
+        skip = True
+        for e in stack:
+            if counter > self.skip_num:
+                break
+            if e[0] != self.skip_stack[counter][0]:
+                skip = False
+            if e[1] != self.skip_stack[counter][1]:
+                skip = False
+            counter += 1
+        if skip:
+            self.skip_count += 1
+            return True
+        else:
+            print("%d API/CLI calls skipped in specified stack "
+                  "frame" % self.skip_count)
+            self.skip_count = 0
+            self.skip_stack = None
+            self.skip_num = None
+            return False
+
+    def user_input(self):
+        print('number\tfunction\tfile\tcode')
+        counter = 0
+        stack = traceback.extract_stack()
+        for e in stack:
+            print('%02d.\t%s\t%s:%d\t[%s]' % (counter, e[2], e[0], e[1], e[3]))
+            counter += 1
+        print(single_line_delim)
+        print("You can enter a number of stack frame chosen from above")
+        print("Calls in/below that stack frame will be not be stepped anymore")
+        print(single_line_delim)
+        while True:
+            choice = raw_input("Enter your choice, if any, and press ENTER to "
+                               "continue running the testcase...")
+            if choice == "":
+                choice = None
+            try:
+                if choice is not None:
+                    num = int(choice)
+            except:
+                print("Invalid input")
+                continue
+            if choice is not None and (num < 0 or num >= len(stack)):
+                print("Invalid choice")
+                continue
+            break
+        if choice is not None:
+            self.skip_stack = stack
+            self.skip_num = num
+
+    def before_cli(self, cli):
+        """ Wait for ENTER before executing CLI """
+        if self.skip():
+            print("Skip pause before executing CLI: %s" % cli)
+        else:
+            print(double_line_delim)
+            print("Test paused before executing CLI: %s" % cli)
+            print(single_line_delim)
+            self.user_input()
+        super(StepHook, self).before_cli(cli)
+
+    def before_api(self, api_name, api_args):
+        """ Wait for ENTER before executing API """
+        if self.skip():
+            print("Skip pause before executing API: %s (%s)"
+                  % (api_name, api_args))
+        else:
+            print(double_line_delim)
+            print("Test paused before executing API: %s (%s)"
+                  % (api_name, api_args))
+            print(single_line_delim)
+            self.user_input()
+        super(StepHook, self).before_api(api_name, api_args)
diff --git a/test/log.py b/test/log.py
new file mode 100644 (file)
index 0000000..38610b7
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+
+import sys
+import os
+import logging
+
+""" @var formatting delimiter consisting of '=' characters """
+double_line_delim = '=' * 70
+""" @var formatting delimiter consisting of '-' characters """
+single_line_delim = '-' * 70
+
+
+def colorize(msg, color):
+    return color + msg + COLOR_RESET
+
+
+class ColorFormatter(logging.Formatter):
+
+    def init(self, fmt=None, datefmt=None):
+        super(ColorFormatter, self).__init__(fmt, datefmt)
+
+    def format(self, record):
+        message = super(ColorFormatter, self).format(record)
+        if hasattr(record, 'color'):
+            message = colorize(message, record.color)
+        return message
+
+handler = logging.StreamHandler(sys.stdout)
+handler.setFormatter(ColorFormatter())
+
+global_logger = logging.getLogger()
+global_logger.addHandler(handler)
+try:
+    verbose = int(os.getenv("V", 0))
+except:
+    verbose = 0
+
+# 40 = ERROR, 30 = WARNING, 20 = INFO, 10 = DEBUG, 0 = NOTSET (all messages)
+if verbose >= 2:
+    log_level = 10
+elif verbose == 1:
+    log_level = 20
+else:
+    log_level = 40
+
+scapy_logger = logging.getLogger("scapy.runtime")
+scapy_logger.setLevel(logging.ERROR)
+
+
+def getLogger(name):
+    logger = logging.getLogger(name)
+    logger.setLevel(log_level)
+    return logger
+
+# Static variables to store color formatting strings.
+#
+# These variables (RED, GREEN, YELLOW and LPURPLE) are used to configure
+# the color of the text to be printed in the terminal. Variable COLOR_RESET
+# is used to revert the text color to the default one.
+if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
+    RED = '\033[91m'
+    GREEN = '\033[92m'
+    YELLOW = '\033[93m'
+    LPURPLE = '\033[94m'
+    COLOR_RESET = '\033[0m'
+else:
+    RED = ''
+    GREEN = ''
+    YELLOW = ''
+    LPURPLE = ''
+    COLOR_RESET = ''
index 0fced5d..53e1ee0 100644 (file)
@@ -189,7 +189,7 @@ class TestL2bd(VppTestCase):
             MAC learning enabled
             learn 100 MAC enries
             3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of dot1ad
-             in the first version)
+            in the first version)
 
         2.sending l2 eth pkts between 3 interface
             64B, 512B, 1518B, 9200B (ether_size)
index d448a04..35c7a81 100644 (file)
@@ -78,7 +78,6 @@ class TestL2xc(VppTestCase):
         interfaces. Create host IPv4 address for every host MAC address too.
 
         :param count: Number of hosts to create MAC and IPv4 addresses for.
-        Type: int
         """
         for pg_if in self.pg_interfaces:
             # self.MY_MACS[i.sw_if_index] = []
@@ -151,12 +150,11 @@ class TestL2xc(VppTestCase):
         """ L2XC test
 
         Test scenario:
-        1.config
-            2 pairs of 2 interfaces, l2xconnected
-
-        2.sending l2 eth packets between 4 interfaces
-            64B, 512B, 1518B, 9018B (ether_size)
-            burst of packets per interface
+            1. config
+               2 pairs of 2 interfaces, l2xconnected
+            2. sending l2 eth packets between 4 interfaces
+               64B, 512B, 1518B, 9018B (ether_size)
+               burst of packets per interface
         """
 
         # Create incoming packet streams for packet-generator interfaces
index 454f3ed..261a0f4 100644 (file)
@@ -1,7 +1,19 @@
-import vpp_papi
+import os
 from logging import error
 from hook import Hook
 
+do_import = True
+try:
+    no_vpp_papi = os.getenv("NO_VPP_PAPI")
+    if no_vpp_papi == "1":
+        do_import = False
+except:
+    pass
+
+if do_import:
+    import vpp_papi
+
+
 # from vnet/vnet/mpls/mpls_types.h
 MPLS_IETF_MAX_LABEL = 0xfffff
 MPLS_LABEL_INVALID = MPLS_IETF_MAX_LABEL + 1
@@ -16,7 +28,7 @@ class VppPapiProvider(object):
     """
 
     def __init__(self, name, shm_prefix):
-        self.hook = Hook()
+        self.hook = Hook("vpp-papi-provider")
         self.name = name
         self.shm_prefix = shm_prefix
 
@@ -130,7 +142,6 @@ class VppPapiProvider(object):
                          default_router, max_interval, min_interval,
                          lifetime, initial_count, initial_interval, async))
 
-
     def vxlan_add_del_tunnel(
             self,
             src_addr,
@@ -177,7 +188,7 @@ class VppPapiProvider(object):
         :param rx_sw_if_index: Software interface index of Rx interface.
         :param tx_sw_if_index: Software interface index of Tx interface.
         :param enable: Create cross-connect if equal to 1, delete cross-connect
-        if equal to 0.
+                       if equal to 0.
         :type rx_sw_if_index: str or int
         :type rx_sw_if_index: str or int
         :type enable: int