From 869039df5d19310638610a70aeba0c78dfbc1fa1 Mon Sep 17 00:00:00 2001
From: Jan Dittberner <jan@dittberner.info>
Date: Mon, 2 May 2016 20:55:09 +0200
Subject: [PATCH] Add test suite

This commit adds a test suite and fixes replacement of ip address nodes
for IP addresses that are not part of a IP range directive.
---
 .gitignore               |  1 +
 jandd/sphinxext/ip.py    | 12 ++++------
 tests/root/conf.py       |  6 ++---
 tests/root/index.rst     |  2 ++
 tests/root/testpage1.rst |  6 +++++
 tests/root/testpage2.rst |  8 +++++++
 tests/run.py             | 43 ++++++++++++++++++++++++++++++++++
 tests/test_ip.py         | 50 ++++++++++++++++++++++++++++++++++++----
 8 files changed, 113 insertions(+), 15 deletions(-)
 create mode 100644 tests/root/testpage1.rst
 create mode 100644 tests/root/testpage2.rst
 create mode 100755 tests/run.py

diff --git a/.gitignore b/.gitignore
index 9d7cebc..d475091 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
 __pycache__/
 dist/
 .coverage
+htmlcov/
diff --git a/jandd/sphinxext/ip.py b/jandd/sphinxext/ip.py
index d0f6340..0842260 100644
--- a/jandd/sphinxext/ip.py
+++ b/jandd/sphinxext/ip.py
@@ -189,18 +189,17 @@ class IPDomain(Domain):
             if role is None:
                 return None
             resnode = role.result_nodes(env.get_doctree(fromdocname),
-                                        env, node, True)[0][0]
+                                        env, node, True)[0][2]
             if isinstance(resnode, addnodes.pending_xref):
                 text = node[0][0]
                 reporter = env.get_doctree(fromdocname).reporter
                 reporter.warning('Cannot resolve reference to %r' % text,
                                  line=node.line)
-                return None
+                return node.children
             return resnode
         else:
             title = typ.upper() + ' ' + target
-            anchor = ip_object_anchor(typ, target)
-            return make_refnode(builder, fromdocname, info[0], anchor,
+            return make_refnode(builder, fromdocname, info[0], key,
                                 contnode, title)
 
     @property
@@ -227,7 +226,8 @@ def process_ips(app, doctree):
             'ip': ip,
             'typ': node.parent['typ'],
         })
-        node.replace_self(nodes.literal('', ip))
+        replacement = nodes.literal(ip, ip)
+        node.replace_self(replacement)
 
 
 def sort_ip_info(item):
@@ -260,8 +260,6 @@ def process_ip_nodes(app, doctree, fromdocname):
                 para += nodes.Text(" in ")
                 para += newnode
                 content.append(para)
-
-                #print(ip_info, 'in', node['rangespec'])
         node.replace_self(content)
 
 
diff --git a/tests/root/conf.py b/tests/root/conf.py
index f3e3836..040557f 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -13,13 +13,13 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys
-import os
+#import sys
+#import os
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.insert(0, os.path.abspath('..'))
+#sys.path.insert(0, os.path.abspath('..'))
 
 # -- General configuration ------------------------------------------------
 
diff --git a/tests/root/index.rst b/tests/root/index.rst
index 0765e9e..8ada3e1 100644
--- a/tests/root/index.rst
+++ b/tests/root/index.rst
@@ -11,6 +11,8 @@ Contents:
 .. toctree::
    :maxdepth: 2
 
+   testpage1
+   testpage2
 
 
 Indices and tables
