diff options
-rw-r--r-- | libpathod/language.py | 53 | ||||
-rw-r--r-- | test/test_language.py | 163 |
2 files changed, 171 insertions, 45 deletions
diff --git a/libpathod/language.py b/libpathod/language.py index 706dd6b7..0b49c2de 100644 --- a/libpathod/language.py +++ b/libpathod/language.py @@ -209,6 +209,7 @@ class _Token(object): """ return None + @abc.abstractmethod def spec(self): # pragma: no cover """ A parseable specification for this token. @@ -233,6 +234,9 @@ class _ValueLiteral(_Token): def get_generator(self, settings): return LiteralGenerator(self.val) + def freeze(self, settings): + return self + class ValueLiteral(_ValueLiteral): @classmethod @@ -266,6 +270,10 @@ class ValueGenerate(_Token): def get_generator(self, settings): return RandomGenerator(self.datatype, self.bytes()) + def freeze(self, settings): + g = self.get_generator(settings) + return ValueLiteral(g[:].encode("string_escape")) + @classmethod def expr(klass): e = pp.Literal("@").suppress() + v_integer @@ -297,6 +305,9 @@ class ValueFile(_Token): e = e + v_naked_literal return e.setParseAction(lambda x: klass(*x)) + def freeze(self, settings): + return self + def get_generator(self, settings): uf = settings.get("unconstrained_file_access") sd = settings.get("staticdir") @@ -353,6 +364,9 @@ class Raw(_Token): def spec(self): return "r" + def freeze(self, settings): + return self + class _Component(_Token): """ @@ -397,6 +411,9 @@ class Header(_Header): def spec(self): return "h%s=%s"%(self.key.spec(), self.value.spec()) + def freeze(self, settings): + return Header(self.key.freeze(settings), self.value.freeze(settings)) + class ShortcutContentType(_Header): def __init__(self, value): @@ -411,6 +428,9 @@ class ShortcutContentType(_Header): def spec(self): return "c%s"%(self.value.spec()) + def freeze(self, settings): + return ShortcutContentType(self.value.freeze(settings)) + class ShortcutLocation(_Header): def __init__(self, value): @@ -425,6 +445,9 @@ class ShortcutLocation(_Header): def spec(self): return "l%s"%(self.value.spec()) + def freeze(self, settings): + return ShortcutLocation(self.value.freeze(settings)) + class Body(_Component): def __init__(self, value): @@ -444,6 +467,9 @@ class Body(_Component): def spec(self): return "b%s"%(self.value.spec()) + def freeze(self, settings): + return Body(self.value.freeze(settings)) + class Path(_Component): def __init__(self, value): @@ -464,6 +490,9 @@ class Path(_Component): def spec(self): return "%s"%(self.value.spec()) + def freeze(self, settings): + return Path(self.value.freeze(settings)) + class Method(_Component): methods = [ @@ -503,6 +532,9 @@ class Method(_Component): s = s[1:-1].lower() return "%s"%s + def freeze(self, settings): + return Method(self.value.freeze(settings)) + class Code(_Component): def __init__(self, code): @@ -519,6 +551,9 @@ class Code(_Component): def spec(self): return "%s"%(self.code) + def freeze(self, settings): + return Code(self.code) + class Reason(_Component): def __init__(self, value): @@ -536,6 +571,9 @@ class Reason(_Component): def spec(self): return "m%s"%(self.value.spec()) + def freeze(self, settings): + return Reason(self.value.freeze(settings)) + class _Action(_Token): """ @@ -598,6 +636,9 @@ class PauseAt(_Action): def intermediate(self, settings): return (self.offset, "pause", self.seconds) + def freeze(self, settings): + return self + class DisconnectAt(_Action): def __init__(self, offset): @@ -615,6 +656,9 @@ class DisconnectAt(_Action): def intermediate(self, settings): return (self.offset, "disconnect") + def freeze(self, settings): + return self + class InjectAt(_Action): def __init__(self, offset, value): @@ -639,6 +683,9 @@ class InjectAt(_Action): self.value.get_generator(settings) ) + def freeze(self, settings): + return InjectAt(self.offset, self.value.freeze(settings)) + class _Message(object): __metaclass__ = abc.ABCMeta @@ -758,6 +805,12 @@ class _Message(object): vals.append(self.body.value.get_generator(settings)) return vals + def freeze(self, settings): + return self.__class__([i.freeze(settings) for i in self.tokens]) + + def __repr__(self): + return self.spec() + Sep = pp.Optional(pp.Literal(":")).suppress() diff --git a/test/test_language.py b/test/test_language.py index 688dfc8c..a6f735f1 100644 --- a/test/test_language.py +++ b/test/test_language.py @@ -36,6 +36,10 @@ class TestValueLiteral: v = language.ValueLiteral("f\x00oo") assert v.spec() == repr(v) == r'"f\x00oo"' + def test_freeze(self): + v = language.ValueLiteral("foo") + assert v.freeze({}).val == v.val + class TestValueGenerate: def test_basic(self): @@ -72,6 +76,11 @@ class TestValueGenerate: v = language.ValueGenerate(1, "b", "ascii") assert v.spec() == repr(v) == "@1,ascii" + def test_freeze(self): + v = language.ValueGenerate(100, "b", "ascii") + f = v.freeze({}) + assert len(f.val) == 100 + class TestValueFile: def test_file_value(self): @@ -104,6 +113,11 @@ class TestValueFile: v2 = language.Value.parseString(v.spec())[0] assert v2.path == "one two" + def test_freeze(self): + v = language.Value.parseString("<'one two'")[0] + v2 = v.freeze({}) + assert v2.path == v.path + class TestMisc: def test_generators(self): @@ -147,11 +161,17 @@ class TestMisc: def test_path(self): e = language.Path.expr() assert e.parseString('"/foo"')[0].value.val == "/foo" - e = language.Path("/foo") - assert e.value.val == "/foo" - s = e.spec() - assert s == e.expr().parseString(s)[0].spec() + v = language.Path("/foo") + assert v.value.val == "/foo" + + v = e.parseString("@100")[0] + v2 = v.freeze({}) + v3 = v2.freeze({}) + assert v2.value.val == v3.value.val + + s = v.spec() + assert s == v.expr().parseString(s)[0].spec() def test_method(self): e = language.Method.expr() @@ -168,10 +188,16 @@ class TestMisc: 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_raw(self): e = language.Raw.expr().parseString("r")[0] assert e assert e.spec() == "r" + assert e.freeze({}).spec() == "r" def test_body(self): e = language.Body.expr() @@ -180,6 +206,9 @@ class TestMisc: v = e.parseString("b@100")[0] assert str(v.value) == "@100" + v2 = v.freeze({}) + v3 = v2.freeze({}) + assert v2.value.val == v3.value.val v = e.parseString("b@100g,digits", parseAll=True)[0] assert v.value.datatype == "digits" @@ -194,6 +223,8 @@ class TestMisc: assert v.string() == "200" assert v.spec() == "200" + assert v.freeze({}).code == v.code + def test_reason(self): e = language.Reason.expr() v = e.parseString("m'msg'")[0] @@ -202,6 +233,11 @@ class TestMisc: 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_internal_response(self): d = cStringIO.StringIO() s = language.PathodErrorResponse("foo") @@ -222,6 +258,14 @@ class TestHeaders: s = v.spec() assert s == e.parseString(s)[0].spec() + def test_header_freeze(self): + e = language.Header.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_ctype_shortcut(self): e = language.ShortcutContentType.expr() v = e.parseString("c'foo'")[0] @@ -231,6 +275,12 @@ class TestHeaders: s = v.spec() assert s == e.parseString(s)[0].spec() + e = language.ShortcutContentType.expr() + v = e.parseString("c@100")[0] + v2 = v.freeze({}) + v3 = v2.freeze({}) + assert v2.value.val == v3.value.val + def test_location_shortcut(self): e = language.ShortcutLocation.expr() v = e.parseString("l'foo'")[0] @@ -240,12 +290,19 @@ class TestHeaders: s = v.spec() assert s == e.parseString(s)[0].spec() + e = language.ShortcutLocation.expr() + v = e.parseString("l@100")[0] + v2 = v.freeze({}) + v3 = v2.freeze({}) + assert v2.value.val == v3.value.val + def test_shortcut_content_type(self): assert language.parse_response({}, "400:c'foo'").headers[0].key.val == "Content-Type" assert language.parse_response({}, "400:l'foo'").headers[0].key.val == "Location" + class Test_Action: def test_cmp(self): a = language.DisconnectAt(0) @@ -267,6 +324,10 @@ class Test_Action: e = language.DisconnectAt("r") assert repr(e) + def test_freeze(self): + l = language.DisconnectAt(5) + assert l.freeze({}).spec() == l.spec() + class TestDisconnects: def test_parse_response(self): @@ -323,6 +384,13 @@ class TestInject: v = e.parseString("i0,'foo'")[0] assert v.spec() == 'i0,"foo"' + def test_spec(self): + e = language.InjectAt.expr() + v = e.parseString("i0,@100")[0] + v2 = v.freeze({}) + v3 = v2.freeze({}) + assert v2.value.val == v3.value.val + class TestPauses: def test_parse_response(self): @@ -349,9 +417,12 @@ class TestPauses: assert language.PauseAt(0, 5).spec() == "p0,5" assert language.PauseAt(0, "f").spec() == "p0,f" + def test_freeze(self): + l = language.PauseAt("r", 5) + assert l.freeze({}).spec() == l.spec() -class TestParseRequest: +class TestRequest: def test_file(self): p = tutils.test_data.path("data") d = dict(staticdir=p) @@ -413,46 +484,9 @@ class TestParseRequest: rt("get:/foo") rt("get:/foo:da") - -class TestParseResponse: - def test_parse_err(self): - tutils.raises(language.ParseException, language.parse_response, {}, "400:msg,b:") - try: - language.parse_response({}, "400'msg':b:") - except language.ParseException, v: - assert v.marked() - assert str(v) - - def test_nonascii(self): - tutils.raises("ascii", language.parse_response, {}, "foo:b\xf0") - - def test_parse_header(self): - r = language.parse_response({}, '400:h"foo"="bar"') - assert utils.get_header("foo", r.headers) - - def test_parse_pause_before(self): - r = language.parse_response({}, "400:p0,10") - assert r.actions[0].spec() == "p0,10" - - def test_parse_pause_after(self): - r = language.parse_response({}, "400:pa,10") - assert r.actions[0].spec() == "pa,10" - - def test_parse_pause_random(self): - r = language.parse_response({}, "400:pr,10") - assert r.actions[0].spec() == "pr,10" - - def test_parse_stress(self): - r = language.parse_response({}, "400:b@100g") - assert r.length({}) - - def test_spec(self): - def rt(s): - s = language.parse_response({}, s).spec() - assert language.parse_response({}, s).spec() == s - rt("400:b@100g") - rt("400") - rt("400:da") + def test_freeze(self): + r = language.parse_request({}, "GET:/:b@100").freeze({}) + assert len(r.spec()) > 100 class TestWriteValues: @@ -610,6 +644,45 @@ class TestResponse: s = r.preview_safe() assert not "p0" in s.spec() + def test_parse_err(self): + tutils.raises(language.ParseException, language.parse_response, {}, "400:msg,b:") + try: + language.parse_response({}, "400'msg':b:") + except language.ParseException, v: + assert v.marked() + assert str(v) + + def test_nonascii(self): + tutils.raises("ascii", language.parse_response, {}, "foo:b\xf0") + + def test_parse_header(self): + r = language.parse_response({}, '400:h"foo"="bar"') + assert utils.get_header("foo", r.headers) + + def test_parse_pause_before(self): + r = language.parse_response({}, "400:p0,10") + assert r.actions[0].spec() == "p0,10" + + def test_parse_pause_after(self): + r = language.parse_response({}, "400:pa,10") + assert r.actions[0].spec() == "pa,10" + + def test_parse_pause_random(self): + r = language.parse_response({}, "400:pr,10") + assert r.actions[0].spec() == "pr,10" + + def test_parse_stress(self): + r = language.parse_response({}, "400:b@100g") + assert r.length({}) + + def test_spec(self): + def rt(s): + s = language.parse_response({}, s).spec() + assert language.parse_response({}, s).spec() == s + rt("400:b@100g") + rt("400") + rt("400:da") + def test_read_file(): |