diff tests/test.py @ 542:f71d34dda19f

Add an optional C-implementation for configmix.config.unquote and configmix.config.pathstr2path. This is currently for Python 3.5+. It is tested with Python 3.7 and Python3.8 (FreeBSD 12.2 amd64, LLVM 10.0.1). A build for the stable API ("abi3") fails because PyUnicode_New() is currently not in the stable API. Also includes are extended tests for unquote() and pathstr2path().
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 31 Dec 2021 21:24:16 +0100
parents be6ef72c55d5
children 491413368c7c
line wrap: on
line diff
--- a/tests/test.py	Wed Dec 29 13:33:11 2021 +0100
+++ b/tests/test.py	Fri Dec 31 21:24:16 2021 +0100
@@ -17,6 +17,7 @@
 import configmix.json
 import configmix.py
 import configmix.toml
+import configmix.config
 from configmix.compat import u, PY2
 
 
@@ -1215,19 +1216,6 @@
             "value",
             self._cfg.getvar_s("events.qc-2021%x2e1-5G-summit.xref.%x23"))
 
-    def test_quoting_and_unquoting_are_inverse(self):
-        for c in """%.:#|"'{}[]""":
-            qc = configmix.quote(c)
-            self.assertTrue(qc.startswith("%x") and len(qc) == 4)
-            self.assertEqual(c, configmix.unquote(qc))
-
-    def test_quoting_and_unquoting_are_identical(self):
-        # other characters
-        for c in """abc09/""":
-            qc = configmix.quote(c)
-            self.assertEqual(c, qc)
-            self.assertEqual(c, configmix.unquote(qc))
-
     def test_namespace_quoting(self):
         v1 = self._cfg.getvar("PY:version")
         v2 = self._cfg.getvar("P%x59:version")
@@ -1784,5 +1772,123 @@
         self.assertEqual(u"in the root namespace", jcfg[2])
 
 
+class _TParserMixin:
+    def test_quote_and_unquote_empty(self):
+        e = configmix.quote(u"")
+        self.assertEqual(u"", e)
+        self.assertEqual(u"", self.unquote(e))
+        
+    def test_quoting_and_unquoting_are_inverse(self):
+        for c in u"""%.:#|"'{}[]""":
+            qc = configmix.quote(c)
+            self.assertTrue(qc.startswith(u"%x") and len(qc) == 4)
+            self.assertEqual(c, self.unquote(qc))
+
+    def test_quoting_and_unquoting_are_identical(self):
+        # other characters
+        for c in configmix.config._QUOTE_SAFE:
+            qc = configmix.quote(c)
+            self.assertEqual(c, qc)
+            self.assertEqual(c, self.unquote(qc))
+
+    def test_unquote_unimax(self):
+        self.assertEqual(u"\U00019001", self.unquote(u"%U00019001"))
+        self.assertEqual(u"X\U00019AF1Z", self.unquote(u"X%U00019aF1Z"))
+
+    def test_unquote_base_plane(self):
+        self.assertEqual(u"\uFFFF", self.unquote(u"%uffff"))
+        self.assertEqual(u"X\uFFFFZ", self.unquote(u"X%uffffZ"))
+
+    def test_unquote_latin(self):
+        self.assertEqual(u"\xFF", self.unquote(u"%xff"))
+        self.assertEqual(u"X\xFFZ", self.unquote(u"X%xffZ"))
+
+    def test_unquote_zero(self):
+        self.assertEqual(u"\x00", self.unquote(u"%x00"))
+        self.assertEqual(u"X\x00Z", self.unquote(u"X%x00Z"))
+
+    def test_unquote_adjacent_x(self):
+        self.assertEqual(u"\x00\x01\xA0\xB0\xFF",
+                         self.unquote(u"%x00%x01%xA0%xB0%xFF"))
+
+    def test_unquote_adjacent_u(self):
+        self.assertEqual(u"\u0000\u0001\u00A0\uABCD\uFEFE",
+                         self.unquote(u"%u0000%u0001%u00A0%uabCD%ufeFE"))
+
+    def test_unquote_adjacent_U(self):
+        self.assertEqual(
+            u"\U00000000\U00000001\U000000A0\U0001ABCD\U0001FEFE",
+            self.unquote(u"%U00000000%U00000001%U000000A0%U0001abCD%U0001feFE"))
+
+    def test_invalid_hex_digits(self):
+        self.assertRaises(
+            ValueError,
+            self.unquote,
+            u"%xgG")
+        self.assertRaises(
+            ValueError,
+            self.unquote,
+            u"%ugGGG")
+        self.assertRaises(
+            ValueError,
+            self.unquote,
+            u"%UgGGGgGGG")
+
+    def test_invalid_too_short(self):
+        self.assertRaises(
+            ValueError,
+            self.unquote,
+            u"%x0")
+        self.assertRaises(
+            ValueError,
+            self.unquote,
+            u"%u000")
+        self.assertRaises(
+            ValueError,
+            self.unquote,
+            u"%U0000000")
+
+    def test_invalid_too_short_no_sigil(self):
+        self.assertRaises(
+            ValueError,
+            self.unquote,
+            u"%")
+
+    def test_empty_pathstr(self):
+        # an empty path string returns an empty path tuple
+        self.assertEqual(tuple(), self.pathstr2path(u""))
+
+    def test_split(self):
+        p = self.pathstr2path(u"abc.def.hik.jkl")
+        self.assertEqual((u"abc", u"def", u"hik", u"jkl"), p)
+
+    def test_split_all_empty_parts(self):
+        p = self.pathstr2path(u"....")
+        self.assertEqual((u"", u"", u"", u"", u""), p)
+
+    def test_split_all_empty_tail(self):
+        p = self.pathstr2path(u"1.2.")
+        self.assertEqual((u"1", u"2", u""), p)
+
+    def test_split_unquote(self):
+        p = self.pathstr2path(u"a%x2Eb.c%u002Ed.e%U0000002Ef")
+        self.assertEqual((u"a.b", u"c.d", u"e.f"), p)
+
+
+class T09Parser(_TParserMixin, unittest.TestCase):
+
+    def setUp(self):
+        self.unquote = configmix.config.py_unquote
+        self.pathstr2path = configmix.config.py_pathstr2path
+
+
+if configmix.config.fast_unquote is not None:
+    class T10FastParser(_TParserMixin, unittest.TestCase):
+
+        def setUp(self):
+            self.unquote = configmix.config.fast_unquote
+            self.pathstr2path = configmix.config.fast_pathstr2path
+
+
 if __name__ == "__main__":
     unittest.main()