import pytest from pathod import language from pathod.language import base, exceptions def parse_request(s): return language.parse_pathoc(s).next() def test_times(): reqs = list(language.parse_pathoc("get:/:x5")) assert len(reqs) == 5 assert not reqs[0].times def test_caseless_literal(): class CL(base.CaselessLiteral): TOK = "foo" v = CL("foo") assert v.expr() assert v.values(language.Settings()) class TestTokValueNakedLiteral: def test_expr(self): v = base.TokValueNakedLiteral("foo") assert v.expr() def test_spec(self): v = base.TokValueNakedLiteral("foo") assert v.spec() == repr(v) == "foo" v = base.TokValueNakedLiteral("f\x00oo") assert v.spec() == repr(v) == r"f\x00oo" class TestTokValueLiteral: def test_expr(self): v = base.TokValueLiteral("foo") assert v.expr() assert v.val == b"foo" v = base.TokValueLiteral("foo\n") assert v.expr() assert v.val == b"foo\n" assert repr(v) def test_spec(self): v = base.TokValueLiteral("foo") assert v.spec() == r"'foo'" v = base.TokValueLiteral("f\x00oo") assert v.spec() == repr(v) == r"'f\x00oo'" v = base.TokValueLiteral('"') assert v.spec() == repr(v) == """ '"' """.strip() # While pyparsing has a escChar argument for QuotedString, # escChar only performs scapes single-character escapes and does not work for e.g. r"\x02". # Thus, we cannot use that option, which means we cannot have single quotes in strings. # To fix this, we represent single quotes as r"\x07". v = base.TokValueLiteral("'") assert v.spec() == r"'\x27'" def roundtrip(self, spec): e = base.TokValueLiteral.expr() v = base.TokValueLiteral(spec) v2 = e.parseString(v.spec()) assert v.val == v2[0].val assert v.spec() == v2[0].spec() def test_roundtrip(self): self.roundtrip("'") self.roundtrip(r"\'") self.roundtrip("a") self.roundtrip("\"") # self.roundtrip("\\") self.roundtrip("200:b'foo':i23,'\\''") self.roundtrip("\a") class TestTokValueGenerate: def test_basic(self): v = base.TokValue.parseString("@10b")[0] assert v.usize == 10 assert v.unit == "b" assert v.bytes() == 10 v = base.TokValue.parseString("@10")[0] assert v.unit == "b" v = base.TokValue.parseString("@10k")[0] assert v.bytes() == 10240 v = base.TokValue.parseString("@10g")[0] assert v.bytes() == 1024 ** 3 * 10 v = base.TokValue.parseString("@10g,digits")[0] assert v.datatype == "digits" g = v.get_generator({}) assert g[:100] v = base.TokValue.parseString("@10,digits")[0] assert v.unit == "b" assert v.datatype == "digits" def test_spec(self): v = base.TokValueGenerate(1, "b", "bytes") assert v.spec() == repr(v) == "@1" v = base.TokValueGenerate(1, "k", "bytes") assert v.spec() == repr(v) == "@1k" v = base.TokValueGenerate(1, "k", "ascii") assert v.spec() == repr(v) == "@1k,ascii" v = base.TokValueGenerate(1, "b", "ascii") assert v.spec() == repr(v) == "@1,ascii" def test_freeze(self): v = base.TokValueGenerate(100, "b", "ascii") f = v.freeze(language.Settings()) assert len(f.val) == 100 class TestTokValueFile: def test_file_value(self): v = base.TokValue.parseString("<'one two'")[0] assert str(v) assert v.path == "one two" v = base.TokValue.parseString("<path")[0] assert v.path == "path" def test_access_control(self, tmpdir): v = base.TokValue.parseString("<path")[0] f = tmpdir.join("path") f.write(b"x" * 10000) assert v.get_generator(language.Settings(staticdir=str(tmpdir))) v = base.TokValue.parseString("<path2")[0] with pytest.raises(exceptions.FileAccessDenied): v.get_generator(language.Settings(staticdir=str(tmpdir))) with pytest.raises(Exception, match="access disabled"): v.get_generator(language.Settings()) v = base.TokValue.parseString("</outside")[0] with pytest.raises(Exception, match="outside"): v.get_generator(language.Settings(staticdir=str(tmpdir))) def test_spec(self): v = base.TokValue.parseString("<'one two'")[0] v2 = base.TokValue.parseString(v.spec())[0] assert v2.path == "one two" def test_freeze(self): v = base.TokValue.parseString("<'one two'")[0] v2 = v.freeze({}) assert v2.path == v.path class TestMisc: def test_generators(self): v = base.TokValue.parseString("'val'")[0] g = v.get_generator({}) assert g[:] == b"val" def test_value(self): assert base.TokValue.parseString("'val'")[0].val == b"val" assert base.TokValue.parseString('"val"')[0].val == b"val" assert base.TokValue.parseString('"\'val\'"')[0].val == b"'val'" def test_value2(self): class TT(base.Value): preamble = "m" e = TT.expr() v = e.parseString("m'msg'")[0] assert v.value.val == b"msg" s = v.spec() assert s == e.parseString(s)[0].spec() v = e.parseString("m@100")[0] v2 = v.freeze({}) v3 = v2.freeze({}) assert v2.value.val == v3.value.val def test_fixedlengthvalue(self, tmpdir): class TT(base.FixedLengthValue): preamble = "m" length = 4 e = TT.expr() assert e.parseString("m@4") with pytest.raises(Exception, match="Invalid value length"): e.parseString("m@100") with pytest.raises(Exception, match="Invalid value length"): e.parseString("m@1") s = base.Settings(staticdir=str(tmpdir)) with open(str(tmpdir.join("path")), 'wb') as f: f.write(b"a" * 20) v = e.parseString("m<path")[0] with pytest.raises(Exception, match="Invalid value length"): v.values(s) with open(str(tmpdir.join("path2")), 'wb') as f: f.write(b"a" * 4) v = e.parseString("m<path2")[0] assert v.values(s) class TKeyValue(base.KeyValue): preamble = "h" def values(self, settings): return [ self.key.get_generator(settings), ": ", self.value.get_generator(settings), "\r\n", ] class TestKeyValue: def test_simple(self): e = TKeyValue.expr() v = e.parseString("h'foo'='bar'")[0] assert v.key.val == b"foo" assert v.value.val == b"bar" v2 = e.parseString(v.spec())[0] assert v2.key.val == v.key.val assert v2.value.val == v.value.val s = v.spec() assert s == e.parseString(s)[0].spec() def test_freeze(self): e = TKeyValue.expr() v = e.parseString("h@10=@10'")[0] v2 = v.freeze({}) v3 = v2.freeze({}) assert v2.key.val == v3.key.val assert v2.value.val == v3.value.val def test_intfield(): class TT(base.IntField): preamble = "t" names = { "one": 1, "two": 2, "three": 3 } max = 4 e = TT.expr() v = e.parseString("tone")[0] assert v.value == 1 assert v.spec() == "tone" assert v.values(language.Settings()) v = e.parseString("t1")[0] assert v.value == 1 assert v.spec() == "t1" v = e.parseString("t4")[0] assert v.value == 4 assert v.spec() == "t4" with pytest.raises(Exception, match="can't exceed"): e.parseString("t5") def test_options_or_value(): class TT(base.OptionsOrValue): options = [ "one", "two", "three" ] e = TT.expr() assert e.parseString("one")[0].value.val == b"one" assert e.parseString("'foo'")[0].value.val == b"foo" assert e.parseString("'get'")[0].value.val == b"get" assert e.parseString("one")[0].spec() == "one" assert e.parseString("'foo'")[0].spec() == "'foo'" s = e.parseString("one")[0].spec() assert s == e.parseString(s)[0].spec() s = e.parseString("'foo'")[0].spec() assert s == e.parseString(s)[0].spec() v = e.parseString("@100")[0] v2 = v.freeze({}) v3 = v2.freeze({}) assert v2.value.val == v3.value.val def test_integer(): e = base.Integer.expr() v = e.parseString("200")[0] assert v.string() == b"200" assert v.spec() == "200" assert v.freeze({}).value == v.value class BInt(base.Integer): bounds = (1, 5) with pytest.raises(Exception, match="must be between"): BInt(0) with pytest.raises(Exception, match="must be between"): BInt(6) assert BInt(5) assert BInt(1) assert BInt(3) class TBoolean(base.Boolean): name = "test" def test_unique_name(): b = TBoolean(True) assert b.unique_name class test_boolean: e = TBoolean.expr() assert e.parseString("test")[0].value assert not e.parseString("-test")[0].value def roundtrip(s): e = TBoolean.expr() s2 = e.parseString(s)[0].spec() v1 = e.parseString(s)[0].value v2 = e.parseString(s2)[0].value assert s == s2 assert v1 == v2 roundtrip("test") roundtrip("-test")