diff --git a/tests/root/testpage1.rst b/tests/root/testpage1.rst
new file mode 100644
index 0000000..9d20815
--- /dev/null
+++ b/tests/root/testpage1.rst
@@ -0,0 +1,6 @@
+Test page 1
+===========
+
+.. ip:v4range:: 192.168.0.1/24
+
+.. ip:v6range:: 2001:dead:beef::/64
diff --git a/tests/root/testpage2.rst b/tests/root/testpage2.rst
new file mode 100644
index 0000000..f7c3351
--- /dev/null
+++ b/tests/root/testpage2.rst
@@ -0,0 +1,8 @@
+Test page 2
+===========
+
+This page contains IP addresses :ip:v4:`127.0.0.1`, :ip:v4:`192.168.0.1` and
+:ip:v6:`2001:dead:beef::1` as well as :ip:v6:`::1`.
+
+There is also :ip:v6range:`2001:dada:b001::/64` and
+:ip:v4range:`172.16.0.0/24`.
diff --git a/tests/run.py b/tests/run.py
new file mode 100755
index 0000000..95236c2
--- /dev/null
+++ b/tests/run.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+jandd.sphinxext.ip unit test driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This script runs the jandd.sphinxext.ip unit test suite.
+
+:copyright: Copyright 2016 Jan Dittberner
+:license: GPLv3+, see COPYING for details.
+"""
+
+import sys
+from os import path
+import unittest
+
+
+def run(extra_args=[]):
+    sys.path.insert(0, path.join(path.dirname(__file__), path.pardir))
+    sys.path.insert(1, path.abspath(
+        path.join(path.dirname(__file__), path.pardir,
+                  'jandd', 'sphinxext', 'ip'
+        ))
+    )
+
+    try:
+        import sphinx
+    except ImportError:
+        print("The sphinx package is needed to run the jandd.sphinxext.ip "
+              "test suite.")
+
+    import test_ip
+
+    print("Running jandd.sphinxext.ip test suite ...")
+
+    suite = unittest.TestLoader().loadTestsFromTestCase(
+        test_ip.TestIPExtension
+    )
+    unittest.TextTestRunner(verbosity=2).run(suite)
+
+
+if __name__ == '__main__':
+    run()
diff --git a/tests/test_ip.py b/tests/test_ip.py
index 4d7a466..8d83348 100644
--- a/tests/test_ip.py
+++ b/tests/test_ip.py
@@ -1,20 +1,60 @@
 # -*- coding: utf-8 -*-
 
 from io import StringIO
-from .util import TestApp, test_root
+from util import TestApp, test_root
 import unittest
 
 
+IP4_ADDRESSES = ['127.0.0.1', '192.168.0.1']
+IP6_ADDRESSES = ['::1', '2001:dead:beef::1']
+IP4_RANGES = ['172.16.0.0/24', '192.168.0.0/24']
+IP6_RANGES = ['2001:dead:beef::/64', '2001:dada:b001::/64']
+
+
 class TestIPExtension(unittest.TestCase):
     def setUp(self):
         if not (test_root / '_static').exists():
             (test_root / '_static').mkdir()
         self.feed_warnfile = StringIO()
+        self.app = TestApp(
+            buildername='html', warning=self.feed_warnfile, cleanenv=True)
+        self.app.build(force_all=True, filenames=[])
 
     def tearDown(self):
+        self.app.cleanup()
         (test_root / '_build').rmtree(True)
 
-    def test_ip4_role(self):
-        feed_warnfile = self.feed_warnfile
-        app = TestApp(buildername='html', warning=feed_warnfile, cleanenv=True)
-        app.build(force_all=True, filenames=[])
+    def test_ip_domaindata(self):
+        self.assertIn('ip', self.app.env.domaindata)
+        ipdomdata = self.app.env.domaindata['ip']
+        self.assertIn('v4', ipdomdata)
+        self.assertIn('v6', ipdomdata)
+        self.assertIn('v4range', ipdomdata)
+        self.assertIn('v6range', ipdomdata)
+        self.assertIn('ips', ipdomdata)
+
+    def find_in_index(self, entry):
+        indexentries = self.app.env.indexentries
+        for index in indexentries:
+            for value in indexentries[index]:
+                if value[1] == entry:
+                    return
+        self.fail("%s not found in index" % entry)
+
+    def test_ip4_addresses(self):
+        ipv4 = self.app.env.domaindata['ip']['v4']
+        ips = self.app.env.domaindata['ip']['ips']
+        for ip in IP4_ADDRESSES:
+            self.assertIn(ip, ipv4)
+            self.assertIn(ip, [item['ip'] for item in ips])
+            self.find_in_index("IPv4 address; %s" % ip)
+            self.find_in_index("%s; Test page 2" % ip)
+
+    def test_ip6_addresses(self):
+        ipv6 = self.app.env.domaindata['ip']['v6']
+        ips = self.app.env.domaindata['ip']['ips']
+        for ip in IP6_ADDRESSES:
+            self.assertIn(ip, ipv6)
+            self.assertIn(ip, [item['ip'] for item in ips])
+            self.find_in_index("IPv6 address; %s" % ip)
+            self.find_in_index("%s; Test page 2" % ip)