]> git.puffer.fish Git - mirror/frr.git/commitdiff
topotest: added two new sections to guidelines
authorRafael Zalamena <rzalamena@gmail.com>
Mon, 26 Jun 2017 18:38:21 +0000 (15:38 -0300)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 28 Nov 2018 01:22:11 +0000 (20:22 -0500)
And fixed some smaller details in the previous sections.

tests/topotests/GUIDELINES.md

index 855937b055329b3b9b0255bd2dc061d7e96599b5..249837a06b85ce2216d60d9f4273745d0a00e877 100644 (file)
@@ -63,6 +63,7 @@ This is the recommended test writing routine:
 * Write the test itself
 * Create a Pull Request
 
+
 ### Topotest File Hierarchy
 
 Before starting to write any tests one must know the file hierarchy. The
@@ -211,9 +212,9 @@ $ sudo pytest -s --topology-only ospf-topo1/test_ospf_topo1.py
 
 Parameters explanation:
 
-* -s: actives input/output capture. This is required by mininet in order to show
+* `-s`: actives input/output capture. This is required by mininet in order to show
   the interactive shell.
-* --topology-only: don't run any tests, just build the topology.
+* `--topology-only`: don't run any tests, just build the topology.
 
 After executing the commands above, you should get the following terminal
 output:
@@ -351,22 +352,129 @@ frr-1#
 
 ### Writing Tests
 
-**TODO**
+Test topologies should always be bootstrapped from the
+[example-test/test_template.py](example-test/test_template.py),
+because it contains important boilerplate code that can't be avoided, like:
+
+* imports: os, sys, pytest, topotest/topogen and mininet topology class
+* The global variable CWD (Current Working directory): which is most likely
+  going to be used to reference the routers configuration file location
+
+  Example:
+
+```py
+# For all registered routers, load the zebra configuration file
+for rname, router in router_list.iteritems():
+    router.load_config(
+        TopoRouter.RD_ZEBRA,
+        os.path.join(CWD, '{}/zebra.conf'.format(rname))
+    )
+    # os.path.join() joins the CWD string with arguments adding the necessary
+    # slashes ('/'). Arguments must not begin with '/'.
+```
+
+* The topology class that inherits from Mininet Topo class
+
+```py
+class TemplateTopo(Topo):
+  def build(self, *_args, **_opts):
+    tgen = get_topogen(self)
+    # topology build code
+```
+
+* pytest `setup_module()` and `teardown_module()` to start the topology
+
+```py
+def setup_module(_m):
+    tgen = Topogen(TemplateTopo)
+    tgen.start_topology('debug')
+
+def teardown_module(_m):
+    tgen = get_topogen()
+    tgen.stop_topology()
+```
+
+* `__main__` initialization code (to support running the script directly)
+
+```py
+if __name__ == '__main__':
+    sys.exit(pytest.main(["-s"]))
+```
+
+Requirements:
+
+* Test code should always be declared inside functions that begin with the
+  `test_` prefix. Functions beginning with different prefixes will not be run by
+  pytest.
+* Configuration files and long output commands should go into separated files
+  inside folders named after the equipment.
+* Tests must be able to run without any interaction. To make sure your test
+  conforms with this, run it without the `-s` parameter.
 
 
 ### Debugging Execution
 
