summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2010-08-25 10:26:40 -0700
committerBen Pfaff <blp@nicira.com>2010-08-25 14:55:48 -0700
commit991559357f6a03c3a5b70c053c8c2554aa8d5ee4 (patch)
tree8731002433c65ea41dbe94648c0e39737f666469 /tests
parentd1b680c61626595b2777f4bf25997a9178acb60c (diff)
downloadopenvswitch-991559357f6a03c3a5b70c053c8c2554aa8d5ee4.tar.gz
Implement initial Python bindings for Open vSwitch database.
These initial bindings pass a few hundred of the corresponding tests for C implementations of various bits of the Open vSwitch library API. The poorest part of them is actually the Python IDL interface in ovs.db.idl, which has not received enough attention yet. It appears to work, but it doesn't yet support writes (transactions) and it is difficult to use. I hope to improve it as it becomes clear what semantics Python applications actually want from an IDL.
Diffstat (limited to 'tests')
-rw-r--r--tests/atlocal.in3
-rw-r--r--tests/automake.mk11
-rw-r--r--tests/daemon-py.at185
-rw-r--r--tests/daemon.at2
-rw-r--r--tests/json.at45
-rw-r--r--tests/jsonrpc-py.at48
-rw-r--r--tests/jsonrpc.at2
-rw-r--r--tests/ovsdb-column.at6
-rw-r--r--tests/ovsdb-data.at104
-rw-r--r--tests/ovsdb-idl-py.at149
-rw-r--r--tests/ovsdb-schema.at6
-rw-r--r--tests/ovsdb-table.at14
-rw-r--r--tests/ovsdb-types.at108
-rw-r--r--tests/ovsdb.at57
-rw-r--r--tests/reconnect.at18
-rw-r--r--tests/test-daemon.py66
-rw-r--r--tests/test-json.py92
-rw-r--r--tests/test-jsonrpc.c2
-rw-r--r--tests/test-jsonrpc.py221
-rw-r--r--tests/test-ovsdb.py372
-rw-r--r--tests/test-reconnect.py195
-rw-r--r--tests/testsuite.at2
22 files changed, 1581 insertions, 127 deletions
diff --git a/tests/atlocal.in b/tests/atlocal.in
index 8ac4f676b..f1c045766 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -3,3 +3,6 @@ HAVE_OPENSSL='@HAVE_OPENSSL@'
HAVE_PYTHON='@HAVE_PYTHON@'
PERL='@PERL@'
PYTHON='@PYTHON@'
+
+PYTHONPATH=$PYTHONPATH:$abs_top_srcdir/python
+export PYTHONPATH
diff --git a/tests/automake.mk b/tests/automake.mk
index 9a248feb9..e647bbb99 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -11,12 +11,14 @@ TESTSUITE_AT = \
tests/classifier.at \
tests/check-structs.at \
tests/daemon.at \
+ tests/daemon-py.at \
tests/vconn.at \
tests/dir_name.at \
tests/aes128.at \
tests/uuid.at \
tests/json.at \
tests/jsonrpc.at \
+ tests/jsonrpc-py.at \
tests/timeval.at \
tests/lockfile.at \
tests/reconnect.at \
@@ -38,6 +40,7 @@ TESTSUITE_AT = \
tests/ovsdb-server.at \
tests/ovsdb-monitor.at \
tests/ovsdb-idl.at \
+ tests/ovsdb-idl-py.at \
tests/ovs-vsctl.at \
tests/interface-reconfigure.at
TESTSUITE = $(srcdir)/tests/testsuite
@@ -263,3 +266,11 @@ EXTRA_DIST += \
tests/testpki-privkey2.pem \
tests/testpki-req.pem \
tests/testpki-req2.pem
+
+# Python tests.
+EXTRA_DIST += \
+ tests/test-daemon.py \
+ tests/test-json.py \
+ tests/test-jsonrpc.py \
+ tests/test-ovsdb.py \
+ tests/test-reconnect.py
diff --git a/tests/daemon-py.at b/tests/daemon-py.at
new file mode 100644
index 000000000..7ff376eb7
--- /dev/null
+++ b/tests/daemon-py.at
@@ -0,0 +1,185 @@
+AT_BANNER([daemon unit tests - Python])
+
+AT_SETUP([daemon - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CAPTURE_FILE([pid])
+AT_CAPTURE_FILE([expected])
+# Start the daemon and wait for the pidfile to get created
+# and that its contents are the correct pid.
+AT_CHECK([$PYTHON $srcdir/test-daemon.py --pidfile-name=$PWD/pid& echo $! > expected], [0], [ignore], [ignore])
+OVS_WAIT_UNTIL([test -s pid], [kill `cat expected`])
+AT_CHECK(
+ [pid=`cat pid` && expected=`cat expected` && test "$pid" = "$expected"],
+ [0], [], [], [kill `cat expected`])
+AT_CHECK([kill -0 `cat pid`], [0], [], [], [kill `cat expected`])
+# Kill the daemon and make sure that the pidfile gets deleted.
+kill `cat expected`
+OVS_WAIT_WHILE([kill -0 `cat expected`])
+AT_CHECK([test ! -e pid])
+AT_CLEANUP
+
+AT_SETUP([daemon --monitor - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CAPTURE_FILE([pid])
+AT_CAPTURE_FILE([parent])
+AT_CAPTURE_FILE([parentpid])
+AT_CAPTURE_FILE([newpid])
+# Start the daemon and wait for the pidfile to get created.
+AT_CHECK([$PYTHON $srcdir/test-daemon.py --pidfile-name=$PWD/pid --monitor& echo $! > parent], [0], [ignore], [ignore])
+OVS_WAIT_UNTIL([test -s pid], [kill `cat parent`])
+# Check that the pidfile names a running process,
+# and that the parent process of that process is our child process.
+AT_CHECK([kill -0 `cat pid`], [0], [], [], [kill `cat parent`])
+AT_CHECK([ps -o ppid= -p `cat pid` > parentpid],
+ [0], [], [], [kill `cat parent`])
+AT_CHECK(
+ [parentpid=`cat parentpid` &&
+ parent=`cat parent` &&
+ test $parentpid = $parent],
+ [0], [], [], [kill `cat parent`])
+# Kill the daemon process, making it look like a segfault,
+# and wait for a new child process to get spawned.
+AT_CHECK([cp pid oldpid], [0], [], [], [kill `cat parent`])
+AT_CHECK([kill -SEGV `cat pid`], [0], [], [ignore], [kill `cat parent`])
+OVS_WAIT_WHILE([kill -0 `cat oldpid`], [kill `cat parent`])
+OVS_WAIT_UNTIL([test -s pid && test `cat pid` != `cat oldpid`],
+ [kill `cat parent`])
+AT_CHECK([cp pid newpid], [0], [], [], [kill `cat parent`])
+# Check that the pidfile names a running process,
+# and that the parent process of that process is our child process.
+AT_CHECK([ps -o ppid= -p `cat pid` > parentpid],
+ [0], [], [], [kill `cat parent`])
+AT_CHECK(
+ [parentpid=`cat parentpid` &&
+ parent=`cat parent` &&
+ test $parentpid = $parent],
+ [0], [], [], [kill `cat parent`])
+# Kill the daemon process with SIGTERM, and wait for the daemon
+# and the monitor processes to go away and the pidfile to get deleted.
+AT_CHECK([kill `cat pid`], [0], [], [ignore], [kill `cat parent`])
+OVS_WAIT_WHILE([kill -0 `cat parent` || kill -0 `cat newpid` || test -e pid],
+ [kill `cat parent`])
+AT_CLEANUP
+
+AT_SETUP([daemon --detach - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CAPTURE_FILE([pid])
+# Start the daemon and make sure that the pidfile exists immediately.
+# We don't wait for the pidfile to get created because the daemon is
+# supposed to do so before the parent exits.
+AT_CHECK([$PYTHON $srcdir/test-daemon.py --pidfile-name=$PWD/pid --detach], [0], [ignore], [ignore])
+AT_CHECK([test -s pid])
+AT_CHECK([kill -0 `cat pid`])
+# Kill the daemon and make sure that the pidfile gets deleted.
+cp pid saved-pid
+kill `cat pid`
+OVS_WAIT_WHILE([kill -0 `cat saved-pid`])
+AT_CHECK([test ! -e pid])
+AT_CLEANUP
+
+AT_SETUP([daemon --detach --monitor - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+m4_define([CHECK],
+ [AT_CHECK([$1], [$2], [$3], [$4], [kill `cat daemon monitor`])])
+AT_CAPTURE_FILE([daemon])
+AT_CAPTURE_FILE([olddaemon])
+AT_CAPTURE_FILE([newdaemon])
+AT_CAPTURE_FILE([monitor])
+AT_CAPTURE_FILE([newmonitor])
+AT_CAPTURE_FILE([init])
+# Start the daemon and make sure that the pidfile exists immediately.
+# We don't wait for the pidfile to get created because the daemon is
+# supposed to do so before the parent exits.
+AT_CHECK([$PYTHON $srcdir/test-daemon.py --pidfile-name=$PWD/daemon --detach --monitor], [0], [ignore], [ignore])
+AT_CHECK([test -s daemon])
+# Check that the pidfile names a running process,
+# and that the parent process of that process is a running process,
+# and that the parent process of that process is init.
+CHECK([kill -0 `cat daemon`])
+CHECK([ps -o ppid= -p `cat daemon` > monitor])
+CHECK([kill -0 `cat monitor`])
+CHECK([ps -o ppid= -p `cat monitor` > init])
+CHECK([test `cat init` = 1])
+# Kill the daemon process, making it look like a segfault,
+# and wait for a new daemon process to get spawned.
+CHECK([cp daemon olddaemon])
+CHECK([kill -SEGV `cat daemon`], [0], [ignore], [ignore])
+OVS_WAIT_WHILE([kill -0 `cat olddaemon`], [kill `cat olddaemon daemon`])
+OVS_WAIT_UNTIL([test -s daemon && test `cat daemon` != `cat olddaemon`],
+ [kill `cat olddaemon daemon`])
+CHECK([cp daemon newdaemon])
+# Check that the pidfile names a running process,
+# and that the parent process of that process is our child process.
+CHECK([kill -0 `cat daemon`])
+CHECK([diff olddaemon newdaemon], [1], [ignore])
+CHECK([ps -o ppid= -p `cat daemon` > newmonitor])
+CHECK([diff monitor newmonitor])
+CHECK([kill -0 `cat newmonitor`])
+CHECK([ps -o ppid= -p `cat newmonitor` > init])
+CHECK([test `cat init` = 1])
+# Kill the daemon process with SIGTERM, and wait for the daemon
+# and the monitor processes to go away and the pidfile to get deleted.
+CHECK([kill `cat daemon`], [0], [], [ignore])
+OVS_WAIT_WHILE(
+ [kill -0 `cat monitor` || kill -0 `cat newdaemon` || test -e daemon],
+ [kill `cat monitor newdaemon`])
+m4_undefine([CHECK])
+AT_CLEANUP
+
+AT_SETUP([daemon --detach startup errors - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CAPTURE_FILE([pid])
+AT_CHECK([$PYTHON $srcdir/test-daemon.py --pidfile-name=$PWD/pid --detach --bail], [1], [], [stderr])
+AT_CHECK([grep 'test-daemon.py: exiting after daemonize_start() as requested' stderr],
+ [0], [ignore], [])
+AT_CHECK([test ! -s pid])
+AT_CLEANUP
+
+AT_SETUP([daemon --detach --monitor startup errors - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CAPTURE_FILE([pid])
+AT_CHECK([$PYTHON $srcdir/test-daemon.py --pidfile-name=$PWD/pid --detach --monitor --bail], [1], [], [stderr])
+AT_CHECK([grep 'test-daemon.py: exiting after daemonize_start() as requested' stderr],
+ [0], [ignore], [])
+AT_CHECK([test ! -s pid])
+AT_CLEANUP
+
+AT_SETUP([daemon --detach closes standard fds - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CAPTURE_FILE([pid])
+AT_CAPTURE_FILE([status])
+AT_CAPTURE_FILE([stderr])
+AT_CHECK([(yes 2>stderr; echo $? > status) | $PYTHON $srcdir/test-daemon.py --pidfile-name=$PWD/pid --detach], [0], [], [])
+AT_CHECK([kill `cat pid`])
+AT_CHECK([test -s status])
+if grep '[[bB]]roken pipe' stderr >/dev/null 2>&1; then
+ # Something in the environment caused SIGPIPE to be ignored, but
+ # 'yes' at least told us that it got EPIPE. Good enough; we know
+ # that stdout was closed.
+ :
+else
+ # Otherwise make sure that 'yes' died from SIGPIPE.
+ AT_CHECK([kill -l `cat status`], [0], [PIPE
+])
+fi
+AT_CLEANUP
+
+AT_SETUP([daemon --detach --monitor closes standard fds])
+AT_CAPTURE_FILE([pid])
+AT_CAPTURE_FILE([status])
+AT_CAPTURE_FILE([stderr])
+OVSDB_INIT([db])
+AT_CHECK([(yes 2>stderr; echo $? > status) | $PYTHON $srcdir/test-daemon.py --pidfile-name=$PWD/pid --detach], [0], [], [])
+AT_CHECK([kill `cat pid`])
+AT_CHECK([test -s status])
+if grep '[[bB]]roken pipe' stderr >/dev/null 2>&1; then
+ # Something in the environment caused SIGPIPE to be ignored, but
+ # 'yes' at least told us that it got EPIPE. Good enough; we know
+ # that stdout was closed.
+ :
+else
+ # Otherwise make sure that 'yes' died from SIGPIPE.
+ AT_CHECK([kill -l `cat status`], [0], [PIPE
+])
+fi
+AT_CLEANUP
diff --git a/tests/daemon.at b/tests/daemon.at
index 06f1e6125..d3708637f 100644
--- a/tests/daemon.at
+++ b/tests/daemon.at
@@ -1,4 +1,4 @@
-AT_BANNER([daemon unit tests])
+AT_BANNER([daemon unit tests - C])
AT_SETUP([daemon])
AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov wrapper make pids differ
diff --git a/tests/json.at b/tests/json.at
index af53d76f4..56329ed25 100644
--- a/tests/json.at
+++ b/tests/json.at
@@ -1,4 +1,4 @@
-m4_define([JSON_CHECK_POSITIVE],
+m4_define([JSON_CHECK_POSITIVE_C],
[AT_SETUP([$1])
AT_KEYWORDS([json positive])
AT_CHECK([printf %s "AS_ESCAPE([$2])" > input])
@@ -8,7 +8,22 @@ m4_define([JSON_CHECK_POSITIVE],
])
AT_CLEANUP])
-m4_define([JSON_CHECK_NEGATIVE],
+m4_define([JSON_CHECK_POSITIVE_PY],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([json positive Python])
+ AT_SKIP_IF([test $HAVE_PYTHON = no])
+ AT_CHECK([printf %s "AS_ESCAPE([$2])" > input])
+ AT_CAPTURE_FILE([input])
+ AT_CHECK([$PYTHON $srcdir/test-json.py $4 input], [0], [stdout], [])
+ AT_CHECK([cat stdout], [0], [$3
+])
+ AT_CLEANUP])
+
+m4_define([JSON_CHECK_POSITIVE],
+ [JSON_CHECK_POSITIVE_C([$1 - C], [$2], [$3], [$4])
+ JSON_CHECK_POSITIVE_PY([$1 - Python], [$2], [$3], [$4])])
+
+m4_define([JSON_CHECK_NEGATIVE_C],
[AT_SETUP([$1])
AT_KEYWORDS([json negative])
AT_CHECK([printf %s "AS_ESCAPE([$2])" > input])
@@ -18,6 +33,21 @@ m4_define([JSON_CHECK_NEGATIVE],
])
AT_CLEANUP])
+m4_define([JSON_CHECK_NEGATIVE_PY],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([json negative Python])
+ AT_SKIP_IF([test $HAVE_PYTHON = no])
+ AT_CHECK([printf %s "AS_ESCAPE([$2])" > input])
+ AT_CAPTURE_FILE([input])
+ AT_CHECK([$PYTHON $srcdir/test-json.py $4 input], [1], [stdout], [])
+ AT_CHECK([[sed 's/^error: [^:]*:/error:/' < stdout]], [0], [$3
+])
+ AT_CLEANUP])
+
+m4_define([JSON_CHECK_NEGATIVE],
+ [JSON_CHECK_NEGATIVE_C([$1 - C], [$2], [$3], [$4])
+ JSON_CHECK_NEGATIVE_PY([$1 - Python], [$2], [$3], [$4])])
+
AT_BANNER([JSON -- arrays])
JSON_CHECK_POSITIVE([empty array], [[ [ ] ]], [[[]]])
@@ -76,13 +106,22 @@ JSON_CHECK_NEGATIVE([null bytes not allowed],
[[["\u0000"]]],
[error: null bytes not supported in quoted strings])
-AT_SETUP([end of input in quoted string])
+AT_SETUP([end of input in quoted string - C])
AT_KEYWORDS([json negative])
AT_CHECK([printf '"xxx' | test-json -], [1],
[error: line 0, column 4, byte 4: unexpected end of input in quoted string
])
AT_CLEANUP
+AT_SETUP([end of input in quoted string - Python])
+AT_KEYWORDS([json negative Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CHECK([printf '"xxx' > input
+$PYTHON $srcdir/test-json.py input], [1],
+ [error: line 0, column 4, byte 4: unexpected end of input in quoted string
+])
+AT_CLEANUP
+
AT_BANNER([JSON -- objects])
JSON_CHECK_POSITIVE([empty object], [[{ }]], [[{}]])
diff --git a/tests/jsonrpc-py.at b/tests/jsonrpc-py.at
new file mode 100644
index 000000000..e8a98bbce
--- /dev/null
+++ b/tests/jsonrpc-py.at
@@ -0,0 +1,48 @@
+AT_BANNER([JSON-RPC - Python])
+
+AT_SETUP([JSON-RPC request and successful reply - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CHECK([$PYTHON $srcdir/test-jsonrpc.py --detach --pidfile-name=$PWD/pid listen punix:socket])
+AT_CHECK([test -s pid])
+AT_CHECK([kill -0 `cat pid`])
+AT_CHECK(
+ [[$PYTHON $srcdir/test-jsonrpc.py request unix:socket echo '[{"a": "b", "x": null}]']], [0],
+ [[{"error":null,"id":0,"result":[{"a":"b","x":null}]}
+]], [ignore], [test ! -e pid || kill `cat pid`])
+AT_CHECK([kill `cat pid`])
+AT_CLEANUP
+
+AT_SETUP([JSON-RPC request and error reply - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CHECK([$PYTHON $srcdir/test-jsonrpc.py --detach --pidfile-name=$PWD/pid listen punix:socket])
+AT_CHECK([test -s pid])
+AT_CHECK([kill -0 `cat pid`])
+AT_CHECK(
+ [[$PYTHON $srcdir/test-jsonrpc.py request unix:socket bad-request '[]']], [0],
+ [[{"error":{"error":"unknown method"},"id":0,"result":null}
+]], [ignore], [test ! -e pid || kill `cat pid`])
+AT_CHECK([kill `cat pid`])
+AT_CLEANUP
+
+AT_SETUP([JSON-RPC notification - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CHECK([$PYTHON $srcdir/test-jsonrpc.py --detach --pidfile-name=$PWD/pid listen punix:socket])
+AT_CHECK([test -s pid])
+# When a daemon dies it deletes its pidfile, so make a copy.
+AT_CHECK([cp pid pid2])
+AT_CHECK([kill -0 `cat pid2`])
+AT_CHECK([[$PYTHON $srcdir/test-jsonrpc.py notify unix:socket shutdown '[]']], [0], [],
+ [ignore], [kill `cat pid2`])
+AT_CHECK(
+ [pid=`cat pid2`
+ # First try a quick sleep, so that the test completes very quickly
+ # in the normal case. POSIX doesn't require fractional times to
+ # work, so this might not work.
+ sleep 0.1; if kill -0 $pid; then :; else echo success; exit 0; fi
+ # Then wait up to 2 seconds.
+ sleep 1; if kill -0 $pid; then :; else echo success; exit 0; fi
+ sleep 1; if kill -0 $pid; then :; else echo success; exit 0; fi
+ echo failure; exit 1], [0], [success
+], [ignore])
+AT_CHECK([test ! -e pid])
+AT_CLEANUP
diff --git a/tests/jsonrpc.at b/tests/jsonrpc.at
index 83410f990..856fa46ee 100644
--- a/tests/jsonrpc.at
+++ b/tests/jsonrpc.at
@@ -1,4 +1,4 @@
-AT_BANNER([JSON-RPC])
+AT_BANNER([JSON-RPC - C])
AT_SETUP([JSON-RPC request and successful reply])
AT_CHECK([test-jsonrpc --detach --pidfile=$PWD/pid listen punix:socket])
diff --git a/tests/ovsdb-column.at b/tests/ovsdb-column.at
index 7dd55e413..b8d093991 100644
--- a/tests/ovsdb-column.at
+++ b/tests/ovsdb-column.at
@@ -1,13 +1,13 @@
AT_BANNER([OVSDB -- columns])
-OVSDB_CHECK_POSITIVE([ordinary column],
+OVSDB_CHECK_POSITIVE_CPY([ordinary column],
[[parse-column mycol '{"type": "integer"}']],
[[{"type":"integer"}]])
-OVSDB_CHECK_POSITIVE([immutable column],
+OVSDB_CHECK_POSITIVE_CPY([immutable column],
[[parse-column mycol '{"type": "real", "mutable": false}']],
[[{"mutable":false,"type":"real"}]])
-OVSDB_CHECK_POSITIVE([ephemeral column],
+OVSDB_CHECK_POSITIVE_CPY([ephemeral column],
[[parse-column mycol '{"type": "uuid", "ephemeral": true}']],
[[{"ephemeral":true,"type":"uuid"}]])
diff --git a/tests/ovsdb-data.at b/tests/ovsdb-data.at
index ac0f0b7be..98e810837 100644
--- a/tests/ovsdb-data.at
+++ b/tests/ovsdb-data.at
@@ -1,6 +1,6 @@
AT_BANNER([OVSDB -- default values])
-OVSDB_CHECK_POSITIVE([default atoms],
+OVSDB_CHECK_POSITIVE_CPY([default atoms],
[default-atoms],
[[integer: OK
real: OK
@@ -8,7 +8,7 @@ boolean: OK
string: OK
uuid: OK]])
-OVSDB_CHECK_POSITIVE([default data],
+OVSDB_CHECK_POSITIVE_CPY([default data],
[default-data],
[[key integer, value void, n_min 0: OK
key integer, value integer, n_min 0: OK
@@ -73,7 +73,7 @@ key uuid, value uuid, n_min 1: OK]])
AT_BANNER([OVSDB -- atoms without constraints])
-OVSDB_CHECK_POSITIVE([integer atom from JSON],
+OVSDB_CHECK_POSITIVE_CPY([integer atom from JSON],
[[parse-atoms '["integer"]' \
'[0]' \
'[-1]' \
@@ -99,7 +99,7 @@ OVSDB_CHECK_POSITIVE([integer atom from string],
9223372036854775807
-9223372036854775808])
-OVSDB_CHECK_POSITIVE([real atom from JSON],
+OVSDB_CHECK_POSITIVE_CPY([real atom from JSON],
[[parse-atoms '["real"]' \
'[0]' \
'[0.0]' \
@@ -133,7 +133,7 @@ OVSDB_CHECK_POSITIVE([real atom from string],
1e+37
0.00390625])
-OVSDB_CHECK_POSITIVE([boolean atom from JSON],
+OVSDB_CHECK_POSITIVE_CPY([boolean atom from JSON],
[[parse-atoms '["boolean"]' '[true]' '[false]' ]],
[true
false])
@@ -143,7 +143,7 @@ OVSDB_CHECK_POSITIVE([boolean atom from string],
[true
false])
-OVSDB_CHECK_POSITIVE([string atom from JSON],
+OVSDB_CHECK_POSITIVE_CPY([string atom from JSON],
[[parse-atoms '["string"]' '[""]' '["true"]' '["\"\\\/\b\f\n\r\t"]']],
[""
"true"
@@ -164,7 +164,7 @@ quoted-string
"true"
"\"\\/\b\f\n\r\t"])
-OVSDB_CHECK_POSITIVE([uuid atom from JSON],
+OVSDB_CHECK_POSITIVE_CPY([uuid atom from JSON],
[[parse-atoms '["uuid"]' '["uuid", "550e8400-e29b-41d4-a716-446655440000"]']],
[[["uuid","550e8400-e29b-41d4-a716-446655440000"]]])
@@ -172,23 +172,23 @@ OVSDB_CHECK_POSITIVE([uuid atom from string],
[[parse-atom-strings '["uuid"]' '550e8400-e29b-41d4-a716-446655440000']],
[550e8400-e29b-41d4-a716-446655440000])
-OVSDB_CHECK_POSITIVE([integer atom sorting],
+OVSDB_CHECK_POSITIVE_CPY([integer atom sorting],
[[sort-atoms '["integer"]' '[55,0,-1,2,1]']],
[[[-1,0,1,2,55]]])
-OVSDB_CHECK_POSITIVE([real atom sorting],
+OVSDB_CHECK_POSITIVE_CPY([real atom sorting],
[[sort-atoms '["real"]' '[1.25,1.23,0.0,-0.0,-1e99]']],
[[[-1e+99,0,0,1.23,1.25]]])
-OVSDB_CHECK_POSITIVE([boolean atom sorting],
+OVSDB_CHECK_POSITIVE_CPY([boolean atom sorting],
[[sort-atoms '["boolean"]' '[true,false,true,false,false]']],
[[[false,false,false,true,true]]])
-OVSDB_CHECK_POSITIVE([string atom sorting],
+OVSDB_CHECK_POSITIVE_CPY([string atom sorting],
[[sort-atoms '["string"]' '["abd","abc","\b","xxx"]']],
[[["\b","abc","abd","xxx"]]])
-OVSDB_CHECK_POSITIVE([uuid atom sorting],
+OVSDB_CHECK_POSITIVE_CPY([uuid atom sorting],
[[sort-atoms '["uuid"]' '[
["uuid", "00000000-0000-0000-0000-000000000001"],
["uuid", "00000000-1000-0000-0000-000000000000"],
@@ -225,25 +225,26 @@ OVSDB_CHECK_POSITIVE([uuid atom sorting],
["uuid", "00001000-0000-0000-0000-000000000000"]]']],
[[[["uuid","00000000-0000-0000-0000-000000000000"],["uuid","00000000-0000-0000-0000-000000000001"],["uuid","00000000-0000-0000-0000-000000000010"],["uuid","00000000-0000-0000-0000-000000000100"],["uuid","00000000-0000-0000-0000-000000001000"],["uuid","00000000-0000-0000-0000-000000010000"],["uuid","00000000-0000-0000-0000-000000100000"],["uuid","00000000-0000-0000-0000-000001000000"],["uuid","00000000-0000-0000-0000-000010000000"],["uuid","00000000-0000-0000-0000-000100000000"],["uuid","00000000-0000-0000-0000-001000000000"],["uuid","00000000-0000-0000-0000-010000000000"],["uuid","00000000-0000-0000-0000-100000000000"],["uuid","00000000-0000-0000-0001-000000000000"],["uuid","00000000-0000-0000-0010-000000000000"],["uuid","00000000-0000-0000-0100-000000000000"],["uuid","00000000-0000-0000-1000-000000000000"],["uuid","00000000-0000-0001-0000-000000000000"],["uuid","00000000-0000-0010-0000-000000000000"],["uuid","00000000-0000-0100-0000-000000000000"],["uuid","00000000-0000-1000-0000-000000000000"],["uuid","00000000-0001-0000-0000-000000000000"],["uuid","00000000-0010-0000-0000-000000000000"],["uuid","00000000-0100-0000-0000-000000000000"],["uuid","00000000-1000-0000-0000-000000000000"],["uuid","00000001-0000-0000-0000-000000000000"],["uuid","00000010-0000-0000-0000-000000000000"],["uuid","00000100-0000-0000-0000-000000000000"],["uuid","00001000-0000-0000-0000-000000000000"],["uuid","00010000-0000-0000-0000-000000000000"],["uuid","00100000-0000-0000-0000-000000000000"],["uuid","01000000-0000-0000-0000-000000000000"],["uuid","10000000-0000-0000-0000-000000000000"]]]])
-OVSDB_CHECK_POSITIVE([real not acceptable integer JSON atom],
+OVSDB_CHECK_POSITIVE_CPY([real not acceptable integer JSON atom],
[[parse-atoms '["integer"]' '[0.5]' ]],
[syntax "0.5": syntax error: expected integer])
dnl <C0> is not allowed anywhere in a UTF-8 string.
dnl <ED A0 80> is a surrogate and not allowed in UTF-8.
-OVSDB_CHECK_POSITIVE([no invalid UTF-8 sequences in strings],
+OVSDB_CHECK_POSITIVE_CPY([no invalid UTF-8 sequences in strings],
[parse-atoms '[["string"]]' \
'@<:@"m4_esyscmd([printf "\300"])"@:>@' \
'@<:@"m4_esyscmd([printf "\355\240\200"])"@:>@' \
],
[constraint violation: "m4_esyscmd([printf "\300"])" is not a valid UTF-8 string: invalid UTF-8 sequence 0xc0
-constraint violation: "m4_esyscmd([printf "\355\240\200"])" is not a valid UTF-8 string: invalid UTF-8 sequence 0xed 0xa0])
+constraint violation: "m4_esyscmd([printf "\355\240\200"])" is not a valid UTF-8 string: invalid UTF-8 sequence 0xed 0xa0],
+ [], [], [xfail])
OVSDB_CHECK_NEGATIVE([real not acceptable integer string atom],
[[parse-atom-strings '["integer"]' '0.5' ]],
["0.5" is not a valid integer])
-OVSDB_CHECK_POSITIVE([string "true" not acceptable boolean JSON atom],
+OVSDB_CHECK_POSITIVE_CPY([string "true" not acceptable boolean JSON atom],
[[parse-atoms '["boolean"]' '["true"]' ]],
[syntax ""true"": syntax error: expected boolean])
@@ -251,11 +252,11 @@ OVSDB_CHECK_NEGATIVE([string "true" not acceptable boolean string atom],
[[parse-atom-strings '["boolean"]' '"true"' ]],
[""true"" is not a valid boolean (use "true" or "false")])
-OVSDB_CHECK_POSITIVE([integer not acceptable string JSON atom],
+OVSDB_CHECK_POSITIVE_CPY([integer not acceptable string JSON atom],
[[parse-atoms '["string"]' '[1]']],
[syntax "1": syntax error: expected string])
-OVSDB_CHECK_POSITIVE([uuid atom must be expressed as JSON array],
+OVSDB_CHECK_POSITIVE_CPY([uuid atom must be expressed as JSON array],
[[parse-atoms '["uuid"]' '["550e8400-e29b-41d4-a716-446655440000"]']],
[[syntax ""550e8400-e29b-41d4-a716-446655440000"": syntax error: expected ["uuid", <string>]]])
@@ -273,7 +274,7 @@ OVSDB_CHECK_NEGATIVE([uuids must be valid],
AT_BANNER([OVSDB -- atoms with enum constraints])
-OVSDB_CHECK_POSITIVE([integer atom enum],
+OVSDB_CHECK_POSITIVE_CPY([integer atom enum],
[[parse-atoms '[{"type": "integer", "enum": ["set", [1, 6, 8, 10]]}]' \
'[0]' \
'[1]' \
@@ -296,7 +297,7 @@ constraint violation: 9 is not one of the allowed values ([1, 6, 8, 10])
10
constraint violation: 11 is not one of the allowed values ([1, 6, 8, 10])]])
-OVSDB_CHECK_POSITIVE([real atom enum],
+OVSDB_CHECK_POSITIVE_CPY([real atom enum],
[[parse-atoms '[{"type": "real", "enum": ["set", [-1.5, 1.5]]}]' \
'[-2]' \
'[-1]' \
@@ -313,14 +314,14 @@ constraint violation: 1 is not one of the allowed values ([-1.5, 1.5])
1.5
constraint violation: 2 is not one of the allowed values ([-1.5, 1.5])]])
-OVSDB_CHECK_POSITIVE([boolean atom enum],
+OVSDB_CHECK_POSITIVE_CPY([boolean atom enum],
[[parse-atoms '[{"type": "boolean", "enum": false}]' \
'[false]' \
'[true]']],
[[false
constraint violation: true is not one of the allowed values ([false])]])
-OVSDB_CHECK_POSITIVE([string atom enum],
+OVSDB_CHECK_POSITIVE_CPY([string atom enum],
[[parse-atoms '[{"type": "string", "enum": ["set", ["abc", "def"]]}]' \
'[""]' \
'["ab"]' \
@@ -335,7 +336,7 @@ constraint violation: ab is not one of the allowed values ([abc, def])
constraint violation: defg is not one of the allowed values ([abc, def])
constraint violation: DEF is not one of the allowed values ([abc, def])]])
-OVSDB_CHECK_POSITIVE([uuid atom enum],
+OVSDB_CHECK_POSITIVE_CPY([uuid atom enum],
[[parse-atoms '[{"type": "uuid", "enum": ["set", [["uuid", "6d53a6dd-2da7-4924-9927-97f613812382"], ["uuid", "52cbc842-137a-4db5-804f-9f34106a0ba3"]]]}]' \
'["uuid", "6d53a6dd-2da7-4924-9927-97f613812382"]' \
'["uuid", "52cbc842-137a-4db5-804f-9f34106a0ba3"]' \
@@ -346,7 +347,7 @@ constraint violation: dab2a6b2-6094-4f43-a7ef-4c0f0608f176 is not one of the all
AT_BANNER([OVSDB -- atoms with other constraints])
-OVSDB_CHECK_POSITIVE([integers >= 5],
+OVSDB_CHECK_POSITIVE_CPY([integers >= 5],
[[parse-atoms '[{"type": "integer", "minInteger": 5}]' \
'[0]' \
'[4]' \
@@ -359,7 +360,7 @@ constraint violation: 4 is less than minimum allowed value 5
6
12345])
-OVSDB_CHECK_POSITIVE([integers <= -1],
+OVSDB_CHECK_POSITIVE_CPY([integers <= -1],
[[parse-atoms '[{"type": "integer", "maxInteger": -1}]' \
'[0]' \
'[-1]' \
@@ -370,7 +371,7 @@ OVSDB_CHECK_POSITIVE([integers <= -1],
-2
-123])
-OVSDB_CHECK_POSITIVE([integers in range -10 to 10],
+OVSDB_CHECK_POSITIVE_CPY([integers in range -10 to 10],
[[parse-atoms '[{"type": "integer", "minInteger": -10, "maxInteger": 10}]' \
'[-20]' \
'[-11]' \
@@ -391,7 +392,7 @@ constraint violation: -11 is not in the valid range -10 to 10 (inclusive)
constraint violation: 11 is not in the valid range -10 to 10 (inclusive)
constraint violation: 123576 is not in the valid range -10 to 10 (inclusive)])
-OVSDB_CHECK_POSITIVE([reals >= 5],
+OVSDB_CHECK_POSITIVE_CPY([reals >= 5],
[[parse-atoms '[{"type": "real", "minReal": 5}]' \
'[0]' \
'[4]' \
@@ -404,7 +405,7 @@ constraint violation: 4 is less than minimum allowed value 5
6
12345])
-OVSDB_CHECK_POSITIVE([reals <= -1],
+OVSDB_CHECK_POSITIVE_CPY([reals <= -1],
[[parse-atoms '[{"type": "real", "maxReal": -1}]' \
'[0]' \
'[-1]' \
@@ -415,7 +416,7 @@ OVSDB_CHECK_POSITIVE([reals <= -1],
-2
-123])
-OVSDB_CHECK_POSITIVE([reals in range -10 to 10],
+OVSDB_CHECK_POSITIVE_CPY([reals in range -10 to 10],
[[parse-atoms '[{"type": "real", "minReal": -10, "maxReal": 10}]' \
'[-20]' \
'[-11]' \
@@ -436,7 +437,7 @@ constraint violation: -11 is not in the valid range -10 to 10 (inclusive)
constraint violation: 11 is not in the valid range -10 to 10 (inclusive)
constraint violation: 123576 is not in the valid range -10 to 10 (inclusive)])
-OVSDB_CHECK_POSITIVE([strings at least 2 characters long],
+OVSDB_CHECK_POSITIVE_CPY([strings at least 2 characters long],
[[parse-atoms '{"type": "string", "minLength": 2}' \
'[""]' \
'["a"]' \
@@ -447,9 +448,10 @@ OVSDB_CHECK_POSITIVE([strings at least 2 characters long],
constraint violation: "a" length 1 is less than minimum allowed length 2
"ab"
"abc"
-constraint violation: "𝄞" length 1 is less than minimum allowed length 2]])
+constraint violation: "𝄞" length 1 is less than minimum allowed length 2]],
+ [], [], [xfail])
-OVSDB_CHECK_POSITIVE([strings no more than 2 characters long],
+OVSDB_CHECK_POSITIVE_CPY([strings no more than 2 characters long],
[[parse-atoms '{"type": "string", "maxLength": 2}' \
'[""]' \
'["a"]' \
@@ -464,7 +466,7 @@ constraint violation: "abc" length 3 is greater than maximum allowed length 2
AT_BANNER([OSVDB -- simple data])
-OVSDB_CHECK_POSITIVE([integer JSON datum],
+OVSDB_CHECK_POSITIVE_CPY([integer JSON datum],
[[parse-data '["integer"]' '[0]' '["set",[1]]' '[-1]']],
[0
1
@@ -477,7 +479,7 @@ OVSDB_CHECK_POSITIVE([integer string datum],
-1
1])
-OVSDB_CHECK_POSITIVE([real JSON datum],
+OVSDB_CHECK_POSITIVE_CPY([real JSON datum],
[[parse-data '["real"]' '[0]' '["set",[1.0]]' '[-1.25]']],
[0
1
@@ -489,7 +491,7 @@ OVSDB_CHECK_POSITIVE([real string datum],
1
-1.25])
-OVSDB_CHECK_POSITIVE([boolean JSON datum],
+OVSDB_CHECK_POSITIVE_CPY([boolean JSON datum],
[[parse-data '["boolean"]' '["set", [true]]' '[false]' ]],
[true
false])
@@ -499,7 +501,7 @@ OVSDB_CHECK_POSITIVE([boolean string datum],
[true
false])
-OVSDB_CHECK_POSITIVE([string JSON datum],
+OVSDB_CHECK_POSITIVE_CPY([string JSON datum],
[[parse-data '["string"]' '["set",[""]]' '["true"]' '["\"\\\/\b\f\n\r\t"]']],
[""
"true"
@@ -514,7 +516,7 @@ OVSDB_CHECK_POSITIVE([string string datum],
AT_BANNER([OVSDB -- set data])
-OVSDB_CHECK_POSITIVE([JSON optional boolean],
+OVSDB_CHECK_POSITIVE_CPY([JSON optional boolean],
[[parse-data '{"key": "boolean", "min": 0}' \
'[true]' \
'["set", [false]]' \
@@ -534,7 +536,7 @@ false
[]]],
[set])
-OVSDB_CHECK_POSITIVE([JSON set of 0 or more integers],
+OVSDB_CHECK_POSITIVE_CPY([JSON set of 0 or more integers],
[[parse-data '{"key": "integer", "min": 0, "max": "unlimited"}' \
'["set", [0]]' \
'[1]' \
@@ -566,7 +568,7 @@ OVSDB_CHECK_POSITIVE([string set of 0 or more integers],
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]])
-OVSDB_CHECK_POSITIVE([JSON set of 1 to 3 uuids],
+OVSDB_CHECK_POSITIVE_CPY([JSON set of 1 to 3 uuids],
[[parse-data '{"key": "uuid", "min": 1, "max": 3}' \
'["set", [["uuid", "550e8400-e29b-41d4-a716-446655440000"]]]' \
'["uuid", "b5078be0-7664-4299-b836-8bcc03ef941f"]' \
@@ -586,7 +588,7 @@ OVSDB_CHECK_POSITIVE([string set of 1 to 3 uuids],
[[[550e8400-e29b-41d4-a716-446655440000]
[550e8400-e29b-41d4-a716-446655440000, 90558331-09af-4d2f-a572-509cad2e9088, c5051240-30ff-43ed-b4b9-93cf3f050813]]])
-OVSDB_CHECK_POSITIVE([JSON set of 0 to 3 strings],
+OVSDB_CHECK_POSITIVE_CPY([JSON set of 0 to 3 strings],
[[parse-data '{"key": "string", "min": 0, "max": 3}' \
'["set", []]' \
'["a longer string"]' \
@@ -610,7 +612,7 @@ OVSDB_CHECK_POSITIVE([string set of 0 to 3 strings],
["a relatively long string", "short string"]
["a relatively long string", "short string", zzz]]])
-OVSDB_CHECK_NEGATIVE([duplicate boolean not allowed in JSON set],
+OVSDB_CHECK_NEGATIVE_CPY([duplicate boolean not allowed in JSON set],
[[parse-data '{"key": "boolean", "max": 5}' '["set", [true, true]]']],
[ovsdb error: set contains duplicate])
@@ -618,7 +620,7 @@ OVSDB_CHECK_NEGATIVE([duplicate boolean not allowed in string set],
[[parse-data-strings '{"key": "boolean", "max": 5}' 'true, true']],
[set contains duplicate value])
-OVSDB_CHECK_NEGATIVE([duplicate integer not allowed in JSON set],
+OVSDB_CHECK_NEGATIVE_CPY([duplicate integer not allowed in JSON set],
[[parse-data '{"key": "integer", "max": 5}' '["set", [1, 2, 3, 1]]']],
[ovsdb error: set contains duplicate])
@@ -626,7 +628,7 @@ OVSDB_CHECK_NEGATIVE([duplicate integer not allowed in string set],
[[parse-data-strings '{"key": "integer", "max": 5}' '[1, 2, 3, 1]']],
[set contains duplicate value])
-OVSDB_CHECK_NEGATIVE([duplicate real not allowed in JSON set],
+OVSDB_CHECK_NEGATIVE_CPY([duplicate real not allowed in JSON set],
[[parse-data '{"key": "real", "max": 5}' '["set", [0.0, -0.0]]']],
[ovsdb error: set contains duplicate])
@@ -634,7 +636,7 @@ OVSDB_CHECK_NEGATIVE([duplicate real not allowed in string set],
[[parse-data-strings '{"key": "real", "max": 5}' '0.0, -0.0']],
[set contains duplicate value])
-OVSDB_CHECK_NEGATIVE([duplicate string not allowed in JSON set],
+OVSDB_CHECK_NEGATIVE_CPY([duplicate string not allowed in JSON set],
[[parse-data '{"key": "string", "max": 5}' '["set", ["asdf", "ASDF", "asdf"]]']],
[ovsdb error: set contains duplicate])
@@ -642,7 +644,7 @@ OVSDB_CHECK_NEGATIVE([duplicate string not allowed in string set],
[[parse-data-strings '{"key": "string", "max": 5}' 'asdf, ASDF, "asdf"']],
[set contains duplicate value])
-OVSDB_CHECK_NEGATIVE([duplicate uuid not allowed in JSON set],
+OVSDB_CHECK_NEGATIVE_CPY([duplicate uuid not allowed in JSON set],
[[parse-data '{"key": "uuid", "max": 5}' \
'["set", [["uuid", "7ef21525-0088-4a28-a418-5518413e43ea"],
["uuid", "355ad037-f1da-40aa-b47c-ff9c7e8c6a38"],
@@ -658,7 +660,7 @@ OVSDB_CHECK_NEGATIVE([duplicate uuid not allowed in string set],
AT_BANNER([OVSDB -- map data])
-OVSDB_CHECK_POSITIVE([JSON map of 1 integer to boolean],
+OVSDB_CHECK_POSITIVE_CPY([JSON map of 1 integer to boolean],
[[parse-data '{"key": "integer", "value": "boolean"}' \
'["map", [[1, true]]]']],
[[["map",[[1,true]]]]])
@@ -668,7 +670,7 @@ OVSDB_CHECK_POSITIVE([string map of 1 integer to boolean],
'1=true']],
[[1=true]])
-OVSDB_CHECK_POSITIVE([JSON map of at least 1 integer to boolean],
+OVSDB_CHECK_POSITIVE_CPY([JSON map of at least 1 integer to boolean],
[[parse-data '{"key": "integer", "value": "boolean", "max": "unlimited"}' \
'["map", [[1, true]]]' \
'["map", [[0, true], [1, false], [2, true], [3, true], [4, true]]]' \
@@ -686,7 +688,7 @@ OVSDB_CHECK_POSITIVE([string map of at least 1 integer to boolean],
{0=true, 1=false, 2=true, 3=true, 4=true}
{0=true, 3=false, 4=false}]])
-OVSDB_CHECK_POSITIVE([JSON map of 1 boolean to integer],
+OVSDB_CHECK_POSITIVE_CPY([JSON map of 1 boolean to integer],
[[parse-data '{"key": "boolean", "value": "integer"}' \
'["map", [[true, 1]]]']],
[[["map",[[true,1]]]]])
@@ -696,7 +698,7 @@ OVSDB_CHECK_POSITIVE([string map of 1 boolean to integer],
'true=1']],
[[true=1]])
-OVSDB_CHECK_POSITIVE([JSON map of 1 uuid to real],
+OVSDB_CHECK_POSITIVE_CPY([JSON map of 1 uuid to real],
[[parse-data '{"key": "uuid", "value": "real", "min": 1, "max": 5}' \
'["map", [[["uuid", "cad8542b-6ee1-486b-971b-7dcbf6e14979"], 1.0],
[["uuid", "6b94b968-2702-4f64-9457-314a34d69b8c"], 2.0],
@@ -714,7 +716,7 @@ OVSDB_CHECK_POSITIVE([string map of 1 uuid to real],
1c92b8ca-d5e4-4628-a85d-1dc2d099a99a=5.0']],
[[{1c92b8ca-d5e4-4628-a85d-1dc2d099a99a=5, 25bfa475-d072-4f60-8be1-00f48643e9cb=4, 6b94b968-2702-4f64-9457-314a34d69b8c=2, cad8542b-6ee1-486b-971b-7dcbf6e14979=1, d2c4a168-24de-47eb-a8a3-c1abfc814979=3}]])
-OVSDB_CHECK_POSITIVE([JSON map of 10 string to string],
+OVSDB_CHECK_POSITIVE_CPY([JSON map of 10 string to string],
[[parse-data '{"key": "string", "value": "string", "min": 1, "max": 10}' \
'["map", [["2 gills", "1 chopin"],
["2 chopins", "1 pint"],
@@ -742,7 +744,7 @@ OVSDB_CHECK_POSITIVE([string map of 10 string to string],
"2 kilderkins"= "1 barrel"}']],
[[{"2 chopins"="1 pint", "2 demibushel"="1 firkin", "2 firkins"="1 kilderkin", "2 gallons"="1 peck", "2 gills"="1 chopin", "2 kilderkins"="1 barrel", "2 pecks"="1 demibushel", "2 pints"="1 quart", "2 pottles"="1 gallon", "2 quarts"="1 pottle"}]])
-OVSDB_CHECK_NEGATIVE([duplicate integer key not allowed in JSON map],
+OVSDB_CHECK_NEGATIVE_CPY([duplicate integer key not allowed in JSON map],
[[parse-data '{"key": "integer", "value": "boolean", "max": 5}' \
'["map", [[1, true], [2, false], [1, false]]]']],
[ovsdb error: map contains duplicate key])
diff --git a/tests/ovsdb-idl-py.at b/tests/ovsdb-idl-py.at
new file mode 100644
index 000000000..5f7661bf2
--- /dev/null
+++ b/tests/ovsdb-idl-py.at
@@ -0,0 +1,149 @@
+AT_BANNER([OVSDB -- interface description language (IDL) - Python])
+
+# OVSDB_CHECK_IDL(TITLE, [PRE-IDL-TXN], TRANSACTIONS, OUTPUT, [KEYWORDS],
+# [FILTER])
+#
+# Creates a database with a schema derived from idltest.ovsidl, runs
+# each PRE-IDL-TXN (if any), starts an ovsdb-server on that database,
+# and runs "test-ovsdb idl" passing each of the TRANSACTIONS along.
+#
+# Checks that the overall output is OUTPUT. Before comparison, the
+# output is sorted (using "sort") and UUIDs in the output are replaced
+# by markers of the form <N> where N is a number. The first unique
+# UUID is replaced by <0>, the next by <1>, and so on. If a given
+# UUID appears more than once it is always replaced by the same
+# marker. If FILTER is supplied then the output is also filtered
+# through the specified program.
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_IDL_PY],
+ [AT_SETUP([$1])
+ AT_SKIP_IF([test $HAVE_PYTHON = no])
+ AT_KEYWORDS([ovsdb server idl positive Python $5])
+ AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
+ [0], [stdout], [ignore])
+ AT_CHECK([ovsdb-server '-vPATTERN:console:ovsdb-server|%c|%m' --detach --pidfile=$PWD/pid --remote=punix:socket --unixctl=$PWD/unixctl db], [0], [ignore], [ignore])
+ m4_if([$2], [], [],
+ [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore], [kill `cat pid`])])
+ AT_CHECK([$PYTHON $srcdir/test-ovsdb.py -t10 idl unix:socket $3],
+ [0], [stdout], [ignore], [kill `cat pid`])
+ AT_CHECK([sort stdout | perl $srcdir/uuidfilt.pl]m4_if([$6],,, [[| $6]]),
+ [0], [$4], [], [kill `cat pid`])
+ OVSDB_SERVER_SHUTDOWN
+ AT_CLEANUP])
+
+OVSDB_CHECK_IDL_PY([simple idl, initially empty, no ops - Python],
+ [],
+ [],
+ [000: empty
+001: done
+])
+
+OVSDB_CHECK_IDL([simple idl, initially empty, various ops - Python],
+ [],
+ [['["idltest",
+ {"op": "insert",
+ "table": "simple",
+ "row": {"i": 1,
+ "r": 2.0,
+ "b": true,
+ "s": "mystring",
+ "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"],
+ "ia": ["set", [1, 2, 3]],
+ "ra": ["set", [-0.5]],
+ "ba": ["set", [true, false]],
+ "sa": ["set", ["abc", "def"]],
+ "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"],
+ ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}},
+ {"op": "insert",
+ "table": "simple",
+ "row": {}}]' \
+ '["idltest",
+ {"op": "update",
+ "table": "simple",
+ "where": [],
+ "row": {"b": true}}]' \
+ '["idltest",
+ {"op": "update",
+ "table": "simple",
+ "where": [],
+ "row": {"r": 123.5}}]' \
+ '["idltest",
+ {"op": "insert",
+ "table": "simple",
+ "row": {"i": -1,
+ "r": 125,
+ "b": false,
+ "s": "",
+ "ia": ["set", [1]],
+ "ra": ["set", [1.5]],
+ "ba": ["set", [false]],
+ "sa": ["set", []],
+ "ua": ["set", []]}}]' \
+ '["idltest",
+ {"op": "update",
+ "table": "simple",
+ "where": [["i", "<", 1]],
+ "row": {"s": "newstring"}}]' \
+ '["idltest",
+ {"op": "delete",
+ "table": "simple",
+ "where": [["i", "==", 0]]}]' \
+ 'reconnect']],
+ [[000: empty
+001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]}
+002: i=0 r=0 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+002: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+003: {"error":null,"result":[{"count":2}]}
+004: i=0 r=0 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+004: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+005: {"error":null,"result":[{"count":2}]}
+006: i=0 r=123.5 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+006: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+007: {"error":null,"result":[{"uuid":["uuid","<6>"]}]}
+008: i=-1 r=125 b=false s= u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+008: i=0 r=123.5 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+008: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+009: {"error":null,"result":[{"count":2}]}
+010: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+010: i=0 r=123.5 b=true s=newstring u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+010: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+011: {"error":null,"result":[{"count":1}]}
+012: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+012: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+013: reconnect
+014: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+014: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+015: done
+]])
+
+OVSDB_CHECK_IDL([simple idl, initially populated - Python],
+ [['["idltest",
+ {"op": "insert",
+ "table": "simple",
+ "row": {"i": 1,
+ "r": 2.0,
+ "b": true,
+ "s": "mystring",
+ "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"],
+ "ia": ["set", [1, 2, 3]],
+ "ra": ["set", [-0.5]],
+ "ba": ["set", [true, false]],
+ "sa": ["set", ["abc", "def"]],
+ "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"],
+ ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}},
+ {"op": "insert",
+ "table": "simple",
+ "row": {}}]']],
+ [['["idltest",
+ {"op": "update",
+ "table": "simple",
+ "where": [],
+ "row": {"b": true}}]']],
+ [[000: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+000: i=1 r=2 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<3> <4>] uuid=<5>
+001: {"error":null,"result":[{"count":2}]}
+002: i=0 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+002: i=1 r=2 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<3> <4>] uuid=<5>
+003: done
+]])
diff --git a/tests/ovsdb-schema.at b/tests/ovsdb-schema.at
index 6cd2fa20f..008cc4316 100644
--- a/tests/ovsdb-schema.at
+++ b/tests/ovsdb-schema.at
@@ -1,6 +1,6 @@
AT_BANNER([OVSDB -- schemas])
-OVSDB_CHECK_POSITIVE([schema with valid refTables],
+OVSDB_CHECK_POSITIVE_CPY([schema with valid refTables],
[[parse-schema \
'{"name": "mydb",
"tables": {
@@ -23,7 +23,7 @@ OVSDB_CHECK_POSITIVE([schema with valid refTables],
"refTable": "a"}}}}}}}']],
[[{"name":"mydb","tables":{"a":{"columns":{"map":{"type":{"key":{"refTable":"b","type":"uuid"},"value":{"refTable":"a","type":"uuid"}}}}},"b":{"columns":{"aRef":{"type":{"key":{"refTable":"a","type":"uuid"}}}}}}}]])
-OVSDB_CHECK_NEGATIVE([schema with invalid refTables],
+OVSDB_CHECK_NEGATIVE_CPY([schema with invalid refTables],
[[parse-schema \
'{"name": "mydb",
"tables": {
@@ -44,4 +44,4 @@ OVSDB_CHECK_NEGATIVE([schema with invalid refTables],
"key": {
"type": "uuid",
"refTable": "a"}}}}}}}']],
- [[test-ovsdb: syntax error: column map key refers to undefined table c]])
+ [[syntax error: column map key refers to undefined table c]])
diff --git a/tests/ovsdb-table.at b/tests/ovsdb-table.at
index 623dd6dd0..70f8ac252 100644
--- a/tests/ovsdb-table.at
+++ b/tests/ovsdb-table.at
@@ -1,35 +1,35 @@
AT_BANNER([OVSDB -- tables])
-OVSDB_CHECK_POSITIVE([table with one column],
+OVSDB_CHECK_POSITIVE_CPY([table with one column],
[[parse-table mytable '{"columns": {"name": {"type": "string"}}}']],
[[{"columns":{"name":{"type":"string"}}}]])
-OVSDB_CHECK_POSITIVE([immutable table with one column],
+OVSDB_CHECK_POSITIVE_CPY([immutable table with one column],
[[parse-table mytable \
'{"columns": {"name": {"type": "string"}},
"mutable": false}']],
[[{"columns":{"name":{"type":"string"}},"mutable":false}]])
-OVSDB_CHECK_POSITIVE([table with maxRows of 2],
+OVSDB_CHECK_POSITIVE_CPY([table with maxRows of 2],
[[parse-table mytable '{"columns": {"name": {"type": "string"}},
"maxRows": 2}']],
[[{"columns":{"name":{"type":"string"}},"maxRows":2}]])
-OVSDB_CHECK_NEGATIVE([column names may not begin with _],
+OVSDB_CHECK_NEGATIVE_CPY([column names may not begin with _],
[[parse-table mytable \
'{"columns": {"_column": {"type": "integer"}}}']],
[[names beginning with "_" are reserved]],
[table])
-OVSDB_CHECK_NEGATIVE([table must have at least one column (1)],
+OVSDB_CHECK_NEGATIVE_CPY([table must have at least one column (1)],
[[parse-table mytable '{}']],
[[Parsing table schema for table mytable failed: Required 'columns' member is missing.]])
-OVSDB_CHECK_NEGATIVE([table must have at least one column (2)],
+OVSDB_CHECK_NEGATIVE_CPY([table must have at least one column (2)],
[[parse-table mytable '{"columns": {}}']],
[[table must have at least one column]])
-OVSDB_CHECK_NEGATIVE([table maxRows must be positive],
+OVSDB_CHECK_NEGATIVE_CPY([table maxRows must be positive],
[[parse-table mytable '{"columns": {"name": {"type": "string"}},
"maxRows": 0}']],
[[syntax "{"columns":{"name":{"type":"string"}},"maxRows":0}": syntax error: maxRows must be at least 1]])
diff --git a/tests/ovsdb-types.at b/tests/ovsdb-types.at
index 7122e9d2d..7bba84601 100644
--- a/tests/ovsdb-types.at
+++ b/tests/ovsdb-types.at
@@ -1,167 +1,167 @@
AT_BANNER([OVSDB -- atomic types])
-OVSDB_CHECK_POSITIVE([integer],
+OVSDB_CHECK_POSITIVE_CPY([integer],
[[parse-atomic-type '["integer"]' ]], ["integer"])
-OVSDB_CHECK_POSITIVE([real],
+OVSDB_CHECK_POSITIVE_CPY([real],
[[parse-atomic-type '["real"]' ]], ["real"])
-OVSDB_CHECK_POSITIVE([boolean],
+OVSDB_CHECK_POSITIVE_CPY([boolean],
[[parse-atomic-type '["boolean"]' ]], ["boolean"])
-OVSDB_CHECK_POSITIVE([string],
+OVSDB_CHECK_POSITIVE_CPY([string],
[[parse-atomic-type '["string"]' ]], ["string"])
-OVSDB_CHECK_POSITIVE([uuid],
+OVSDB_CHECK_POSITIVE_CPY([uuid],
[[parse-atomic-type '["uuid"]' ]], ["uuid"])
-OVSDB_CHECK_NEGATIVE([void is not a valid atomic-type],
+OVSDB_CHECK_NEGATIVE_CPY([void is not a valid atomic-type],
[[parse-atomic-type '["void"]' ]], ["void" is not an atomic-type])
AT_BANNER([OVSDB -- base types])
-OVSDB_CHECK_POSITIVE([integer enum],
+OVSDB_CHECK_POSITIVE_CPY([integer enum],
[[parse-base-type '{"type": "integer", "enum": ["set", [-1, 4, 5]]}' ]],
[[{"enum":["set",[-1,4,5]],"type":"integer"}]])
-OVSDB_CHECK_POSITIVE([integer >= 5],
+OVSDB_CHECK_POSITIVE_CPY([integer >= 5],
[[parse-base-type '{"type": "integer", "minInteger": 5}' ]],
[{"minInteger":5,"type":"integer"}])
-OVSDB_CHECK_POSITIVE([integer <= 7],
+OVSDB_CHECK_POSITIVE_CPY([integer <= 7],
[[parse-base-type '{"type": "integer", "maxInteger": 7}' ]],
[{"maxInteger":7,"type":"integer"}])
-OVSDB_CHECK_POSITIVE([integer between -5 and 10],
+OVSDB_CHECK_POSITIVE_CPY([integer between -5 and 10],
[[parse-base-type '{"type": "integer", "minInteger": -5, "maxInteger": 10}']],
[{"maxInteger":10,"minInteger":-5,"type":"integer"}])
-OVSDB_CHECK_NEGATIVE([integer max may not be less than min],
+OVSDB_CHECK_NEGATIVE_CPY([integer max may not be less than min],
[[parse-base-type '{"type": "integer", "minInteger": 5, "maxInteger": 3}']],
[minInteger exceeds maxInteger])
-OVSDB_CHECK_POSITIVE([real enum],
+OVSDB_CHECK_POSITIVE_CPY([real enum],
[[parse-base-type '{"type": "real", "enum": ["set", [1.5, 0, 2.75]]}' ]],
[[{"enum":["set",[0,1.5,2.75]],"type":"real"}]])
-OVSDB_CHECK_POSITIVE([real >= -1.5],
+OVSDB_CHECK_POSITIVE_CPY([real >= -1.5],
[[parse-base-type '{"type": "real", "minReal": -1.5}']],
[{"minReal":-1.5,"type":"real"}])
-OVSDB_CHECK_POSITIVE([real <= 1e5],
+OVSDB_CHECK_POSITIVE_CPY([real <= 1e5],
[[parse-base-type '{"type": "real", "maxReal": 1e5}']],
[{"maxReal":100000,"type":"real"}])
-OVSDB_CHECK_POSITIVE([real between -2.5 and 3.75],
+OVSDB_CHECK_POSITIVE_CPY([real between -2.5 and 3.75],
[[parse-base-type '{"type": "real", "minReal": -2.5, "maxReal": 3.75}']],
[{"maxReal":3.75,"minReal":-2.5,"type":"real"}])
-OVSDB_CHECK_NEGATIVE([real max may not be less than min],
+OVSDB_CHECK_NEGATIVE_CPY([real max may not be less than min],
[[parse-base-type '{"type": "real", "minReal": 555, "maxReal": 444}']],
[minReal exceeds maxReal])
-OVSDB_CHECK_POSITIVE([boolean],
+OVSDB_CHECK_POSITIVE_CPY([boolean],
[[parse-base-type '[{"type": "boolean"}]' ]], ["boolean"])
-OVSDB_CHECK_POSITIVE([boolean enum],
+OVSDB_CHECK_POSITIVE_CPY([boolean enum],
[[parse-base-type '{"type": "boolean", "enum": true}' ]],
[[{"enum":true,"type":"boolean"}]])
-OVSDB_CHECK_POSITIVE([string enum],
+OVSDB_CHECK_POSITIVE_CPY([string enum],
[[parse-base-type '{"type": "string", "enum": ["set", ["def", "abc"]]}']],
[[{"enum":["set",["abc","def"]],"type":"string"}]])
-OVSDB_CHECK_POSITIVE([string minLength],
+OVSDB_CHECK_POSITIVE_CPY([string minLength],
[[parse-base-type '{"type": "string", "minLength": 1}']],
[{"minLength":1,"type":"string"}])
-OVSDB_CHECK_POSITIVE([string maxLength],
+OVSDB_CHECK_POSITIVE_CPY([string maxLength],
[[parse-base-type '{"type": "string", "maxLength": 5}']],
[{"maxLength":5,"type":"string"}])
-OVSDB_CHECK_POSITIVE([string minLength and maxLength],
+OVSDB_CHECK_POSITIVE_CPY([string minLength and maxLength],
[[parse-base-type '{"type": "string", "minLength": 1, "maxLength": 5}']],
[{"maxLength":5,"minLength":1,"type":"string"}])
-OVSDB_CHECK_NEGATIVE([maxLength must not be less than minLength],
+OVSDB_CHECK_NEGATIVE_CPY([maxLength must not be less than minLength],
[[parse-base-type '{"type": "string", "minLength": 5, "maxLength": 3}']],
[minLength exceeds maxLength])
-OVSDB_CHECK_NEGATIVE([maxLength must not be negative],
+OVSDB_CHECK_NEGATIVE_CPY([maxLength must not be negative],
[[parse-base-type '{"type": "string", "maxLength": -1}']],
[maxLength out of valid range 0 to 4294967295])
-OVSDB_CHECK_POSITIVE([uuid enum],
+OVSDB_CHECK_POSITIVE_CPY([uuid enum],
[[parse-base-type '{"type": "uuid", "enum": ["uuid", "36bf19c0-ad9d-4232-bb85-b3d73dfe2123"]}' ]],
[[{"enum":["uuid","36bf19c0-ad9d-4232-bb85-b3d73dfe2123"],"type":"uuid"}]])
-OVSDB_CHECK_POSITIVE([uuid refTable],
+OVSDB_CHECK_POSITIVE_CPY([uuid refTable],
[[parse-base-type '{"type": "uuid", "refTable": "myTable"}' ]],
[{"refTable":"myTable","type":"uuid"}])
-OVSDB_CHECK_NEGATIVE([uuid refTable must be valid id],
+OVSDB_CHECK_NEGATIVE_CPY([uuid refTable must be valid id],
[[parse-base-type '{"type": "uuid", "refTable": "a-b-c"}' ]],
[Type mismatch for member 'refTable'])
-OVSDB_CHECK_NEGATIVE([void is not a valid base-type],
+OVSDB_CHECK_NEGATIVE_CPY([void is not a valid base-type],
[[parse-base-type '["void"]' ]], ["void" is not an atomic-type])
-OVSDB_CHECK_NEGATIVE(["type" member must be present],
+OVSDB_CHECK_NEGATIVE_CPY(["type" member must be present],
[[parse-base-type '{}']], [Parsing ovsdb type failed: Required 'type' member is missing.])
AT_BANNER([OVSDB -- simple types])
-OVSDB_CHECK_POSITIVE([simple integer],
+OVSDB_CHECK_POSITIVE_CPY([simple integer],
[[parse-type '["integer"]' ]], ["integer"])
-OVSDB_CHECK_POSITIVE([simple real],
+OVSDB_CHECK_POSITIVE_CPY([simple real],
[[parse-type '["real"]' ]], ["real"])
-OVSDB_CHECK_POSITIVE([simple boolean],
+OVSDB_CHECK_POSITIVE_CPY([simple boolean],
[[parse-type '["boolean"]' ]], ["boolean"])
-OVSDB_CHECK_POSITIVE([simple string],
+OVSDB_CHECK_POSITIVE_CPY([simple string],
[[parse-type '["string"]' ]], ["string"])
-OVSDB_CHECK_POSITIVE([simple uuid],
+OVSDB_CHECK_POSITIVE_CPY([simple uuid],
[[parse-type '["uuid"]' ]], ["uuid"])
-OVSDB_CHECK_POSITIVE([integer in object],
+OVSDB_CHECK_POSITIVE_CPY([integer in object],
[[parse-type '{"key": "integer"}' ]], ["integer"])
-OVSDB_CHECK_POSITIVE([real in object with explicit min and max],
+OVSDB_CHECK_POSITIVE_CPY([real in object with explicit min and max],
[[parse-type '{"key": "real", "min": 1, "max": 1}' ]], ["real"])
-OVSDB_CHECK_NEGATIVE([key type is required],
+OVSDB_CHECK_NEGATIVE_CPY([key type is required],
[[parse-type '{}' ]], [Required 'key' member is missing.])
-OVSDB_CHECK_NEGATIVE([void is not a valid type],
+OVSDB_CHECK_NEGATIVE_CPY([void is not a valid type],
[[parse-type '["void"]' ]], ["void" is not an atomic-type])
AT_BANNER([OVSDB -- set types])
-OVSDB_CHECK_POSITIVE([optional boolean],
+OVSDB_CHECK_POSITIVE_CPY([optional boolean],
[[parse-type '{"key": "boolean", "min": 0}' ]],
[[{"key":"boolean","min":0}]],
[set])
-OVSDB_CHECK_POSITIVE([set of 1 to 3 uuids],
+OVSDB_CHECK_POSITIVE_CPY([set of 1 to 3 uuids],
[[parse-type '{"key": "uuid", "min": 1, "max": 3}' ]],
[[{"key":"uuid","max":3}]])
-OVSDB_CHECK_POSITIVE([set of 0 to 3 strings],
+OVSDB_CHECK_POSITIVE_CPY([set of 0 to 3 strings],
[[parse-type '{"key": "string", "min": 0, "max": 3}' ]],
[[{"key":"string","max":3,"min":0}]])
-OVSDB_CHECK_POSITIVE([set of 0 or more integers],
+OVSDB_CHECK_POSITIVE_CPY([set of 0 or more integers],
[[parse-type '{"key": "integer", "min": 0, "max": "unlimited"}']],
[[{"key":"integer","max":"unlimited","min":0}]])
-OVSDB_CHECK_POSITIVE([set of 1 or more reals],
+OVSDB_CHECK_POSITIVE_CPY([set of 1 or more reals],
[[parse-type '{"key": "real", "min": 1, "max": "unlimited"}']],
[[{"key":"real","max":"unlimited"}]])
-OVSDB_CHECK_NEGATIVE([set max cannot be less than min],
+OVSDB_CHECK_NEGATIVE_CPY([set max cannot be less than min],
[[parse-type '{"key": "real", "min": 5, "max": 3}' ]],
[ovsdb type fails constraint checks])
-OVSDB_CHECK_NEGATIVE([set max cannot be negative],
+OVSDB_CHECK_NEGATIVE_CPY([set max cannot be negative],
[[parse-type '{"key": "real", "max": -1}' ]],
[bad min or max value])
-OVSDB_CHECK_NEGATIVE([set min cannot be negative],
+OVSDB_CHECK_NEGATIVE_CPY([set min cannot be negative],
[[parse-type '{"key": "real", "min": -1}' ]],
[bad min or max value])
-OVSDB_CHECK_NEGATIVE([set min cannot be greater than one],
+OVSDB_CHECK_NEGATIVE_CPY([set min cannot be greater than one],
[[parse-type '{"key": "real", "min": 10, "max": "unlimited"}']],
[ovsdb type fails constraint checks])
AT_BANNER([OVSDB -- map types])
-OVSDB_CHECK_POSITIVE([map of 1 integer to boolean],
+OVSDB_CHECK_POSITIVE_CPY([map of 1 integer to boolean],
[[parse-type '{"key": "integer", "value": "boolean"}' ]],
[[{"key":"integer","value":"boolean"}]])
-OVSDB_CHECK_POSITIVE([map of 1 boolean to integer, explicit min and max],
+OVSDB_CHECK_POSITIVE_CPY([map of 1 boolean to integer, explicit min and max],
[[parse-type '{"key": "boolean", "value": "integer", "min": 1, "max": 1}' ]],
[[{"key":"boolean","value":"integer"}]])
-OVSDB_CHECK_POSITIVE([map of 1 to 5 uuid to real],
+OVSDB_CHECK_POSITIVE_CPY([map of 1 to 5 uuid to real],
[[parse-type '{"key": "uuid", "value": "real", "min": 1, "max": 5}' ]],
[[{"key":"uuid","max":5,"value":"real"}]])
-OVSDB_CHECK_POSITIVE([map of 0 to 10 string to uuid],
+OVSDB_CHECK_POSITIVE_CPY([map of 0 to 10 string to uuid],
[[parse-type '{"key": "string", "value": "uuid", "min": 0, "max": 10}' ]],
[[{"key":"string","max":10,"min":0,"value":"uuid"}]])
-OVSDB_CHECK_POSITIVE([map of 1 to 20 real to string],
+OVSDB_CHECK_POSITIVE_CPY([map of 1 to 20 real to string],
[[parse-type '{"key": "real", "value": "string", "min": 1, "max": 20}' ]],
[[{"key":"real","max":20,"value":"string"}]])
-OVSDB_CHECK_POSITIVE([map of 0 or more string to real],
+OVSDB_CHECK_POSITIVE_CPY([map of 0 or more string to real],
[[parse-type '{"key": "string", "value": "real", "min": 0, "max": "unlimited"}' ]],
[[{"key":"string","max":"unlimited","min":0,"value":"real"}]])
-OVSDB_CHECK_NEGATIVE([map key type is required],
+OVSDB_CHECK_NEGATIVE_CPY([map key type is required],
[[parse-type '{"value": "integer"}' ]],
[Required 'key' member is missing.])
diff --git a/tests/ovsdb.at b/tests/ovsdb.at
index 1ee9c0378..d64d75e54 100644
--- a/tests/ovsdb.at
+++ b/tests/ovsdb.at
@@ -11,6 +11,33 @@ m4_define([OVSDB_CHECK_POSITIVE],
], [])
AT_CLEANUP])
+# OVSDB_CHECK_POSITIVE_PY(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], [PREREQ],
+# [XFAIL])
+#
+# Runs "test-ovsdb.py TEST-OVSDB-ARGS" and checks that it exits with
+# status 0 and prints OUTPUT on stdout.
+#
+# If XFAIL is nonempty then the test is expected to fail (presumably because
+# this test works in the C implementation but does not work in Python yet)
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_POSITIVE_PY],
+ [AT_SETUP([$1])
+ AT_SKIP_IF([test $HAVE_PYTHON = no])
+ m4_if([$6], [], [], [AT_XFAIL_IF([:])])
+ AT_KEYWORDS([ovsdb positive Python $4])
+ AT_CHECK([$PYTHON $srcdir/test-ovsdb.py $2], [0], [$3
+], [])
+ AT_CLEANUP])
+
+# OVSDB_CHECK_POSITIVE_CPY(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS],
+# [PREREQ], [PY-XFAIL])
+#
+# Runs identical C and Python tests, as specified.
+m4_define([OVSDB_CHECK_POSITIVE_CPY],
+ [OVSDB_CHECK_POSITIVE([$1 - C], [$2], [$3], [$4], [$5])
+ OVSDB_CHECK_POSITIVE_PY([$1 - Python], [$2], [$3], [$4], [$5], [$6])])
+
# OVSDB_CHECK_NEGATIVE(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], [PREREQ])
#
# Runs "test-ovsdb TEST-OVSDB-ARGS" and checks that it exits with
@@ -31,6 +58,35 @@ m4_define([OVSDB_CHECK_NEGATIVE],
[0], [ignore], [ignore])
AT_CLEANUP])
+# OVSDB_CHECK_NEGATIVE_PY(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], [PREREQ])
+#
+# Runs "test-ovsdb TEST-OVSDB-ARGS" and checks that it exits with
+# status 1 and that its output on stdout contains substring OUTPUT.
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_NEGATIVE_PY],
+ [AT_SETUP([$1])
+ AT_SKIP_IF([test $HAVE_PYTHON = no])
+ AT_KEYWORDS([ovsdb negative $4])
+ AT_CHECK([$PYTHON $srcdir/test-ovsdb.py $2], [1], [], [stderr])
+ m4_assert(m4_len([$3]))
+ AT_CHECK(
+ [if grep -F -e "AS_ESCAPE([$3])" stderr
+ then
+ :
+ else
+ exit 99
+ fi],
+ [0], [ignore], [ignore])
+ AT_CLEANUP])
+
+# OVSDB_CHECK_NEGATIVE_CPY(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS],
+# [PREREQ])
+#
+# Runs identical C and Python tests, as specified.
+m4_define([OVSDB_CHECK_NEGATIVE_CPY],
+ [OVSDB_CHECK_NEGATIVE([$1 - C], [$2], [$3], [$4], [$5])
+ OVSDB_CHECK_NEGATIVE_PY([$1 - Python], [$2], [$3], [$4], [$5])])
+
m4_include([tests/ovsdb-log.at])
m4_include([tests/ovsdb-types.at])
m4_include([tests/ovsdb-data.at])
@@ -48,3 +104,4 @@ m4_include([tests/ovsdb-tool.at])
m4_include([tests/ovsdb-server.at])
m4_include([tests/ovsdb-monitor.at])
m4_include([tests/ovsdb-idl.at])
+m4_include([tests/ovsdb-idl-py.at])
diff --git a/tests/reconnect.at b/tests/reconnect.at
index d35e7bff1..559836420 100644
--- a/tests/reconnect.at
+++ b/tests/reconnect.at
@@ -2,13 +2,25 @@ AT_BANNER([reconnect library])
m4_define([__RECONNECT_CHECK],
[AT_SETUP([$1])
+ $2
AT_KEYWORDS([reconnect])
- AT_DATA([input], [$2])
- AT_CHECK([$3], [0], [$4])
+ AT_DATA([input], [$3])
+ AT_CHECK([$4], [0], [$5])
AT_CLEANUP])
m4_define([RECONNECT_CHECK],
- [__RECONNECT_CHECK([$1], [$2], [test-reconnect < input], [$3])])
+ [__RECONNECT_CHECK(
+ [$1 - C],
+ [],
+ [$2],
+ [test-reconnect < input],
+ [$3])
+ __RECONNECT_CHECK(
+ [$1 - Python],
+ [AT_SKIP_IF([test $HAVE_PYTHON = no])],
+ [$2],
+ [$PYTHON $srcdir/test-reconnect.py < input],
+ [$3])])
######################################################################
RECONNECT_CHECK([nothing happens if not enabled],
diff --git a/tests/test-daemon.py b/tests/test-daemon.py
new file mode 100644
index 000000000..3c757f308
--- /dev/null
+++ b/tests/test-daemon.py
@@ -0,0 +1,66 @@
+# Copyright (c) 2010 Nicira Networks.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import getopt
+import sys
+import time
+
+import ovs.daemon
+import ovs.util
+
+def main(argv):
+ try:
+ options, args = getopt.gnu_getopt(
+ argv[1:], 'b', ["bail", "help"] + ovs.daemon.LONG_OPTIONS)
+ except getopt.GetoptError, geo:
+ sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg))
+ sys.exit(1)
+
+ bail = False
+ for key, value in options:
+ if key == '--help':
+ usage()
+ elif key in ['-b', '--bail']:
+ bail = True
+ elif not ovs.daemon.parse_opt(key, value):
+ sys.stderr.write("%s: unhandled option %s\n"
+ % (ovs.util.PROGRAM_NAME, key))
+ sys.exit(1)
+
+ ovs.daemon.die_if_already_running()
+ ovs.daemon.daemonize_start()
+ if bail:
+ sys.stderr.write("%s: exiting after daemonize_start() as requested\n"
+ % ovs.util.PROGRAM_NAME)
+ sys.exit(1)
+ ovs.daemon.daemonize_complete()
+
+ while True:
+ time.sleep(1)
+
+def usage():
+ sys.stdout.write("""\
+%s: Open vSwitch daemonization test program for Python
+usage: %s [OPTIONS]
+""" % ovs.util.PROGRAM_NAME)
+ ovs.daemon.usage()
+ sys.stdout.write("""
+Other options:
+ -h, --help display this help message
+ -b, --bail exit with an error after daemonize_start()
+""")
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/tests/test-json.py b/tests/test-json.py
new file mode 100644
index 000000000..bffb88a03
--- /dev/null
+++ b/tests/test-json.py
@@ -0,0 +1,92 @@
+# Copyright (c) 2009, 2010 Nicira Networks.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import codecs
+import getopt
+import sys
+
+import ovs.json
+
+def print_json(json):
+ if type(json) in [str, unicode]:
+ print "error: %s" % json
+ return False
+ else:
+ ovs.json.to_stream(json, sys.stdout)
+ sys.stdout.write("\n")
+ return True
+
+def parse_multiple(stream):
+ buf = stream.read(4096)
+ ok = True
+ parser = None
+ while len(buf):
+ if parser is None and buf[0] in " \t\r\n":
+ buf = buf[1:]
+ else:
+ if parser is None:
+ parser = ovs.json.Parser()
+ n = parser.feed(buf)
+ buf = buf[n:]
+ if len(buf):
+ if not print_json(parser.finish()):
+ ok = False
+ parser = None
+ if len(buf) == 0:
+ buf = stream.read(4096)
+ if parser and not print_json(parser.finish()):
+ ok = False
+ return ok
+
+def main(argv):
+ argv0 = argv[0]
+
+ # Make stdout and stderr UTF-8, even if they are redirected to a file.
+ sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
+ sys.stderr = codecs.getwriter("utf-8")(sys.stderr)
+
+ try:
+ options, args = getopt.gnu_getopt(argv[1:], '', ['multiple'])
+ except getopt.GetoptError, geo:
+ sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
+ sys.exit(1)
+
+ multiple = False
+ for key, value in options:
+ if key == '--multiple':
+ multiple = True
+ else:
+ sys.stderr.write("%s: unhandled option %s\n" % (argv0, key))
+ sys.exit(1)
+
+ if len(args) != 1:
+ sys.stderr.write("usage: %s [--multiple] INPUT.json\n" % argv0)
+ sys.exit(1)
+
+ input_file = args[0]
+ if input_file == "-":
+ stream = sys.stdin
+ else:
+ stream = open(input_file, "r")
+
+ if multiple:
+ ok = parse_multiple(stream)
+ else:
+ ok = print_json(ovs.json.from_stream(stream))
+
+ if not ok:
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/tests/test-jsonrpc.c b/tests/test-jsonrpc.c
index f2d0568ed..e8edec064 100644
--- a/tests/test-jsonrpc.c
+++ b/tests/test-jsonrpc.c
@@ -319,7 +319,7 @@ do_notify(int argc OVS_UNUSED, char *argv[])
error = jsonrpc_send_block(rpc, msg);
if (error) {
- ovs_fatal(error, "could not send request");
+ ovs_fatal(error, "could not send notification");
}
jsonrpc_close(rpc);
}
diff --git a/tests/test-jsonrpc.py b/tests/test-jsonrpc.py
new file mode 100644
index 000000000..a8bf553e2
--- /dev/null
+++ b/tests/test-jsonrpc.py
@@ -0,0 +1,221 @@
+# Copyright (c) 2009, 2010 Nicira Networks
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import errno
+import getopt
+import os
+import sys
+
+import ovs.daemon
+import ovs.json
+import ovs.jsonrpc
+import ovs.poller
+import ovs.stream
+
+def handle_rpc(rpc, msg):
+ done = False
+ reply = None
+
+ if msg.type == ovs.jsonrpc.Message.T_REQUEST:
+ if msg.method == "echo":
+ reply = ovs.jsonrpc.Message.create_reply(msg.params, msg.id)
+ else:
+ reply = ovs.jsonrpc.Message.create_error(
+ {"error": "unknown method"}, msg.id)
+ sys.stderr.write("unknown request %s" % msg.method)
+ elif msg.type == ovs.jsonrpc.Message.T_NOTIFY:
+ if msg.method == "shutdown":
+ done = True
+ else:
+ rpc.error(errno.ENOTTY)
+ sys.stderr.write("unknown notification %s" % msg.method)
+ else:
+ rpc.error(errno.EPROTO)
+ sys.stderr.write("unsolicited JSON-RPC reply or error\n")
+
+ if reply:
+ rpc.send(reply)
+ return done
+
+def do_listen(name):
+ ovs.daemon.die_if_already_running()
+
+ error, pstream = ovs.stream.PassiveStream.open(name)
+ if error:
+ sys.stderr.write("could not listen on \"%s\": %s\n"
+ % (name, os.strerror(error)))
+ sys.exit(1)
+
+ ovs.daemon.daemonize()
+
+ rpcs = []
+ done = False
+ while True:
+ # Accept new connections.
+ error, stream = pstream.accept()
+ if stream:
+ rpcs.append(ovs.jsonrpc.Connection(stream))
+ elif error != errno.EAGAIN:
+ sys.stderr.write("PassiveStream.accept() failed\n")
+ sys.exit(1)
+
+ # Service existing connections.
+ dead_rpcs = []
+ for rpc in rpcs:
+ rpc.run()
+
+ error = 0
+ if not rpc.get_backlog():
+ error, msg = rpc.recv()
+ if not error:
+ if handle_rpc(rpc, msg):
+ done = True
+
+ error = rpc.get_status()
+ if error:
+ rpc.close()
+ dead_rpcs.append(rpc)
+ rpcs = [rpc for rpc in rpcs if not rpc in dead_rpcs]
+
+ if done and not rpcs:
+ break
+
+ poller = ovs.poller.Poller()
+ pstream.wait(poller)
+ for rpc in rpcs:
+ rpc.wait(poller)
+ if not rpc.get_backlog():
+ rpc.recv_wait(poller)
+ poller.block()
+ pstream.close()
+
+def do_request(name, method, params_string):
+ params = ovs.json.from_string(params_string)
+ msg = ovs.jsonrpc.Message.create_request(method, params)
+ s = msg.is_valid()
+ if s:
+ sys.stderr.write("not a valid JSON-RPC request: %s\n" % s)
+ sys.exit(1)
+
+ error, stream = ovs.stream.Stream.open_block(ovs.stream.Stream.open(name))
+ if error:
+ sys.stderr.write("could not open \"%s\": %s\n"
+ % (name, os.strerror(error)))
+ sys.exit(1)
+
+ rpc = ovs.jsonrpc.Connection(stream)
+
+ error = rpc.send(msg)
+ if error:
+ sys.stderr.write("could not send request: %s\n" % os.strerror(error))
+ sys.exit(1)
+
+ error, msg = rpc.recv_block()
+ if error:
+ sys.stderr.write("error waiting for reply: %s\n" % os.strerror(error))
+ sys.exit(1)
+
+ print ovs.json.to_string(msg.to_json())
+
+ rpc.close()
+
+def do_notify(name, method, params_string):
+ params = ovs.json.from_string(params_string)
+ msg = ovs.jsonrpc.Message.create_notify(method, params)
+ s = msg.is_valid()
+ if s:
+ sys.stderr.write("not a valid JSON-RPC notification: %s\n" % s)
+ sys.exit(1)
+
+ error, stream = ovs.stream.Stream.open_block(ovs.stream.Stream.open(name))
+ if error:
+ sys.stderr.write("could not open \"%s\": %s\n"
+ % (name, os.strerror(error)))
+ sys.exit(1)
+
+ rpc = ovs.jsonrpc.Connection(stream)
+
+ error = rpc.send_block(msg)
+ if error:
+ sys.stderr.write("could not send notification: %s\n"
+ % os.strerror(error))
+ sys.exit(1)
+
+ rpc.close()
+
+def main(argv):
+ try:
+ options, args = getopt.gnu_getopt(
+ argv[1:], 'h', ["help"] + ovs.daemon.LONG_OPTIONS)
+ except getopt.GetoptError, geo:
+ sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg))
+ sys.exit(1)
+
+ for key, value in options:
+ if key in ['h', '--help']:
+ usage()
+ elif not ovs.daemon.parse_opt(key, value):
+ sys.stderr.write("%s: unhandled option %s\n"
+ % (ovs.util.PROGRAM_NAME, key))
+ sys.exit(1)
+
+ commands = {"listen": (do_listen, 1),
+ "request": (do_request, 3),
+ "notify": (do_notify, 3),
+ "help": (usage, (0,))}
+
+ command_name = args[0]
+ args = args[1:]
+ if not command_name in commands:
+ sys.stderr.write("%s: unknown command \"%s\" "
+ "(use --help for help)\n" % (argv0, command_name))
+ sys.exit(1)
+
+ func, n_args = commands[command_name]
+ if type(n_args) == tuple:
+ if len(args) < n_args[0]:
+ sys.stderr.write("%s: \"%s\" requires at least %d arguments but "
+ "only %d provided\n"
+ % (argv0, command_name, n_args, len(args)))
+ sys.exit(1)
+ elif type(n_args) == int:
+ if len(args) != n_args:
+ sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
+ "provided\n"
+ % (argv0, command_name, n_args, len(args)))
+ sys.exit(1)
+ else:
+ assert False
+
+ func(*args)
+
+def usage():
+ sys.stdout.write("""\
+%s: JSON-RPC test utility for Python
+usage: %s [OPTIONS] COMMAND [ARG...]
+ listen LOCAL listen for connections on LOCAL
+ request REMOTE METHOD PARAMS send request, print reply
+ notify REMOTE METHOD PARAMS send notification and exit
+""" % (ovs.util.PROGRAM_NAME, ovs.util.PROGRAM_NAME))
+ ovs.stream.usage("JSON-RPC", True, True, True)
+ ovs.daemon.usage()
+ sys.stdout.write("""
+Other options:
+ -h, --help display this help message
+""")
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main(sys.argv)
+
diff --git a/tests/test-ovsdb.py b/tests/test-ovsdb.py
new file mode 100644
index 000000000..863bcb8fd
--- /dev/null
+++ b/tests/test-ovsdb.py
@@ -0,0 +1,372 @@
+# Copyright (c) 2009, 2010 Nicira Networks
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import codecs
+import getopt
+import re
+import os
+import signal
+import sys
+
+from ovs.db import error
+import ovs.db.idl
+import ovs.db.schema
+from ovs.db import data
+from ovs.db import types
+import ovs.ovsuuid
+import ovs.poller
+import ovs.util
+
+def unbox_json(json):
+ if type(json) == list and len(json) == 1:
+ return json[0]
+ else:
+ return json
+
+def do_default_atoms():
+ for type in types.ATOMIC_TYPES:
+ if type == types.VoidType:
+ continue
+
+ sys.stdout.write("%s: " % type.to_string())
+
+ atom = data.Atom.default(type)
+ if atom != data.Atom.default(type):
+ sys.stdout.write("wrong\n")
+ sys.exit(1)
+
+ sys.stdout.write("OK\n")
+
+def do_default_data():
+ any_errors = False
+ for n_min in 0, 1:
+ for key in types.ATOMIC_TYPES:
+ if key == types.VoidType:
+ continue
+ for value in types.ATOMIC_TYPES:
+ if value == types.VoidType:
+ valueBase = None
+ else:
+ valueBase = types.BaseType(value)
+ type = types.Type(types.BaseType(key), valueBase, n_min, 1)
+ assert type.is_valid()
+
+ sys.stdout.write("key %s, value %s, n_min %d: "
+ % (key.to_string(), value.to_string(), n_min))
+
+ datum = data.Datum.default(type)
+ if datum != data.Datum.default(type):
+ sys.stdout.write("wrong\n")
+ any_errors = True
+ else:
+ sys.stdout.write("OK\n")
+ if any_errors:
+ sys.exit(1)
+
+def do_parse_atomic_type(type_string):
+ type_json = unbox_json(ovs.json.from_string(type_string))
+ atomic_type = types.AtomicType.from_json(type_json)
+ print ovs.json.to_string(atomic_type.to_json(), sort_keys=True)
+
+def do_parse_base_type(type_string):
+ type_json = unbox_json(ovs.json.from_string(type_string))
+ base_type = types.BaseType.from_json(type_json)
+ print ovs.json.to_string(base_type.to_json(), sort_keys=True)
+
+def do_parse_type(type_string):
+ type_json = unbox_json(ovs.json.from_string(type_string))
+ type = types.Type.from_json(type_json)
+ print ovs.json.to_string(type.to_json(), sort_keys=True)
+
+def do_parse_atoms(type_string, *atom_strings):
+ type_json = unbox_json(ovs.json.from_string(type_string))
+ base = types.BaseType.from_json(type_json)
+ for atom_string in atom_strings:
+ atom_json = unbox_json(ovs.json.from_string(atom_string))
+ try:
+ atom = data.Atom.from_json(base, atom_json)
+ print ovs.json.to_string(atom.to_json())
+ except error.Error, e:
+ print e
+
+def do_parse_data(type_string, *data_strings):
+ type_json = unbox_json(ovs.json.from_string(type_string))
+ type = types.Type.from_json(type_json)
+ for datum_string in data_strings:
+ datum_json = unbox_json(ovs.json.from_string(datum_string))
+ datum = data.Datum.from_json(type, datum_json)
+ print ovs.json.to_string(datum.to_json())
+
+def do_sort_atoms(type_string, atom_strings):
+ type_json = unbox_json(ovs.json.from_string(type_string))
+ base = types.BaseType.from_json(type_json)
+ atoms = [data.Atom.from_json(base, atom_json)
+ for atom_json in unbox_json(ovs.json.from_string(atom_strings))]
+ print ovs.json.to_string([data.Atom.to_json(atom)
+ for atom in sorted(atoms)])
+
+def do_parse_column(name, column_string):
+ column_json = unbox_json(ovs.json.from_string(column_string))
+ column = ovs.db.schema.ColumnSchema.from_json(column_json, name)
+ print ovs.json.to_string(column.to_json(), sort_keys=True)
+
+def do_parse_table(name, table_string):
+ table_json = unbox_json(ovs.json.from_string(table_string))
+ table = ovs.db.schema.TableSchema.from_json(table_json, name)
+ print ovs.json.to_string(table.to_json(), sort_keys=True)
+
+def do_parse_rows(table_string, *rows):
+ table_json = unbox_json(ovs.json.from_string(table_string))
+ table = ovs.db.schema.TableSchema.from_json(table_json, name)
+
+def do_parse_schema(schema_string):
+ schema_json = unbox_json(ovs.json.from_string(schema_string))
+ schema = ovs.db.schema.DbSchema.from_json(schema_json)
+ print ovs.json.to_string(schema.to_json(), sort_keys=True)
+
+def print_idl(idl, step):
+ n = 0
+ for uuid, row in idl.data["simple"].iteritems():
+ s = ("%03d: i=%s r=%s b=%s s=%s u=%s "
+ "ia=%s ra=%s ba=%s sa=%s ua=%s uuid=%s"
+ % (step, row.i, row.r, row.b, row.s, row.u,
+ row.ia, row.ra, row.ba, row.sa, row.ua, uuid))
+ print(re.sub('""|,', "", s))
+ n += 1
+ if not n:
+ print("%03d: empty" % step)
+
+def substitute_uuids(json, symtab):
+ if type(json) in [str, unicode]:
+ symbol = symtab.get(json)
+ if symbol:
+ return str(symbol)
+ elif type(json) == list:
+ return [substitute_uuids(element, symtab) for element in json]
+ elif type(json) == dict:
+ d = {}
+ for key, value in json.iteritems():
+ d[key] = substitute_uuids(value, symtab)
+ return d
+ return json
+
+def parse_uuids(json, symtab):
+ if type(json) in [str, unicode] and ovs.ovsuuid.UUID.is_valid_string(json):
+ name = "#%d#" % len(symtab)
+ sys.stderr.write("%s = %s\n" % (name, json))
+ symtab[name] = json
+ elif type(json) == list:
+ for element in json:
+ parse_uuids(element, symtab)
+ elif type(json) == dict:
+ for value in json.itervalues():
+ parse_uuids(value, symtab)
+
+def do_idl(remote, *commands):
+ idl = ovs.db.idl.Idl(remote, "idltest")
+
+ if commands:
+ error, stream = ovs.stream.Stream.open_block(
+ ovs.stream.Stream.open(remote))
+ if error:
+ sys.stderr.write("failed to connect to \"%s\"" % remote)
+ sys.exit(1)
+ rpc = ovs.jsonrpc.Connection(stream)
+ else:
+ rpc = None
+
+ symtab = {}
+ seqno = 0
+ step = 0
+ for command in commands:
+ if command.startswith("+"):
+ # The previous transaction didn't change anything.
+ command = command[1:]
+ else:
+ # Wait for update.
+ while idl.get_seqno() == seqno and not idl.run():
+ rpc.run()
+
+ poller = ovs.poller.Poller()
+ idl.wait(poller)
+ rpc.wait(poller)
+ poller.block()
+
+ print_idl(idl, step)
+ step += 1
+
+ seqno = idl.get_seqno()
+
+ if command == "reconnect":
+ print("%03d: reconnect" % step)
+ step += 1
+ idl.force_reconnect()
+ elif not command.startswith("["):
+ idl_set(idl, command, step)
+ step += 1
+ else:
+ json = ovs.json.from_string(command)
+ if type(json) in [str, unicode]:
+ sys.stderr.write("\"%s\": %s\n" % (command, json))
+ sys.exit(1)
+ json = substitute_uuids(json, symtab)
+ request = ovs.jsonrpc.Message.create_request("transact", json)
+ error, reply = rpc.transact_block(request)
+ if error:
+ sys.stderr.write("jsonrpc transaction failed: %s"
+ % os.strerror(error))
+ sys.exit(1)
+ sys.stdout.write("%03d: " % step)
+ sys.stdout.flush()
+ step += 1
+ if reply.result is not None:
+ parse_uuids(reply.result, symtab)
+ reply.id = None
+ sys.stdout.write("%s\n" % ovs.json.to_string(reply.to_json()))
+
+ if rpc:
+ rpc.close()
+ while idl.get_seqno() == seqno and not idl.run():
+ poller = ovs.poller.Poller()
+ idl.wait(poller)
+ poller.block()
+ print_idl(idl, step)
+ step += 1
+ idl.close()
+ print("%03d: done" % step)
+
+def usage():
+ print """\
+%(program_name)s: test utility for Open vSwitch database Python bindings
+usage: %(program_name)s [OPTIONS] COMMAND ARG...
+
+The following commands are supported:
+default-atoms
+ test ovsdb_atom_default()
+default-data
+ test ovsdb_datum_default()
+parse-atomic-type TYPE
+ parse TYPE as OVSDB atomic type, and re-serialize
+parse-base-type TYPE
+ parse TYPE as OVSDB base type, and re-serialize
+parse-type JSON
+ parse JSON as OVSDB type, and re-serialize
+parse-atoms TYPE ATOM...
+ parse JSON ATOMs as atoms of TYPE, and re-serialize
+parse-atom-strings TYPE ATOM...
+ parse string ATOMs as atoms of given TYPE, and re-serialize
+sort-atoms TYPE ATOM...
+ print JSON ATOMs in sorted order
+parse-data TYPE DATUM...
+ parse JSON DATUMs as data of given TYPE, and re-serialize
+parse-column NAME OBJECT
+ parse column NAME with info OBJECT, and re-serialize
+parse-table NAME OBJECT
+ parse table NAME with info OBJECT
+parse-schema JSON
+ parse JSON as an OVSDB schema, and re-serialize
+idl SERVER [TRANSACTION...]
+ connect to SERVER and dump the contents of the database
+ as seen initially by the IDL implementation and after
+ executing each TRANSACTION. (Each TRANSACTION must modify
+ the database or this command will hang.)
+
+The following options are also available:
+ -t, --timeout=SECS give up after SECS seconds
+ -h, --help display this help message\
+""" % {'program_name': ovs.util.PROGRAM_NAME}
+ sys.exit(0)
+
+def main(argv):
+ # Make stdout and stderr UTF-8, even if they are redirected to a file.
+ sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
+ sys.stderr = codecs.getwriter("utf-8")(sys.stderr)
+
+ try:
+ options, args = getopt.gnu_getopt(argv[1:], 't:h',
+ ['timeout',
+ 'help'])
+ except getopt.GetoptError, geo:
+ sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg))
+ sys.exit(1)
+
+ for key, value in options:
+ if key in ['-h', '--help']:
+ usage()
+ elif key in ['-t', '--timeout']:
+ try:
+ timeout = int(value)
+ if timeout < 1:
+ raise TypeError
+ except TypeError:
+ raise error.Error("value %s on -t or --timeout is not at "
+ "least 1" % value)
+ signal.alarm(timeout)
+ else:
+ sys.exit(0)
+
+ optKeys = [key for key, value in options]
+
+ if not args:
+ sys.stderr.write("%s: missing command argument "
+ "(use --help for help)\n" % ovs.util.PROGRAM_NAME)
+ sys.exit(1)
+
+ commands = {"default-atoms": (do_default_atoms, 0),
+ "default-data": (do_default_data, 0),
+ "parse-atomic-type": (do_parse_atomic_type, 1),
+ "parse-base-type": (do_parse_base_type, 1),
+ "parse-type": (do_parse_type, 1),
+ "parse-atoms": (do_parse_atoms, (2,)),
+ "parse-data": (do_parse_data, (2,)),
+ "sort-atoms": (do_sort_atoms, 2),
+ "parse-column": (do_parse_column, 2),
+ "parse-table": (do_parse_table, 2),
+ "parse-schema": (do_parse_schema, 1),
+ "idl": (do_idl, (1,))}
+
+ command_name = args[0]
+ args = args[1:]
+ if not command_name in commands:
+ sys.stderr.write("%s: unknown command \"%s\" "
+ "(use --help for help)\n" % (ovs.util.PROGRAM_NAME,
+ command_name))
+ sys.exit(1)
+
+ func, n_args = commands[command_name]
+ if type(n_args) == tuple:
+ if len(args) < n_args[0]:
+ sys.stderr.write("%s: \"%s\" requires at least %d arguments but "
+ "only %d provided\n"
+ % (ovs.util.PROGRAM_NAME, command_name,
+ n_args, len(args)))
+ sys.exit(1)
+ elif type(n_args) == int:
+ if len(args) != n_args:
+ sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
+ "provided\n"
+ % (ovs.util.PROGRAM_NAME, command_name,
+ n_args, len(args)))
+ sys.exit(1)
+ else:
+ assert False
+
+ func(*args)
+
+if __name__ == '__main__':
+ try:
+ main(sys.argv)
+ except error.Error, e:
+ sys.stderr.write("%s\n" % e)
+ sys.exit(1)
diff --git a/tests/test-reconnect.py b/tests/test-reconnect.py
new file mode 100644
index 000000000..4b483db09
--- /dev/null
+++ b/tests/test-reconnect.py
@@ -0,0 +1,195 @@
+# Copyright (c) 2009, 2010 Nicira Networks.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import errno
+import logging
+import sys
+
+import ovs.reconnect
+
+def do_enable(arg):
+ r.enable(now)
+
+def do_disable(arg):
+ r.disable(now)
+
+def do_force_reconnect(arg):
+ r.force_reconnect(now)
+
+def error_from_string(s):
+ if not s:
+ return 0
+ elif s == "ECONNREFUSED":
+ return errno.ECONNREFUSED
+ elif s == "EOF":
+ return EOF
+ else:
+ sys.stderr.write("unknown error '%s'\n" % s)
+ sys.exit(1)
+
+def do_disconnected(arg):
+ r.disconnected(now, error_from_string(arg))
+
+def do_connecting(arg):
+ r.connecting(now)
+
+def do_connect_failed(arg):
+ r.connect_failed(now, error_from_string(arg))
+
+def do_connected(arg):
+ r.connected(now)
+
+def do_received(arg):
+ r.received(now)
+
+def do_run(arg):
+ global now
+ if arg is not None:
+ now += int(arg)
+
+ action = r.run(now)
+ if action is None:
+ pass
+ elif action == ovs.reconnect.CONNECT:
+ print " should connect"
+ elif action == ovs.reconnect.DISCONNECT:
+ print " should disconnect"
+ elif action == ovs.reconnect.PROBE:
+ print " should send probe"
+ else:
+ assert False
+
+def do_advance(arg):
+ global now
+ now += int(arg)
+
+def do_timeout(arg):
+ global now
+ timeout = r.timeout(now)
+ if timeout >= 0:
+ print " advance %d ms" % timeout
+ now += timeout
+ else:
+ print " no timeout"
+
+def do_set_max_tries(arg):
+ r.set_max_tries(int(arg))
+
+def diff_stats(old, new):
+ if (old.state != new.state or
+ old.state_elapsed != new.state_elapsed or
+ old.backoff != new.backoff):
+ print(" in %s for %d ms (%d ms backoff)"
+ % (new.state, new.state_elapsed, new.backoff))
+
+ if (old.creation_time != new.creation_time or
+ old.last_received != new.last_received or
+ old.last_connected != new.last_connected):
+ print(" created %d, last received %d, last connected %d"
+ % (new.creation_time, new.last_received, new.last_connected))
+
+ if (old.n_successful_connections != new.n_successful_connections or
+ old.n_attempted_connections != new.n_attempted_connections or
+ old.seqno != new.seqno):
+ print(" %d successful connections out of %d attempts, seqno %d"
+ % (new.n_successful_connections, new.n_attempted_connections,
+ new.seqno))
+
+ if (old.is_connected != new.is_connected or
+ old.current_connection_duration != new.current_connection_duration or
+ old.total_connected_duration != new.total_connected_duration):
+ if new.is_connected:
+ negate = ""
+ else:
+ negate = "not "
+ print(" %sconnected (%d ms), total %d ms connected"
+ % (negate, new.current_connection_duration,
+ new.total_connected_duration))
+
+def do_set_passive(arg):
+ r.set_passive(True, now)
+
+def do_listening(arg):
+ r.listening(now)
+
+def do_listen_error(arg):
+ r.listen_error(now, int(arg))
+
+def main():
+ commands = {
+ "enable": do_enable,
+ "disable": do_disable,
+ "force-reconnect": do_force_reconnect,
+ "disconnected": do_disconnected,
+ "connecting": do_connecting,
+ "connect-failed": do_connect_failed,
+ "connected": do_connected,
+ "received": do_received,
+ "run": do_run,
+ "advance": do_advance,
+ "timeout": do_timeout,
+ "set-max-tries": do_set_max_tries,
+ "passive": do_set_passive,
+ "listening": do_listening,
+ "listen-error": do_listen_error
+ }
+
+ logging.basicConfig(level=logging.CRITICAL)
+
+ global now
+ global r
+
+ now = 1000
+ r = ovs.reconnect.Reconnect(now)
+ r.set_name("remote")
+ prev = r.get_stats(now)
+ print "### t=%d ###" % now
+ old_time = now
+ old_max_tries = r.get_max_tries()
+ while True:
+ line = sys.stdin.readline()
+ if line == "":
+ break
+
+ print line[:-1]
+ if line[0] == "#":
+ continue
+
+ args = line.split()
+ if len(args) == 0:
+ continue
+
+ command = args[0]
+ if len(args) > 1:
+ op = args[1]
+ else:
+ op = None
+ commands[command](op)
+
+ if old_time != now:
+ print
+ print "### t=%d ###" % now
+ old_time = now
+
+ cur = r.get_stats(now)
+ diff_stats(prev, cur)
+ prev = cur
+ if r.get_max_tries() != old_max_tries:
+ old_max_tries = r.get_max_tries()
+ print " %d tries left" % old_max_tries
+
+if __name__ == '__main__':
+ main()
+
+
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 2eab5814b..42e62dfbe 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -41,12 +41,14 @@ m4_include([tests/library.at])
m4_include([tests/classifier.at])
m4_include([tests/check-structs.at])
m4_include([tests/daemon.at])
+m4_include([tests/daemon-py.at])
m4_include([tests/vconn.at])
m4_include([tests/dir_name.at])
m4_include([tests/aes128.at])
m4_include([tests/uuid.at])
m4_include([tests/json.at])
m4_include([tests/jsonrpc.at])
+m4_include([tests/jsonrpc-py.at])
m4_include([tests/timeval.at])
m4_include([tests/lockfile.at])
m4_include([tests/reconnect.at])