Skip to content

Commit

Permalink
Merge pull request #9 from opensourcerouting/tgen-json
Browse files Browse the repository at this point in the history
Topogen JSON support
  • Loading branch information
mwinter-osr authored Jun 30, 2017
2 parents c876b62 + f0003c3 commit 187f36a
Show file tree
Hide file tree
Showing 8 changed files with 375 additions and 8 deletions.
28 changes: 28 additions & 0 deletions GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,34 @@ Requirements:
* Tests must be able to run without any interaction. To make sure your test
conforms with this, run it without the `-s` parameter.

Tips:

* Keep results in stack variables, so people inspecting code with `pdb` can
easily print their values.

Don't do this:

```py
assert foobar(router1, router2)
```

Do this instead:

```py
result = foobar(router1, router2)
assert result
```

* Use `assert` messages to indicate where the test failed.

Example:

```py
for router in router_list:
# ...
assert condition, 'Router "{}" condition failed'.format(router.name)
```


### Debugging Execution

Expand Down
20 changes: 18 additions & 2 deletions conftest.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

from lib.topogen import get_topogen
from lib.topotest import json_cmp_result
import pytest

def pytest_addoption(parser):
Expand All @@ -21,6 +22,21 @@ def pytest_runtest_call():
# pylint: disable=E1101
# Trust me, 'config' exists.
if pytest.config.getoption('--topology-only'):
# Allow user to play with the setup.
get_topogen().mininet_cli()
tgen = get_topogen()
if tgen is not None:
# Allow user to play with the setup.
tgen.mininet_cli()

pytest.exit('the topology executed successfully')

def pytest_assertrepr_compare(op, left, right):
"""
Show proper assertion error message for json_cmp results.
"""
json_result = left
if not isinstance(json_result, json_cmp_result):
json_result = right
if not isinstance(json_result, json_cmp_result):
return None

return json_result.errors
Empty file modified example-test/__init__.py
100644 → 100755
Empty file.
11 changes: 7 additions & 4 deletions example-test/test_template.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,18 @@
import sys
import pytest

# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, '../'))

# pylint: disable=C0413
# Import topogen and topotest helpers
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen

# Required to instantiate the topology builder class.
from mininet.topo import Topo

# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))

class TemplateTopo(Topo):
"Test topology builder"
def build(self, *_args, **_opts):
Expand Down Expand Up @@ -99,4 +101,5 @@ def test_call_mininet_cli():
tgen.mininet_cli()

if __name__ == '__main__':
sys.exit(pytest.main(["-s"]))
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
Empty file added lib/test/__init__.py
Empty file.
253 changes: 253 additions & 0 deletions lib/test/test_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
#!/usr/bin/env python

#
# test_json.py
# Tests for library function: json_cmp().
#
# Copyright (c) 2017 by
# Network Device Education Foundation, Inc. ("NetDEF")
#
# Permission to use, copy, modify, and/or distribute this software
# for any purpose with or without fee is hereby granted, provided
# that the above copyright notice and this permission notice appear
# in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
# ANY SPECIAL, DIRECT, 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.
#

"""
Tests for the json_cmp() function.
"""

import os
import sys
import pytest

# Save the Current Working Directory to find lib files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, '../../'))

# pylint: disable=C0413
from lib.topotest import json_cmp

def test_json_intersect_true():
"Test simple correct JSON intersections"

dcomplete = {
'i1': 'item1',
'i2': 'item2',
'i3': 'item3',
'i100': 'item4',
}

dsub1 = {
'i1': 'item1',
'i3': 'item3',
}
dsub2 = {
'i1': 'item1',
'i2': 'item2',
}
dsub3 = {
'i100': 'item4',
'i2': 'item2',
}
dsub4 = {
'i50': None,
'i100': 'item4',
}

assert json_cmp(dcomplete, dsub1) is None
assert json_cmp(dcomplete, dsub2) is None
assert json_cmp(dcomplete, dsub3) is None
assert json_cmp(dcomplete, dsub4) is None

def test_json_intersect_false():
"Test simple incorrect JSON intersections"

dcomplete = {
'i1': 'item1',
'i2': 'item2',
'i3': 'item3',
'i100': 'item4',
}

