diff options
-rwxr-xr-x | cpp/src/tests/cluster_tests.py | 4 | ||||
-rwxr-xr-x | java/testkit/testkit.py | 4 | ||||
-rw-r--r-- | python/qpid/address.py | 127 | ||||
-rw-r--r-- | python/qpid/brokertest.py | 8 | ||||
-rw-r--r-- | python/qpid/tests/address.py | 101 | ||||
-rw-r--r-- | python/qpid/tests/messaging.py | 38 |
6 files changed, 213 insertions, 69 deletions
diff --git a/cpp/src/tests/cluster_tests.py b/cpp/src/tests/cluster_tests.py index 97cfb0d324..637b0aea0f 100755 --- a/cpp/src/tests/cluster_tests.py +++ b/cpp/src/tests/cluster_tests.py @@ -34,8 +34,8 @@ class ClusterTests(BrokerTest): # Start a cluster, send some messages to member 0. cluster = self.cluster(2) s0 = cluster[0].connect().session() - s0.sender("q {create:always}").send(messaging.Message("x")) - s0.sender("q {create:always}").send(messaging.Message("y")) + s0.sender("q; {create:always}").send(messaging.Message("x")) + s0.sender("q; {create:always}").send(messaging.Message("y")) s0.connection.close() # Verify messages available on member 1. diff --git a/java/testkit/testkit.py b/java/testkit/testkit.py index c79c7eadcd..2bb4f234ec 100755 --- a/java/testkit/testkit.py +++ b/java/testkit/testkit.py @@ -42,7 +42,7 @@ class JavaClientTest(BrokerTest): # temp hack: just creating the queue here and closing it. def start_error_watcher(self,broker=None): ssn = broker.connect().session() - err_watcher = ssn.receiver("control {create:always}", capacity=1) + err_watcher = ssn.receiver("control; {create:always}", capacity=1) ssn.close() def client(self,**options): @@ -76,7 +76,7 @@ class JavaClientTest(BrokerTest): # temp hack: just creating a receiver and closing session soon after. def monitor_clients(self,broker=None,run_time=600,error_ck_freq=60): ssn = broker.connect().session() - err_watcher = ssn.receiver("control {create:always}", capacity=1) + err_watcher = ssn.receiver("control; {create:always}", capacity=1) i = run_time/error_ck_freq for j in range(i): try: diff --git a/python/qpid/address.py b/python/qpid/address.py index 3355d1a591..6ad6f23255 100644 --- a/python/qpid/address.py +++ b/python/qpid/address.py @@ -34,22 +34,33 @@ class Type: LBRACE = Type("LBRACE", r"\{") RBRACE = Type("RBRACE", r"\}") COLON = Type("COLON", r":") -COMMA = Type("COMMA", r",") +SEMI = Type("SEMI", r";") SLASH = Type("SLASH", r"/") -ID = Type("ID", r'[a-zA-Z_][a-zA-Z0-9_.#*-]*') +COMMA = Type("COMMA", r",") NUMBER = Type("NUMBER", r'[+-]?[0-9]*\.?[0-9]+') +ID = Type("ID", r'[a-zA-Z_][a-zA-Z0-9_]*') STRING = Type("STRING", r""""(?:[^\\"]|\\.)*"|'(?:[^\\']|\\.)*'""") +ESC = Type("ESC", r"\\[^ux]|\\x[0-9][0-9]|\\u[0-9][0-9][0-9][0-0]") +SYM = Type("SYM", r"[.#*%@$^!+-]") WSPACE = Type("WSPACE", r"[ \n\r\t]+") EOF = Type("EOF") class Token: - def __init__(self, type, value): + def __init__(self, type, value, input, position): self.type = type self.value = value + self.input = input + self.position = position + + def line_info(self): + return line_info(self.input, self.position) def __repr__(self): - return "%s: %r" % (self.type, self.value) + if self.value is None: + return repr(self.type) + else: + return "%s(%r)" % (self.type, self.value) joined = "|".join(["(%s)" % t.pattern for t in TYPES]) LEXER = re.compile(joined) @@ -83,15 +94,51 @@ def lex(st): m = LEXER.match(st, pos) if m is None: line, ln, col = line_info(st, pos) - raise LexError("unrecognized character in <string>:%s,%s: %s" % (ln, col, line)) + raise LexError("unrecognized characters line:%s,%s: %s" % (ln, col, line)) else: idx = m.lastindex - t = Token(TYPES[idx - 1], m.group(idx)) + t = Token(TYPES[idx - 1], m.group(idx), st, pos) yield t pos = m.end() - yield Token(EOF, None) - -class ParseError(Exception): pass + yield Token(EOF, None, st, pos) + +def tok2str(tok): + if tok.type is STRING: + return eval(tok.value) + elif tok.type is ESC: + if tok.value[1] in ("x", "u"): + return eval('"%s"' % tok.value) + else: + return tok.value[1] + else: + return tok.value + +def tok2obj(tok): + if tok.type in (STRING, NUMBER): + return eval(tok.value) + else: + return tok.value + +def toks2str(toks): + if toks: + return "".join(map(tok2str, toks)) + else: + return None + +class ParseError(Exception): + + def __init__(self, token, *expected): + line, ln, col = token.line_info() + exp = ", ".join(map(str, expected)) + if len(expected) > 1: + exp = "(%s)" % exp + if expected: + msg = "expecting %s, got %s line:%s,%s:%s" % (exp, token, ln, col, line) + else: + msg = "unexpected token %s line:%s,%s:%s" % (token, ln, col, line) + Exception.__init__(self, msg) + self.token = token + self.expected = expected class Parser: @@ -107,46 +154,62 @@ class Parser: def eat(self, *types): if types and not self.matches(*types): - raise ParseError("expecting %s -- got %s" % (", ".join(map(str, types)), self.next())) + raise ParseError(self.next(), *types) else: t = self.next() self.idx += 1 return t + def eat_until(self, *types): + result = [] + while not self.matches(*types): + result.append(self.eat()) + return result + def parse(self): result = self.address() self.eat(EOF) return result def address(self): - name = self.eat(ID).value - subject = None - options = None + name = toks2str(self.eat_until(SLASH, SEMI, EOF)) + + if name is None: + raise ParseError(self.next()) + if self.matches(SLASH): self.eat(SLASH) - if self.matches(ID): - subject = self.eat(ID).value - else: - subject = "" - elif self.matches(LBRACE): + subject = toks2str(self.eat_until(SEMI, EOF)) + else: + subject = None + + if self.matches(SEMI): + self.eat(SEMI) options = self.map() + else: + options = None return name, subject, options def map(self): self.eat(LBRACE) + result = {} while True: - if self.matches(RBRACE): - self.eat(RBRACE) - break - else: - if self.matches(ID): - n, v = self.nameval() - result[n] = v - elif self.matches(COMMA): + if self.matches(ID): + n, v = self.nameval() + result[n] = v + if self.matches(COMMA): self.eat(COMMA) + elif self.matches(RBRACE): + break else: - raise ParseError("expecting (ID, COMMA), got %s" % self.next()) + raise ParseError(self.next(), COMMA, RBRACE) + elif self.matches(RBRACE): + break + else: + raise ParseError(self.next(), ID, RBRACE) + + self.eat(RBRACE) return result def nameval(self): @@ -156,16 +219,14 @@ class Parser: return (name, val) def value(self): - if self.matches(NUMBER, STRING): - return eval(self.eat().value) - elif self.matches(ID): - return self.eat().value + if self.matches(NUMBER, STRING, ID): + return tok2obj(self.eat()) elif self.matches(LBRACE): return self.map() else: - raise ParseError("expecting (NUMBER, STRING, LBRACE) got %s" % self.next()) + raise ParseError(self.next(), NUMBER, STRING, ID, LBRACE) def parse(addr): return Parser(lex(addr)).parse() -__all__ = ["parse"] +__all__ = ["parse", "ParseError"] diff --git a/python/qpid/brokertest.py b/python/qpid/brokertest.py index d70990bfae..b6046682de 100644 --- a/python/qpid/brokertest.py +++ b/python/qpid/brokertest.py @@ -176,25 +176,25 @@ class Broker(Popen): def send_message(self, queue, message): s = self.connect().session() - s.sender(queue+" {create:always}").send(message) + s.sender(queue+"; {create:always}").send(message) s.connection.close() def send_messages(self, queue, messages): s = self.connect().session() - sender = s.sender(queue+" {create:always}") + sender = s.sender(queue+"; {create:always}") for m in messages: sender.send(m) s.connection.close() def get_message(self, queue): s = self.connect().session() - m = s.receiver(queue+" {create:always}", capacity=1).fetch(timeout=1) + m = s.receiver(queue+"; {create:always}", capacity=1).fetch(timeout=1) s.acknowledge() s.connection.close() return m def get_messages(self, queue, n): s = self.connect().session() - receiver = s.receiver(queue+" {create:always}", capacity=n) + receiver = s.receiver(queue+"; {create:always}", capacity=n) m = [receiver.fetch(timeout=1) for i in range(n)] s.acknowledge() s.connection.close() diff --git a/python/qpid/tests/address.py b/python/qpid/tests/address.py index 51a9716343..9ce073ee7f 100644 --- a/python/qpid/tests/address.py +++ b/python/qpid/tests/address.py @@ -18,18 +18,101 @@ # from qpid.tests import Test -from qpid.address import parse +from qpid.address import parse, ParseError class AddressTests(Test): + def valid(self, addr, name=None, subject=None, options=None): + expected = (name, subject, options) + got = parse(addr) + assert expected == got, "expected %s, got %s" % (expected, got) + + def invalid(self, addr, error=None): + try: + p = parse(addr) + assert False, "invalid address parsed: %s" % p + except ParseError, e: + assert error == str(e), "expected %r, got %r" % (error, str(e)) + def testHash(self): - name, subject, options = parse("foo/bar.#") - assert name == "foo", name - assert subject == "bar.#", bar - assert options == None + self.valid("foo/bar.#", "foo", "bar.#") def testStar(self): - name, subject, options = parse("foo/bar.*") - assert name == "foo", name - assert subject == "bar.*", bar - assert options == None + self.valid("foo/bar.*", "foo", "bar.*") + + def testColon(self): + self.valid("foo.bar/baz.qux:moo:arf", "foo.bar", "baz.qux:moo:arf") + + def testOptions(self): + self.valid("foo.bar/baz.qux:moo:arf; {key: value}", + "foo.bar", "baz.qux:moo:arf", {"key": "value"}) + + def testOptionsTrailingComma(self): + self.valid("name/subject; {key: value,}", "name", "subject", {"key": "value"}) + + def testSemiSubject(self): + self.valid("foo.bar/'baz.qux;moo:arf'; {key: value}", + "foo.bar", "baz.qux;moo:arf", {"key": "value"}) + + def testCommaSubject(self): + self.valid("foo.bar/baz.qux.{moo,arf}", "foo.bar", "baz.qux.{moo,arf}") + + def testCommaSubjectOptions(self): + self.valid("foo.bar/baz.qux.{moo,arf}; {key: value}", "foo.bar", + "baz.qux.{moo,arf}", {"key": "value"}) + + def testUnbalanced(self): + self.valid("foo.bar/baz.qux.{moo,arf; {key: value}", "foo.bar", + "baz.qux.{moo,arf", {"key": "value"}) + + def testSlashQuote(self): + self.valid("foo.bar\\/baz.qux.{moo,arf; {key: value}", "foo.bar/baz.qux.{moo,arf", + None, {"key": "value"}) + + def testSlashEsc(self): + self.valid("foo.bar\\x00baz.qux.{moo,arf; {key: value}", "foo.bar\x00baz.qux.{moo,arf", + None, {"key": "value"}) + + def testNoName(self): + self.invalid("; {key: value}", "unexpected token SEMI(';') line:1,0:; {key: value}") + + def testEmpty(self): + self.invalid("", "unexpected token EOF line:1,0:") + + def testNoNameSlash(self): + self.invalid("/asdf; {key: value}", + "unexpected token SLASH('/') line:1,0:/asdf; {key: value}") + + def testBadOptions1(self): + self.invalid("name/subject; {", + "expecting (ID, RBRACE), got EOF line:1,15:name/subject; {") + + def testBadOptions2(self): + self.invalid("name/subject; { 3", + "expecting (ID, RBRACE), got NUMBER('3') " + "line:1,16:name/subject; { 3") + + def testBadOptions3(self): + self.invalid("name/subject; { key:", + "expecting (NUMBER, STRING, ID, LBRACE), got EOF " + "line:1,20:name/subject; { key:") + + def testBadOptions4(self): + self.invalid("name/subject; { key: value", + "expecting (COMMA, RBRACE), got EOF " + "line:1,26:name/subject; { key: value") + + def testBadOptions5(self): + self.invalid("name/subject; { key: value asdf", + "expecting (COMMA, RBRACE), got ID('asdf') " + "line:1,27:name/subject; { key: value asdf") + + def testBadOptions6(self): + self.invalid("name/subject; { key: value,", + "expecting (ID, RBRACE), got EOF " + "line:1,27:name/subject; { key: value,") + + def testBadOptions7(self): + self.invalid("name/subject; { key: value } asdf", + "expecting EOF, got ID('asdf') " + "line:1,29:name/subject; { key: value } asdf") diff --git a/python/qpid/tests/messaging.py b/python/qpid/tests/messaging.py index db9e15a01a..830391bf31 100644 --- a/python/qpid/tests/messaging.py +++ b/python/qpid/tests/messaging.py @@ -66,7 +66,7 @@ class Base(Test): return "%s[%s, %s]" % (base, count, self.test_id) def ping(self, ssn): - PING_Q = 'ping-queue {create: always}' + PING_Q = 'ping-queue; {create: always}' # send a message sender = ssn.sender(PING_Q, durable=self.durable()) content = self.content("ping") @@ -190,7 +190,7 @@ class ConnectionTests(Base): self.conn.close() assert not self.conn.connected() -ACK_Q = 'test-ack-queue {create: always}' +ACK_Q = 'test-ack-queue; {create: always}' class SessionTests(Base): @@ -202,7 +202,7 @@ class SessionTests(Base): return self.conn.session() def testSender(self): - snd = self.ssn.sender('test-snd-queue {create: always}', + snd = self.ssn.sender('test-snd-queue; {create: always}', durable=self.durable()) snd2 = self.ssn.sender(snd.target, durable=self.durable()) assert snd is not snd2 @@ -216,7 +216,7 @@ class SessionTests(Base): self.ssn.acknowledge(msg) def testReceiver(self): - rcv = self.ssn.receiver('test-rcv-queue {create: always}') + rcv = self.ssn.receiver('test-rcv-queue; {create: always}') rcv2 = self.ssn.receiver(rcv.source) assert rcv is not rcv2 rcv2.close() @@ -229,7 +229,7 @@ class SessionTests(Base): self.ssn.acknowledge(msg) def testNextReceiver(self): - ADDR = 'test-next-rcv-queue {create: always}' + ADDR = 'test-next-rcv-queue; {create: always}' rcv1 = self.ssn.receiver(ADDR, capacity=UNLIMITED) rcv2 = self.ssn.receiver(ADDR, capacity=UNLIMITED) rcv3 = self.ssn.receiver(ADDR, capacity=UNLIMITED) @@ -258,7 +258,7 @@ class SessionTests(Base): self.ssn.acknowledge() def testStart(self): - START_Q = 'test-start-queue {create: always}' + START_Q = 'test-start-queue; {create: always}' rcv = self.ssn.receiver(START_Q) assert not rcv.started self.ssn.start() @@ -267,7 +267,7 @@ class SessionTests(Base): assert rcv.started def testStop(self): - STOP_Q = 'test-stop-queue {create: always}' + STOP_Q = 'test-stop-queue; {create: always}' self.ssn.start() rcv = self.ssn.receiver(STOP_Q) assert rcv.started @@ -345,8 +345,8 @@ class SessionTests(Base): return contents def txTest(self, commit): - TX_Q = 'test-tx-queue {create: always}' - TX_Q_COPY = 'test-tx-queue-copy {create: always}' + TX_Q = 'test-tx-queue; {create: always}' + TX_Q_COPY = 'test-tx-queue-copy; {create: always}' txssn = self.conn.session(transactional=True) contents = self.send(self.ssn, TX_Q, "txTest", 3) txrcv = txssn.receiver(TX_Q) @@ -376,7 +376,7 @@ class SessionTests(Base): self.txTest(False) def txTestSend(self, commit): - TX_SEND_Q = 'test-tx-send-queue {create: always}' + TX_SEND_Q = 'test-tx-send-queue; {create: always}' txssn = self.conn.session(transactional=True) contents = self.send(txssn, TX_SEND_Q, "txTestSend", 3) rcv = self.ssn.receiver(TX_SEND_Q) @@ -399,7 +399,7 @@ class SessionTests(Base): self.txTestSend(False) def txTestAck(self, commit): - TX_ACK_Q = 'test-tx-ack-queue {create: always}' + TX_ACK_Q = 'test-tx-ack-queue; {create: always}' txssn = self.conn.session(transactional=True) txrcv = txssn.receiver(TX_ACK_Q) self.assertEmpty(txrcv) @@ -444,7 +444,7 @@ class SessionTests(Base): except Disconnected: pass -RECEIVER_Q = 'test-receiver-queue {create: always}' +RECEIVER_Q = 'test-receiver-queue; {create: always}' class ReceiverTests(Base): @@ -581,7 +581,7 @@ class ReceiverTests(Base): # XXX: need testClose NOSUCH_Q = "this-queue-should-not-exist" -UNPARSEABLE_ADDR = "{bad address}" +UNPARSEABLE_ADDR = "name/subject; {bad options" UNLEXABLE_ADDR = "\0x0\0x1\0x2\0x3" class AddressErrorTests(Base): @@ -630,24 +630,24 @@ class AddressErrorTests(Base): def testUnparseableTarget(self): # XXX: should have specific exception for this self.sendErrorTest(UNPARSEABLE_ADDR, SendError, - lambda e: "expecting ID" in str(e)) + lambda e: "expecting COLON" in str(e)) def testUnparseableSource(self): # XXX: should have specific exception for this self.fetchErrorTest(UNPARSEABLE_ADDR, ReceiveError, - lambda e: "expecting ID" in str(e)) + lambda e: "expecting COLON" in str(e)) def testUnlexableTarget(self): # XXX: should have specific exception for this self.sendErrorTest(UNLEXABLE_ADDR, SendError, - lambda e: "unrecognized character" in str(e)) + lambda e: "unrecognized characters" in str(e)) def testUnlexableSource(self): # XXX: should have specific exception for this self.fetchErrorTest(UNLEXABLE_ADDR, ReceiveError, - lambda e: "unrecognized character" in str(e)) + lambda e: "unrecognized characters" in str(e)) -SENDER_Q = 'test-sender-q {create: always}' +SENDER_Q = 'test-sender-q; {create: always}' class SenderTests(Base): @@ -756,7 +756,7 @@ class MessageTests(Base): m.content = u"<html/>" assert m.content_type == "text/html; charset=utf8" -ECHO_Q = 'test-message-echo-queue {create: always}' +ECHO_Q = 'test-message-echo-queue; {create: always}' class MessageEchoTests(Base): |