-**TODO**
+The most effective ways to inspect topology tests are:
+
+* Run pytest with `--pdb` option. This option will cause a pdb shell to appear
+  when an assertion fails
+
+  Example: `pytest -s --pdb ospf-topo1/test_ospf_topo1.py`
+
+* Set a breakpoint in the test code with `pdb`
+
+  Example:
+
+```py
+# Add the pdb import at the beginning of the file
+import pdb
+# ...
+
+# Add a breakpoint where you think the problem is
+def test_bla():
+  # ...
+  pdb.set_trace()
+  # ...
+```
+
+The [Python Debugger](https://docs.python.org/2.7/library/pdb.html) (pdb) shell
+allows us to run many useful operations like:
+
+* Setting breaking point on file/function/conditions (e.g. `break`, `condition`)
+* Inspecting variables (e.g. `p` (print), `pp` (pretty print))
+* Running python code
+
+TIP: The TopoGear (equipment abstraction class) implements the `__str__` method
+that allows the user to inspect equipment information.
+
+Example of pdb usage:
 
 ```shell
->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 > /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(121)test_ospf_convergence()
 -> for rnum in range(1, 5):
-(Pdb) l
-116  
-117     def test_ospf_convergence():
-118         "Test OSPF daemon convergence"
-119         import pdb
+(Pdb) help
+Documented commands (type help <topic>):
+========================================
+EOF    bt         cont      enable  jump  pp       run      unt
+a      c          continue  exit    l     q        s        until
+alias  cl         d         h       list  quit     step     up
+args   clear      debug     help    n     r        tbreak   w
+b      commands   disable   ignore  next  restart  u        whatis
+break  condition  down      j       p     return   unalias  where
+
+Miscellaneous help topics:
+==========================
+exec  pdb
+
+Undocumented commands:
+======================
+retval  rv
+
+(Pdb) list
+116                                   title2="Expected output")
+117
+118     def test_ospf_convergence():
+119         "Test OSPF daemon convergence"
 120         pdb.set_trace()
 121  ->     for rnum in range(1, 5):
 122             router = 'r{}'.format(rnum)
@@ -374,9 +482,37 @@ frr-1#
 124             # Load expected results from the command
 125             reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
 126             expected = open(reffile).read()
+(Pdb) step
+> /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(122)test_ospf_convergence()
+-> router = 'r{}'.format(rnum)
+(Pdb) step
+> /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(125)test_ospf_convergence()
+-> reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
+(Pdb) print rnum
+1
+(Pdb) print router
+r1
 (Pdb) tgen = get_topogen()
-(Pdb) pp tgen.gears['r1']
-<lib.topogen.TopoRouter object at 0x7f2349c98c50>
-(Pdb) pp str(tgen.gears['r1'])
-'TopoGear<name="r1",links=["r1-eth0"<->"switch1-eth0","r1-eth1"<->"switch3-eth0"]> TopoRouter<>'
+(Pdb) pp tgen.gears[router]
+<lib.topogen.TopoRouter object at 0x7f74e06c9850>
+(Pdb) pp str(tgen.gears[router])
+'TopoGear<name="r1",links=["r1-eth0"<->"s1-eth0","r1-eth1"<->"s3-eth0"]> TopoRouter<>'
+(Pdb) l 125
+120         pdb.set_trace()
+121         for rnum in range(1, 5):
+122             router = 'r{}'.format(rnum)
+123
+124             # Load expected results from the command
+125  ->         reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
+126             expected = open(reffile).read()
+127
+128             # Run test function until we get an result. Wait at most 60 seconds.
+129             test_func = partial(compare_show_ip_ospf, router, expected)
+130             result, diff = topotest.run_and_expect(test_func, '',
+(Pdb) router1 = tgen.gears[router]
+(Pdb) router1.vtysh_cmd('show ip ospf route')
+'============ OSPF network routing table ============\r\nN    10.0.1.0/24           [10] area: 0.0.0.0\r\n                           directly attached to r1-eth0\r\nN    10.0.2.0/24           [20] area: 0.0.0.0\r\n                           via 10.0.3.3, r1-eth1\r\nN    10.0.3.0/24           [10] area: 0.0.0.0\r\n                           directly attached to r1-eth1\r\nN    10.0.10.0/24          [20] area: 0.0.0.0\r\n                           via 10.0.3.1, r1-eth1\r\nN IA 172.16.0.0/24         [20] area: 0.0.0.0\r\n                           via 10.0.3.1, r1-eth1\r\nN IA 172.16.1.0/24         [30] area: 0.0.0.0\r\n                           via 10.0.3.1, r1-eth1\r\n\r\n============ OSPF router routing table =============\r\nR    10.0.255.2            [10] area: 0.0.0.0, ASBR\r\n                           via 10.0.3.3, r1-eth1\r\nR    10.0.255.3            [10] area: 0.0.0.0, ABR, ASBR\r\n                           via 10.0.3.1, r1-eth1\r\nR    10.0.255.4         IA [20] area: 0.0.0.0, ASBR\r\n                           via 10.0.3.1, r1-eth1\r\n\r\n============ OSPF external routing table ===========\r\n\r\n\r\n'
+(Pdb) tgen.mininet_cli()
+*** Starting CLI:
+mininet>
 ```