# Incorrect value for 'i1'
dsub1 = {
'i1': 'item3',
'i3': 'item3',
}
# Non-existing key 'i5'
dsub2 = {
'i1': 'item1',
'i5': 'item2',
}
# Key should not exist
dsub3 = {
'i100': None,
}

assert json_cmp(dcomplete, dsub1) is not None
assert json_cmp(dcomplete, dsub2) is not None
assert json_cmp(dcomplete, dsub3) is not None

def test_json_intersect_multilevel_true():
"Test multi level correct JSON intersections"

dcomplete = {
'i1': 'item1',
'i2': 'item2',
'i3': {
'i100': 'item100',
},
'i4': {
'i41': {
'i411': 'item411',
},
'i42': {
'i421': 'item421',
'i422': 'item422',
}
}
}

dsub1 = {
'i1': 'item1',
'i3': {
'i100': 'item100',
},
'i10': None,
}
dsub2 = {
'i1': 'item1',
'i2': 'item2',
'i3': {},
}
dsub3 = {
'i2': 'item2',
'i4': {
'i41': {
'i411': 'item411',
},
'i42': {
'i422': 'item422',
'i450': None,
}
}
}
dsub4 = {
'i2': 'item2',
'i4': {
'i41': {},
'i42': {
'i450': None,
}
}
}
dsub5 = {
'i2': 'item2',
'i3': {
'i100': 'item100',
},
'i4': {
'i42': {
'i450': None,
}
}
}

assert json_cmp(dcomplete, dsub1) is None
assert json_cmp(dcomplete, dsub2) is None
assert json_cmp(dcomplete, dsub3) is None
assert json_cmp(dcomplete, dsub4) is None
assert json_cmp(dcomplete, dsub5) is None

def test_json_intersect_multilevel_false():
"Test multi level incorrect JSON intersections"

dcomplete = {
'i1': 'item1',
'i2': 'item2',
'i3': {
'i100': 'item100',
},
'i4': {
'i41': {
'i411': 'item411',
},
'i42': {
'i421': 'item421',
'i422': 'item422',
}
}
}

# Incorrect sub-level value
dsub1 = {
'i1': 'item1',
'i3': {
'i100': 'item00',
},
'i10': None,
}
# Inexistent sub-level
dsub2 = {
'i1': 'item1',
'i2': 'item2',
'i3': None,
}
# Inexistent sub-level value
dsub3 = {
'i1': 'item1',
'i3': {
'i100': None,
},
}
# Inexistent sub-sub-level value
dsub4 = {
'i4': {
'i41': {
'i412': 'item412',
},
'i42': {
'i421': 'item421',
}
}
}
# Invalid sub-sub-level value
dsub5 = {
'i4': {
'i41': {
'i411': 'item411',
},
'i42': {
'i421': 'item420000',
}
}
}
# sub-sub-level should be value
dsub6 = {
'i4': {
'i41': {
'i411': 'item411',
},
'i42': 'foobar',
}
}

assert json_cmp(dcomplete, dsub1) is not None
assert json_cmp(dcomplete, dsub2) is not None
assert json_cmp(dcomplete, dsub3) is not None
assert json_cmp(dcomplete, dsub4) is not None
assert json_cmp(dcomplete, dsub5) is not None
assert json_cmp(dcomplete, dsub6) is not None

if __name__ == '__main__':
sys.exit(pytest.main())
9 changes: 7 additions & 2 deletions lib/topogen.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

import os
import sys
import json

from mininet.net import Mininet
from mininet.log import setLogLevel
Expand Down Expand Up @@ -388,7 +389,7 @@ def start(self):
"""
return self.tgen.net[self.name].startRouter()

def vtysh_cmd(self, command):
def vtysh_cmd(self, command, isjson=False):
"""
Runs the provided command string in the vty shell and returns a string
with the response.
Expand All @@ -401,7 +402,11 @@ def vtysh_cmd(self, command):
return self.vtysh_multicmd(command)

vtysh_command = 'vtysh -c "{}" 2>/dev/null'.format(command)
return self.run(vtysh_command)
output = self.run(vtysh_command)
if isjson is False:
return output

return json.loads(output)

def vtysh_multicmd(self, commands, pretty_output=True):
"""
Expand Down
Loading

0 comments on commit 187f36a

Please sign in to comment.