diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2012-05-07 16:53:25 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2012-05-07 21:29:47 +0900 |
commit | 875ebcb90107f980a7f4975975dc831331ab5a36 (patch) | |
tree | 31120c77334c1ad3d887fe57967782162145ac3d | |
parent | 3cf1b23b0e58d55d817dca0d7b80a470cc3759e3 (diff) |
Added test framework, ported from Quantum
* Added test result format for Jenkins
* Added some test code for ryu/ofproto/
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | .gitignore | 7 | ||||
-rw-r--r-- | .pylintrc (renamed from pylintrc) | 3 | ||||
-rwxr-xr-x | pylint.sh | 9 | ||||
-rwxr-xr-x | run_tests.sh | 172 | ||||
-rw-r--r-- | ryu/tests/__init__.py | 0 | ||||
-rw-r--r-- | ryu/tests/run_tests.py | 35 | ||||
-rw-r--r-- | ryu/tests/test_lib.py | 244 | ||||
-rw-r--r-- | ryu/tests/unit/__init__.py | 0 | ||||
-rw-r--r-- | ryu/tests/unit/ofproto/__init__.py | 0 | ||||
-rw-r--r-- | ryu/tests/unit/ofproto/test_ofproto_parser.py | 62 | ||||
-rw-r--r-- | ryu/tests/unit/ofproto/test_parser_v10.py | 92 | ||||
-rw-r--r-- | ryu/tests/unit/sample/__init__.py | 0 | ||||
-rw-r--r-- | ryu/tests/unit/sample/test_sample1.py | 20 | ||||
-rw-r--r-- | ryu/tests/unit/sample/test_sample2.py | 14 | ||||
-rw-r--r-- | ryu/tests/unit/sample/test_simple_switch.py | 21 | ||||
-rw-r--r-- | tools/install_venv.py | 136 | ||||
-rw-r--r-- | tools/pip-requires | 13 | ||||
-rw-r--r-- | tools/test-requires | 9 | ||||
-rwxr-xr-x | tools/with_venv.sh | 21 |
19 files changed, 848 insertions, 10 deletions
@@ -8,4 +8,9 @@ GTAGS GRTAGS GPATH GSYMS -pylint.log +*.log +.venv/ +.coverage +covhtml/ +coverage.xml +nosetests.xml @@ -3,3 +3,6 @@ # W0511: TODOs in code comments are fine. # W0142: *args and **kwargs are fine. disable=C0111,W0511,W0142 +output-format=parseable +reports=yes +files-output=no diff --git a/pylint.sh b/pylint.sh deleted file mode 100755 index 7d03a767..00000000 --- a/pylint.sh +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh - -echo "Running pylint ..." -PYLINT_OPTIONS="--rcfile=pylintrc --output-format=parseable" -PYLINT_INCLUDE="ryu bin/ryu-manager bin/ryu-client" -export PYTHONPATH=$PYTHONPATH:.ryu -PYLINT_LOG=pylint.log - -pylint $PYLINT_OPTIONS $PYLINT_INCLUDE > $PYLINT_LOG diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 00000000..ee56f975 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,172 @@ +#!/bin/bash + +function usage { + echo "Usage: $0 [OPTION]..." + echo "Run Ryu's test suite(s)" + echo "" + echo " -V, --virtual-env Always use virtualenv. Install automatically if not present" + echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment" + echo " -c, --coverage Generate coverage report" + echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added." + echo " -p, --pep8 Just run pep8" + echo " -P, --no-pep8 Don't run pep8" + echo " -l, --pylint Just run pylint" + echo " -v, --verbose Run verbose pylint analysis" + echo " -h, --help Print this usage message" + echo "" + echo "Note: with no options specified, the script will try to run the tests in a virtual environment," + echo " If no virtualenv is found, the script will ask if you would like to create one. If you " + echo " prefer to run tests NOT in a virtual environment, simply pass the -N option." + exit +} + +function process_option { + case "$1" in + -h|--help) usage;; + -V|--virtual-env) let always_venv=1; let never_venv=0;; + -N|--no-virtual-env) let always_venv=0; let never_venv=1;; + -f|--force) let force=1;; + -p|--pep8) let just_pep8=1;let never_venv=1; let always_venv=0;; + -P|--no-pep8) no_pep8=1;; + -l|--pylint) let just_pylint=1; let never_venv=1; let always_venv=0;; + -c|--coverage) coverage=1;; + -v|--verbose) verbose=1;; + -*) noseopts="$noseopts $1";; + *) noseargs="$noseargs $1" + esac +} + +venv=.venv +with_venv=tools/with_venv.sh +always_venv=0 +never_venv=0 +just_pep8=0 +no_pep8=0 +just_pylint=0 +force=0 +noseargs= +wrapper="" +coverage=0 +verbose=0 + +for arg in "$@"; do + process_option $arg +done + +# If enabled, tell nose to collect coverage data +if [ $coverage -eq 1 ]; then + noseopts="$noseopts --with-coverage --cover-package=ryu" +fi + +function run_tests { + # Just run the test suites in current environment + ${wrapper} rm -f ./$PLUGIN_DIR/tests.sqlite + + if [ $verbose -eq 1 ]; then + ${wrapper} $NOSETESTS + else + ${wrapper} $NOSETESTS 2> run_tests.log + fi + # If we get some short import error right away, print the error log directly + RESULT=$? + if [ "$RESULT" -ne "0" ]; + then + ERRSIZE=`wc -l run_tests.log | awk '{print \$1}'` + if [ $verbose -eq 0 -a "$ERRSIZE" -lt "40" ]; + then + cat run_tests.log + fi + fi + return $RESULT +} + +function run_pylint { + echo "Running pylint ..." + PYLINT_OPTIONS="--rcfile=.pylintrc --output-format=parseable" + PYLINT_INCLUDE="ryu bin/ryu-manager bin/ryu-client" + export PYTHONPATH=$PYTHONPATH:.ryu + PYLINT_LOG=pylint.log + + pylint $PYLINT_OPTIONS $PYLINT_INCLUDE > $PYLINT_LOG + #BASE_CMD="pylint $PYLINT_OPTIONS $PYLINT_INCLUDE > $PYLINT_LOG" + #[ $verbose -eq 1 ] && $BASE_CMD || msg_count=`$BASE_CMD | grep 'ryu/' | wc -l` + #if [ $verbose -eq 0 ]; then + # echo "Pylint messages count: " $msg_count + #fi + export PYTHONPATH=$OLD_PYTHONPATH +} + +function run_pep8 { + echo "Running pep8 ..." + + PEP8_EXCLUDE="vcsversion.py,*.pyc" + PEP8_OPTIONS="--exclude=$PEP8_EXCLUDE --repeat --show-source" + PEP8_INCLUDE="bin/* ryu setup*.py" + ${wrapper} pep8 $PEP8_OPTIONS $PEP8_INCLUDE +} + +#NOSETESTS="nosetests $noseopts $noseargs" +NOSETESTS="python ./ryu/tests/run_tests.py $noseopts $noseargs" + +#if [ -n "$PLUGIN_DIR" ] +#then +# if ! [ -f ./$PLUGIN_DIR/run_tests.py ] +# then +# echo "Could not find run_tests.py in plugin directory $PLUGIN_DIR" +# exit 1 +# fi +#fi + +if [ $never_venv -eq 0 ] +then + # Remove the virtual environment if --force used + if [ $force -eq 1 ]; then + echo "Cleaning virtualenv..." + rm -rf ${venv} + fi + if [ -e ${venv} ]; then + wrapper="${with_venv}" + else + if [ $always_venv -eq 1 ]; then + # Automatically install the virtualenv + python tools/install_venv.py + wrapper="${with_venv}" + else + echo -e "No virtual environment found...create one? (Y/n) \c" + read use_ve + if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then + # Install the virtualenv and run the test suite in it + python tools/install_venv.py + wrapper=${with_venv} + fi + fi + fi +fi + +# Delete old coverage data from previous runs +if [ $coverage -eq 1 ]; then + ${wrapper} coverage erase +fi + +if [ $just_pep8 -eq 1 ]; then + run_pep8 + exit +fi +if [ $just_pylint -eq 1 ]; then + run_pylint + exit +fi + +run_tests +RV=$? +if [ $no_pep8 -eq 0 ]; then + run_pep8 +fi + +if [ $coverage -eq 1 ]; then + echo "Generating coverage report in coverage.xml and covhtml/" + ${wrapper} coverage xml -i + ${wrapper} coverage html -d covhtml -i +fi + +exit $RV diff --git a/ryu/tests/__init__.py b/ryu/tests/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ryu/tests/__init__.py diff --git a/ryu/tests/run_tests.py b/ryu/tests/run_tests.py new file mode 100644 index 00000000..b3d18686 --- /dev/null +++ b/ryu/tests/run_tests.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + + +import os +import sys + +from nose import config +from nose import core + +sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(__file__)) + + +import ryu.tests.unit +from ryu.tests.test_lib import run_tests + + +if __name__ == '__main__': + exit_status = False + + # if a single test case was specified, + # we should only invoked the tests once + invoke_once = len(sys.argv) > 1 + + cwd = os.getcwd() + c = config.Config(stream=sys.stdout, + env=os.environ, + verbosity=3, + includeExe=True, + traverseNamespace=True, + plugins=core.DefaultPluginManager()) + c.configureWhere(ryu.tests.unit.__path__) + + exit_status = run_tests(c) + sys.exit(exit_status) diff --git a/ryu/tests/test_lib.py b/ryu/tests/test_lib.py new file mode 100644 index 00000000..c812daff --- /dev/null +++ b/ryu/tests/test_lib.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python + +import gettext +import os +import unittest +import sys +import logging + +from nose import result +from nose import core +from nose import config + + +class _AnsiColorizer(object): + """ + A colorizer is an object that loosely wraps around a stream, allowing + callers to write text to the stream in a particular color. + + Colorizer classes must implement C{supported()} and C{write(text, color)}. + """ + _colors = dict(black=30, red=31, green=32, yellow=33, + blue=34, magenta=35, cyan=36, white=37) + + def __init__(self, stream): + self.stream = stream + + def supported(cls, stream=sys.stdout): + """ + A class method that returns True if the current platform supports + coloring terminal output using this method. Returns False otherwise. + """ + if not stream.isatty(): + return False # auto color only on TTYs + try: + import curses + except ImportError: + return False + else: + try: + try: + return curses.tigetnum("colors") > 2 + except curses.error: + curses.setupterm() + return curses.tigetnum("colors") > 2 + except: + raise + # guess false in case of error + return False + supported = classmethod(supported) + + def write(self, text, color): + """ + Write the given text to the stream in the given color. + + @param text: Text to be written to the stream. + + @param color: A string label for a color. e.g. 'red', 'white'. + """ + color = self._colors[color] + self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text)) + + +class _Win32Colorizer(object): + """ + See _AnsiColorizer docstring. + """ + def __init__(self, stream): + from win32console import GetStdHandle, STD_OUT_HANDLE, \ + FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \ + FOREGROUND_INTENSITY + red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN, + FOREGROUND_BLUE, FOREGROUND_INTENSITY) + self.stream = stream + self.screenBuffer = GetStdHandle(STD_OUT_HANDLE) + self._colors = { + 'normal': red | green | blue, + 'red': red | bold, + 'green': green | bold, + 'blue': blue | bold, + 'yellow': red | green | bold, + 'magenta': red | blue | bold, + 'cyan': green | blue | bold, + 'white': red | green | blue | bold} + + def supported(cls, stream=sys.stdout): + try: + import win32console + screenBuffer = win32console.GetStdHandle( + win32console.STD_OUT_HANDLE) + except ImportError: + return False + import pywintypes + try: + screenBuffer.SetConsoleTextAttribute( + win32console.FOREGROUND_RED | + win32console.FOREGROUND_GREEN | + win32console.FOREGROUND_BLUE) + except pywintypes.error: + return False + else: + return True + supported = classmethod(supported) + + def write(self, text, color): + color = self._colors[color] + self.screenBuffer.SetConsoleTextAttribute(color) + self.stream.write(text) + self.screenBuffer.SetConsoleTextAttribute(self._colors['normal']) + + +class _NullColorizer(object): + """ + See _AnsiColorizer docstring. + """ + def __init__(self, stream): + self.stream = stream + + def supported(cls, stream=sys.stdout): + return True + supported = classmethod(supported) + + def write(self, text, color): + self.stream.write(text) + + +class RyuTestResult(result.TextTestResult): + def __init__(self, *args, **kw): + result.TextTestResult.__init__(self, *args, **kw) + self._last_case = None + self.colorizer = None + # NOTE(vish, tfukushima): reset stdout for the terminal check + stdout = sys.__stdout__ + sys.stdout = sys.__stdout__ + for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]: + if colorizer.supported(): + self.colorizer = colorizer(self.stream) + break + sys.stdout = stdout + + def getDescription(self, test): + return str(test) + + # NOTE(vish, tfukushima): copied from unittest with edit to add color + def addSuccess(self, test): + unittest.TestResult.addSuccess(self, test) + if self.showAll: + self.colorizer.write("OK", 'green') + self.stream.writeln() + elif self.dots: + self.stream.write('.') + self.stream.flush() + + # NOTE(vish, tfukushima): copied from unittest with edit to add color + def addFailure(self, test, err): + unittest.TestResult.addFailure(self, test, err) + if self.showAll: + self.colorizer.write("FAIL", 'red') + self.stream.writeln() + elif self.dots: + self.stream.write('F') + self.stream.flush() + + # NOTE(vish, tfukushima): copied from unittest with edit to add color + def addError(self, test, err): + """Overrides normal addError to add support for errorClasses. + If the exception is a registered class, the error will be added + to the list for that class, not errors. + """ + stream = getattr(self, 'stream', None) + ec, ev, tb = err + try: + exc_info = self._exc_info_to_string(err, test) + except TypeError: + # This is for compatibility with Python 2.3. + exc_info = self._exc_info_to_string(err) + for cls, (storage, label, isfail) in self.errorClasses.items(): + if result.isclass(ec) and issubclass(ec, cls): + if isfail: + test.passwd = False + storage.append((test, exc_info)) + # Might get patched into a streamless result + if stream is not None: + if self.showAll: + message = [label] + detail = result._exception_details(err[1]) + if detail: + message.append(detail) + stream.writeln(": ".join(message)) + elif self.dots: + stream.write(label[:1]) + return + self.errors.append((test, exc_info)) + test.passed = False + if stream is not None: + if self.showAll: + self.colorizer.write("ERROR", 'red') + self.stream.writeln() + elif self.dots: + stream.write('E') + + def startTest(self, test): + unittest.TestResult.startTest(self, test) + current_case = test.test.__class__.__name__ + + if self.showAll: + if current_case != self._last_case: + self.stream.writeln(current_case) + self._last_case = current_case + #NOTE(salvatore-orlando): + #slightly changed in order to print test case class + #together with unit test name + self.stream.write( + ' %s' % str(test.test).ljust(60)) + self.stream.flush() + + +class RyuTestRunner(core.TextTestRunner): + def _makeResult(self): + return RyuTestResult(self.stream, + self.descriptions, + self.verbosity, + self.config) + + +def run_tests(c=None): + logger = logging.getLogger() + hdlr = logging.StreamHandler() + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + hdlr.setFormatter(formatter) + logger.addHandler(hdlr) + logger.setLevel(logging.DEBUG) + + # NOTE(bgh): I'm not entirely sure why but nose gets confused here when + # calling run_tests from a plugin directory run_tests.py (instead of the + # main run_tests.py). It will call run_tests with no arguments and the + # testing of run_tests will fail (though the plugin tests will pass). For + # now we just return True to let the run_tests test pass. + if not c: + return True + + runner = RyuTestRunner(stream=c.stream, + verbosity=c.verbosity, + config=c) + return not core.run(config=c, testRunner=runner) diff --git a/ryu/tests/unit/__init__.py b/ryu/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ryu/tests/unit/__init__.py diff --git a/ryu/tests/unit/ofproto/__init__.py b/ryu/tests/unit/ofproto/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ryu/tests/unit/ofproto/__init__.py diff --git a/ryu/tests/unit/ofproto/test_ofproto_parser.py b/ryu/tests/unit/ofproto/test_ofproto_parser.py new file mode 100644 index 00000000..163c9de0 --- /dev/null +++ b/ryu/tests/unit/ofproto/test_ofproto_parser.py @@ -0,0 +1,62 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import binascii +import unittest +from nose.tools import ok_, eq_ + +from ryu.ofproto import ofproto, ofproto_parser, ofproto_v1_0_parser + +import logging +LOG = logging.getLogger(__name__) + + +class TestOfproto_Parser(unittest.TestCase): + def setUp(self): + LOG.debug('setUp') + self.bufHello = binascii.unhexlify('0100000800000001') + self.bufFeaturesReply = binascii.unhexlify( + '010600b0000000020000000000000abc' \ + + '00000100010000000000008700000fff' \ + + '0002aefa39d2b9177472656d61302d30' \ + + '00000000000000000000000000000000' \ + + '000000c0000000000000000000000000' \ + + 'fffe723f9a764cc87673775f30786162' \ + + '63000000000000000000000100000001' \ + + '00000082000000000000000000000000' \ + + '00012200d6c5a1947472656d61312d30' \ + + '00000000000000000000000000000000' \ + + '000000c0000000000000000000000000') + self.bufPacketIn = binascii.unhexlify( + '010a005200000000000001010040' \ + + '00020000000000000002000000000001' \ + + '080045000032000000004011f967c0a8' \ + + '0001c0a8000200010001001e00000000' \ + + '00000000000000000000000000000000' \ + + '00000000') + + def tearDown(self): + LOG.debug('tearDown') + pass + + def testHello(self): + (version, msg_type, msg_len, xid) = ofproto_parser.header(self.bufHello) + eq_(version, 1) + eq_(msg_type, 0) + eq_(msg_len, 8) + eq_(xid, 1) + + def testFeaturesReply(self): + (version, msg_type, msg_len, xid) = ofproto_parser.header(self.bufFeaturesReply) + msg = ofproto_parser.msg(self, version, msg_type, msg_len, xid, self.bufFeaturesReply) + LOG.debug(msg) + ok_(isinstance(msg, ofproto_v1_0_parser.OFPSwitchFeatures)) + LOG.debug(msg.ports[65534]) + ok_(isinstance(msg.ports[1], ofproto_v1_0_parser.OFPPhyPort)) + ok_(isinstance(msg.ports[2], ofproto_v1_0_parser.OFPPhyPort)) + ok_(isinstance(msg.ports[65534], ofproto_v1_0_parser.OFPPhyPort)) + + def testPacketIn(self): + (version, msg_type, msg_len, xid) = ofproto_parser.header(self.bufPacketIn) + msg = ofproto_parser.msg(self, version, msg_type, msg_len, xid, self.bufPacketIn) + LOG.debug(msg) + ok_(isinstance(msg, ofproto_v1_0_parser.OFPPacketIn)) diff --git a/ryu/tests/unit/ofproto/test_parser_v10.py b/ryu/tests/unit/ofproto/test_parser_v10.py new file mode 100644 index 00000000..9b156c80 --- /dev/null +++ b/ryu/tests/unit/ofproto/test_parser_v10.py @@ -0,0 +1,92 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import unittest +import logging +from nose.tools import * +#from ryu.ofproto.ofproto_v1_0 import * +from ryu.ofproto.ofproto_v1_0_parser import * + +LOG = logging.getLogger('test_ofproto_v10') + + +class TestOFPActionOutput(unittest.TestCase): + """ Test case for ofprotp_v1_0_parser.OFPActionOutput + """ + + def setup(self): + pass + + def tearndown(self): + pass + + def test_init(self): + c = OFPActionOutput(ofproto_v1_0.OFPAT_OUTPUT, + ofproto_v1_0.OFP_ACTION_OUTPUT_SIZE) + eq_(ofproto_v1_0.OFPAT_OUTPUT, c.port) + eq_(ofproto_v1_0.OFP_ACTION_OUTPUT_SIZE, c.max_len) + + + def test_parser(self): + type_ = '\x00\x00' + len_ = '\x00\x08' + port = '\x00\x00' + max_len = '\x00\x08' + + buf = type_ + len_ + port + max_len + c = OFPActionOutput(ofproto_v1_0.OFPAT_OUTPUT, + ofproto_v1_0.OFP_ACTION_OUTPUT_SIZE) + + c.parser(buf, 0) + eq_(c.port, ofproto_v1_0.OFPAT_OUTPUT) + eq_(c.max_len, ofproto_v1_0.OFP_ACTION_OUTPUT_SIZE) + + + @raises(AssertionError) + def test_parser_check_type(self): + type_ = '\x00\x01' + len_ = '\x00\x08' + port = '\x00\x00' + max_len = '\x00\x08' + + buf = type_ + len_ + port + max_len + c = OFPActionOutput(ofproto_v1_0.OFPAT_OUTPUT, + ofproto_v1_0.OFP_ACTION_OUTPUT_SIZE) + + c.parser(buf, 0) + + + @raises(AssertionError) + def test_parser_check_len(self): + type_ = '\x00\x00' + len_ = '\x00\x0a' + port = '\x00\x00' + max_len = '\x00\x08' + + buf = type_ + len_ + port + max_len + c = OFPActionOutput(ofproto_v1_0.OFPAT_OUTPUT, + ofproto_v1_0.OFP_ACTION_OUTPUT_SIZE) + + c.parser(buf, 0) + + + def test_serialize_short(self): + c = OFPActionOutput(ofproto_v1_0.OFPAT_OUTPUT, + ofproto_v1_0.OFP_ACTION_OUTPUT_SIZE) + + len_ = c.max_len - 1 + buf = bytearray().zfill(len_) + #LOG.debug("buf: %s", buf) + + c.serialize(buf, c.max_len) + + + def test_serialize_max(self): + c = OFPActionOutput(ofproto_v1_0.OFPAT_OUTPUT, + ofproto_v1_0.OFP_ACTION_OUTPUT_SIZE) + + len_ = c.max_len + struct.calcsize(ofproto_v1_0.OFP_ACTION_OUTPUT_PACK_STR) - 1 + buf = str().zfill(len_) + #LOG.debug("buf: %s", buf) + + c.serialize(buf, c.max_len) + diff --git a/ryu/tests/unit/sample/__init__.py b/ryu/tests/unit/sample/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ryu/tests/unit/sample/__init__.py diff --git a/ryu/tests/unit/sample/test_sample1.py b/ryu/tests/unit/sample/test_sample1.py new file mode 100644 index 00000000..e29dcf9a --- /dev/null +++ b/ryu/tests/unit/sample/test_sample1.py @@ -0,0 +1,20 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import unittest +from nose.tools import ok_, eq_ +#from ryu.app.simple_switch import SimpleSwitch + +import logging + + +LOG = logging.getLogger('ryu.tests.test_sample1') + + +class TestSample1(unittest.TestCase): + + def testS1Func1(self): + LOG.debug('testS1Func1 - START') + ok_(True) + + def testS1Func2(self): + ok_(True) diff --git a/ryu/tests/unit/sample/test_sample2.py b/ryu/tests/unit/sample/test_sample2.py new file mode 100644 index 00000000..74baca9b --- /dev/null +++ b/ryu/tests/unit/sample/test_sample2.py @@ -0,0 +1,14 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import unittest +from nose.tools import ok_, eq_ +#from ryu.app.simple_switch import SimpleSwitch + + +class TestSample2(unittest.TestCase): + + def testS2Func1(self): + ok_(True) + + def testS2Func2(self): + ok_(True) diff --git a/ryu/tests/unit/sample/test_simple_switch.py b/ryu/tests/unit/sample/test_simple_switch.py new file mode 100644 index 00000000..5fb76304 --- /dev/null +++ b/ryu/tests/unit/sample/test_simple_switch.py @@ -0,0 +1,21 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import unittest +from nose.tools import ok_, eq_ + +from ryu.app.simple_switch import SimpleSwitch + +import logging +LOG = logging.getLogger(__name__) + + +class TestSimpleSwitch(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def testInit(self): + ss = SimpleSwitch() + ok_(True) diff --git a/tools/install_venv.py b/tools/install_venv.py new file mode 100644 index 00000000..09b321bd --- /dev/null +++ b/tools/install_venv.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Copyright 2010 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Installation script for Quantum's development virtualenv +""" + +import os +import subprocess +import sys + + +ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +VENV = os.path.join(ROOT, '.venv') +PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires') +TEST_REQUIRES = os.path.join(ROOT, 'tools', 'test-requires') +PY_VERSION = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) + +VENV_EXISTS = bool(os.path.exists(VENV)) + +def die(message, *args): + print >> sys.stderr, message % args + sys.exit(1) + + +def run_command(cmd, redirect_output=True, check_exit_code=True): + """ + Runs a command in an out-of-process shell, returning the + output of that command. Working directory is ROOT. + """ + if redirect_output: + stdout = subprocess.PIPE + else: + stdout = None + proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout) + output = proc.communicate()[0] + if check_exit_code and proc.returncode != 0: + raise Exception('Command "%s" failed.\n%s' % (' '.join(cmd), output)) + return output + + +HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'], + check_exit_code=False).strip()) +HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'], + check_exit_code=False).strip()) + + +def check_dependencies(): + """Make sure virtualenv is in the path.""" + + if not HAS_VIRTUALENV: + raise Exception('Virtualenv not found. ' + \ + 'Try installing python-virtualenv') + print 'done.' + + +def create_virtualenv(venv=VENV, install_pip=False): + """Creates the virtual environment and installs PIP only into the + virtual environment + """ + print 'Creating venv...', + + install = ['virtualenv', '-q', venv] + run_command(install) + + print 'done.' + print 'Installing pip in virtualenv...', + if install_pip and \ + not run_command(['tools/with_venv.sh', 'easy_install', + 'pip>1.0']): + die("Failed to install pip.") + print 'done.' + + +def install_dependencies(venv=VENV): + print 'Installing dependencies with pip (this can take a while)...' + run_command(['tools/with_venv.sh', 'pip', 'install', '-r', + PIP_REQUIRES], redirect_output=False) + run_command(['tools/with_venv.sh', 'pip', 'install', '-r', + TEST_REQUIRES], redirect_output=False) + + # Tell the virtual env how to "import quantum" + pthfile = os.path.join(venv, "lib", PY_VERSION, "site-packages", + "quantum.pth") + f = open(pthfile, 'w') + f.write("%s\n" % ROOT) + + +def print_help(): + help = """ + Quantum development environment setup is complete. + + Quantum development uses virtualenv to track and manage Python dependencies + while in development and testing. + + To activate the Quantum virtualenv for the extent of your current shell + session you can run: + + $ source .venv/bin/activate + + Or, if you prefer, you can run commands in the virtualenv on a case by case + basis by running: + + $ tools/with_venv.sh <your command> + + Also, make test will automatically use the virtualenv. + """ + print help + + +def main(argv): + check_dependencies() + create_virtualenv() + install_dependencies() + print_help() + +if __name__ == '__main__': + main(sys.argv) diff --git a/tools/pip-requires b/tools/pip-requires new file mode 100644 index 00000000..b599fb5b --- /dev/null +++ b/tools/pip-requires @@ -0,0 +1,13 @@ +#Paste +#PasteDeploy==1.5.0 +#Routes>=1.12.3 +#eventlet>=0.9.12 +#lxml +#mox==0.5.3 +gevent>=0.13 +python-gflags==1.3 +simplejson +#sqlalchemy +webob==1.0.8 + +#-e git+https://review.openstack.org/p/openstack/python-quantumclient#egg=python-quantumclient diff --git a/tools/test-requires b/tools/test-requires new file mode 100644 index 00000000..e8276058 --- /dev/null +++ b/tools/test-requires @@ -0,0 +1,9 @@ +#distribute>=0.6.24 + +coverage +#mock>=0.7.1 +nose +#nosexcover +#openstack.nose_plugin +pep8==0.6.1 +#webtest diff --git a/tools/with_venv.sh b/tools/with_venv.sh new file mode 100755 index 00000000..93eaa889 --- /dev/null +++ b/tools/with_venv.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +TOOLS=`dirname $0` +VENV=$TOOLS/../.venv +source $VENV/bin/activate && $@ |