blob: 79049a5b66298c55d485c01b24ad12aa1f5a50cd [file] [log] [blame]
Rich Lanea06d0c32013-03-25 08:52:03 -07001#!/usr/bin/env python
2# Copyright 2013, Big Switch Networks, Inc.
3#
4# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
5# the following special exception:
6#
7# LOXI Exception
8#
9# As a special exception to the terms of the EPL, you may distribute libraries
10# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
11# that copyright and licensing notices generated by LoxiGen are not altered or removed
12# from the LoxiGen Libraries and the notice provided below is (i) included in
13# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
14# documentation for the LoxiGen Libraries, if distributed in binary form.
15#
16# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
17#
18# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
19# a copy of the EPL at:
20#
21# http://www.eclipse.org/legal/epl-v10.html
22#
23# Unless required by applicable law or agreed to in writing, software
24# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
25# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
26# EPL for the specific language governing permissions and limitations
27# under the EPL.
28import unittest
29
30try:
Rich Lanefb7de0e2013-03-29 16:50:29 -070031 import loxi.of10 as ofp
Rich Lane57026dc2013-05-01 10:13:16 -070032 from loxi.generic_util import OFReader
Rich Lanea06d0c32013-03-25 08:52:03 -070033except ImportError:
34 exit("loxi package not found. Try setting PYTHONPATH.")
35
36class TestImports(unittest.TestCase):
37 def test_toplevel(self):
38 import loxi
39 self.assertTrue(hasattr(loxi, "ProtocolError"))
Rich Lane00549ea2013-04-25 13:33:16 -070040 self.assertEquals(loxi.version_names[1], "1.0")
Rich Lanea06d0c32013-03-25 08:52:03 -070041 ofp = loxi.protocol(1)
42 self.assertEquals(ofp.OFP_VERSION, 1)
43 self.assertTrue(hasattr(ofp, "action"))
44 self.assertTrue(hasattr(ofp, "common"))
45 self.assertTrue(hasattr(ofp, "const"))
46 self.assertTrue(hasattr(ofp, "message"))
47
48 def test_version(self):
Rich Lanea94fb082013-04-04 17:04:17 -070049 import loxi
Rich Lanea06d0c32013-03-25 08:52:03 -070050 self.assertTrue(hasattr(loxi.of10, "ProtocolError"))
51 self.assertTrue(hasattr(loxi.of10, "OFP_VERSION"))
52 self.assertEquals(loxi.of10.OFP_VERSION, 1)
53 self.assertTrue(hasattr(loxi.of10, "action"))
54 self.assertTrue(hasattr(loxi.of10, "common"))
55 self.assertTrue(hasattr(loxi.of10, "const"))
56 self.assertTrue(hasattr(loxi.of10, "message"))
57
58class TestActions(unittest.TestCase):
59 def test_output_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070060 expected = "\x00\x00\x00\x08\xff\xf8\xff\xff"
61 action = ofp.action.output(port=ofp.OFPP_IN_PORT, max_len=0xffff)
62 self.assertEquals(expected, action.pack())
63
64 def test_output_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070065 # Normal case
66 buf = "\x00\x00\x00\x08\xff\xf8\xff\xff"
67 action = ofp.action.output.unpack(buf)
68 self.assertEqual(action.port, ofp.OFPP_IN_PORT)
69 self.assertEqual(action.max_len, 0xffff)
70
71 # Invalid length
Rich Lane57026dc2013-05-01 10:13:16 -070072 #buf = "\x00\x00\x00\x09\xff\xf8\xff\xff\x00"
73 #with self.assertRaises(ofp.ProtocolError):
74 # ofp.action.output.unpack(buf)
Rich Lanea06d0c32013-03-25 08:52:03 -070075
76 def test_output_equality(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070077 action = ofp.action.output(port=1, max_len=0x1234)
78 action2 = ofp.action.output(port=1, max_len=0x1234)
79 self.assertEquals(action, action2)
80
81 action2.port = 2
82 self.assertNotEquals(action, action2)
83 action2.port = 1
84
85 action2.max_len = 0xffff
86 self.assertNotEquals(action, action2)
87 action2.max_len = 0x1234
88
89 def test_output_show(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070090 action = ofp.action.output(port=1, max_len=0x1234)
91 expected = "output { port = 1, max_len = 0x1234 }"
92 self.assertEquals(expected, action.show())
93
94 def test_bsn_set_tunnel_dst_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -070095 expected = ''.join([
96 "\xff\xff", "\x00\x10", # type/length
97 "\x00\x5c\x16\xc7", # experimenter
98 "\x00\x00\x00\x02", # subtype
99 "\x12\x34\x56\x78" # dst
100 ])
101 action = ofp.action.bsn_set_tunnel_dst(dst=0x12345678)
102 self.assertEquals(expected, action.pack())
103
104 def test_bsn_set_tunnel_dst_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700105 buf = ''.join([
106 "\xff\xff", "\x00\x10", # type/length
107 "\x00\x5c\x16\xc7", # experimenter
108 "\x00\x00\x00\x02", # subtype
109 "\x12\x34\x56\x78" # dst
110 ])
111 action = ofp.action.bsn_set_tunnel_dst.unpack(buf)
112 self.assertEqual(action.subtype, 2)
113 self.assertEqual(action.dst, 0x12345678)
114
115# Assumes action serialization/deserialization works
116class TestActionList(unittest.TestCase):
117 def test_normal(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700118 expected = []
119 bufs = []
120
121 def add(action):
122 expected.append(action)
123 bufs.append(action.pack())
124
125 add(ofp.action.output(port=1, max_len=0xffff))
126 add(ofp.action.output(port=2, max_len=0xffff))
127 add(ofp.action.output(port=ofp.OFPP_IN_PORT, max_len=0xffff))
128 add(ofp.action.bsn_set_tunnel_dst(dst=0x12345678))
129 add(ofp.action.nicira_dec_ttl())
130
Rich Lane57026dc2013-05-01 10:13:16 -0700131 actions = ofp.action.unpack_list(OFReader(''.join(bufs)))
Rich Lanea06d0c32013-03-25 08:52:03 -0700132 self.assertEquals(actions, expected)
133
134 def test_empty_list(self):
Rich Lane57026dc2013-05-01 10:13:16 -0700135 self.assertEquals(ofp.action.unpack_list(OFReader('')), [])
Rich Lanea06d0c32013-03-25 08:52:03 -0700136
137 def test_invalid_list_length(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700138 buf = '\x00' * 9
Rich Lane57026dc2013-05-01 10:13:16 -0700139 with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
140 ofp.action.unpack_list(OFReader(buf))
Rich Lanea06d0c32013-03-25 08:52:03 -0700141
142 def test_invalid_action_length(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700143 buf = '\x00' * 8
Rich Lane57026dc2013-05-01 10:13:16 -0700144 with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
145 ofp.action.unpack_list(OFReader(buf))
Rich Lanea06d0c32013-03-25 08:52:03 -0700146
147 buf = '\x00\x00\x00\x04'
Rich Lane57026dc2013-05-01 10:13:16 -0700148 with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
149 ofp.action.unpack_list(OFReader(buf))
Rich Lanea06d0c32013-03-25 08:52:03 -0700150
151 buf = '\x00\x00\x00\x10\x00\x00\x00\x00'
Rich Lane57026dc2013-05-01 10:13:16 -0700152 with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
153 ofp.action.unpack_list(OFReader(buf))
Rich Lanea06d0c32013-03-25 08:52:03 -0700154
155 def test_invalid_action_type(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700156 buf = '\xff\xfe\x00\x08\x00\x00\x00\x00'
157 with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action type'):
Rich Lane57026dc2013-05-01 10:13:16 -0700158 ofp.action.unpack_list(OFReader(buf))
Rich Lanea06d0c32013-03-25 08:52:03 -0700159
160class TestConstants(unittest.TestCase):
161 def test_ports(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700162 self.assertEquals(0xffff, ofp.OFPP_NONE)
163
164 def test_wildcards(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700165 self.assertEquals(0xfc000, ofp.OFPFW_NW_DST_MASK)
166
167class TestCommon(unittest.TestCase):
168 def test_port_desc_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700169 obj = ofp.port_desc(port_no=ofp.OFPP_CONTROLLER,
170 hw_addr=[1,2,3,4,5,6],
171 name="foo",
172 config=ofp.OFPPC_NO_FLOOD,
173 state=ofp.OFPPS_STP_FORWARD,
174 curr=ofp.OFPPF_10MB_HD,
175 advertised=ofp.OFPPF_1GB_FD,
176 supported=ofp.OFPPF_AUTONEG,
177 peer=ofp.OFPPF_PAUSE_ASYM)
178 expected = ''.join([
179 '\xff\xfd', # port_no
180 '\x01\x02\x03\x04\x05\x06', # hw_addr
181 'foo'.ljust(16, '\x00'), # name
182 '\x00\x00\x00\x10', # config
183 '\x00\x00\x02\x00', # state
184 '\x00\x00\x00\x01', # curr
185 '\x00\x00\x00\x20', # advertised
186 '\x00\x00\x02\x00', # supported
187 '\x00\x00\x08\x00', # peer
188 ])
189 self.assertEquals(expected, obj.pack())
190
191 def test_port_desc_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700192 buf = ''.join([
193 '\xff\xfd', # port_no
194 '\x01\x02\x03\x04\x05\x06', # hw_addr
195 'foo'.ljust(16, '\x00'), # name
196 '\x00\x00\x00\x10', # config
197 '\x00\x00\x02\x00', # state
198 '\x00\x00\x00\x01', # curr
199 '\x00\x00\x00\x20', # advertised
200 '\x00\x00\x02\x00', # supported
201 '\x00\x00\x08\x00', # peer
202 ])
203 obj = ofp.port_desc.unpack(buf)
204 self.assertEquals(ofp.OFPP_CONTROLLER, obj.port_no)
205 self.assertEquals('foo', obj.name)
206 self.assertEquals(ofp.OFPPF_PAUSE_ASYM, obj.peer)
207
208 def test_table_stats_entry_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700209 obj = ofp.table_stats_entry(table_id=3,
210 name="foo",
211 wildcards=ofp.OFPFW_ALL,
212 max_entries=5,
213 active_count=2,
214 lookup_count=1099511627775,
215 matched_count=9300233470495232273L)
216 expected = ''.join([
217 '\x03', # table_id
218 '\x00\x00\x00', # pad
219 'foo'.ljust(32, '\x00'), # name
220 '\x00\x3f\xFF\xFF', # wildcards
221 '\x00\x00\x00\x05', # max_entries
222 '\x00\x00\x00\x02', # active_count
223 '\x00\x00\x00\xff\xff\xff\xff\xff', # lookup_count
224 '\x81\x11\x11\x11\x11\x11\x11\x11', # matched_count
225 ])
226 self.assertEquals(expected, obj.pack())
227
228 def test_table_stats_entry_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700229 buf = ''.join([
230 '\x03', # table_id
231 '\x00\x00\x00', # pad
232 'foo'.ljust(32, '\x00'), # name
233 '\x00\x3f\xFF\xFF', # wildcards
234 '\x00\x00\x00\x05', # max_entries
235 '\x00\x00\x00\x02', # active_count
236 '\x00\x00\x00\xff\xff\xff\xff\xff', # lookup_count
237 '\x81\x11\x11\x11\x11\x11\x11\x11', # matched_count
238 ])
239 obj = ofp.table_stats_entry.unpack(buf)
240 self.assertEquals(3, obj.table_id)
241 self.assertEquals('foo', obj.name)
242 self.assertEquals(9300233470495232273L, obj.matched_count)
243
244 def test_flow_stats_entry_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700245 obj = ofp.flow_stats_entry(table_id=3,
246 match=ofp.match(),
247 duration_sec=1,
248 duration_nsec=2,
249 priority=100,
250 idle_timeout=5,
251 hard_timeout=10,
252 cookie=0x0123456789abcdef,
253 packet_count=10,
254 byte_count=1000,
255 actions=[ofp.action.output(port=1),
256 ofp.action.output(port=2)])
257 expected = ''.join([
258 '\x00\x68', # length
259 '\x03', # table_id
260 '\x00', # pad
261 '\x00\x3f\xff\xff', # match.wildcards
262 '\x00' * 36, # remaining match fields
263 '\x00\x00\x00\x01', # duration_sec
264 '\x00\x00\x00\x02', # duration_nsec
265 '\x00\x64', # priority
266 '\x00\x05', # idle_timeout
267 '\x00\x0a', # hard_timeout
268 '\x00' * 6, # pad2
269 '\x01\x23\x45\x67\x89\xab\xcd\xef', # cookie
270 '\x00\x00\x00\x00\x00\x00\x00\x0a', # packet_count
271 '\x00\x00\x00\x00\x00\x00\x03\xe8', # byte_count
272 '\x00\x00', # actions[0].type
273 '\x00\x08', # actions[0].len
274 '\x00\x01', # actions[0].port
275 '\x00\x00', # actions[0].max_len
276 '\x00\x00', # actions[1].type
277 '\x00\x08', # actions[1].len
278 '\x00\x02', # actions[1].port
279 '\x00\x00', # actions[1].max_len
280 ])
281 self.assertEquals(expected, obj.pack())
282
283 def test_flow_stats_entry_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700284 buf = ''.join([
285 '\x00\x68', # length
286 '\x03', # table_id
287 '\x00', # pad
288 '\x00\x3f\xff\xff', # match.wildcards
289 '\x00' * 36, # remaining match fields
290 '\x00\x00\x00\x01', # duration_sec
291 '\x00\x00\x00\x02', # duration_nsec
292 '\x00\x64', # priority
293 '\x00\x05', # idle_timeout
294 '\x00\x0a', # hard_timeout
295 '\x00' * 6, # pad2
296 '\x01\x23\x45\x67\x89\xab\xcd\xef', # cookie
297 '\x00\x00\x00\x00\x00\x00\x00\x0a', # packet_count
298 '\x00\x00\x00\x00\x00\x00\x03\xe8', # byte_count
299 '\x00\x00', # actions[0].type
300 '\x00\x08', # actions[0].len
301 '\x00\x01', # actions[0].port
302 '\x00\x00', # actions[0].max_len
303 '\x00\x00', # actions[1].type
304 '\x00\x08', # actions[1].len
305 '\x00\x02', # actions[1].port
306 '\x00\x00', # actions[1].max_len
307 ])
308 obj = ofp.flow_stats_entry.unpack(buf)
309 self.assertEquals(3, obj.table_id)
310 self.assertEquals(ofp.OFPFW_ALL, obj.match.wildcards)
311 self.assertEquals(2, len(obj.actions))
312 self.assertEquals(1, obj.actions[0].port)
313 self.assertEquals(2, obj.actions[1].port)
314
315 def test_match(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700316 match = ofp.match()
317 self.assertEquals(match.wildcards, ofp.OFPFW_ALL)
318 self.assertEquals(match.tcp_src, 0)
319 buf = match.pack()
320 match2 = ofp.match.unpack(buf)
321 self.assertEquals(match, match2)
322
323class TestMessages(unittest.TestCase):
324 def test_hello_construction(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700325 msg = ofp.message.hello()
326 self.assertEquals(msg.version, ofp.OFP_VERSION)
327 self.assertEquals(msg.type, ofp.OFPT_HELLO)
328 self.assertEquals(msg.xid, None)
329
330 msg = ofp.message.hello(xid=123)
331 self.assertEquals(msg.xid, 123)
332
333 # 0 is a valid xid distinct from None
334 msg = ofp.message.hello(xid=0)
335 self.assertEquals(msg.xid, 0)
336
337 def test_hello_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700338 # Normal case
339 buf = "\x01\x00\x00\x08\x12\x34\x56\x78"
340 msg = ofp.message.hello(xid=0x12345678)
341 self.assertEquals(buf, msg.pack())
342
343 # Invalid length
Rich Lane57026dc2013-05-01 10:13:16 -0700344 #buf = "\x01\x00\x00\x09\x12\x34\x56\x78\x9a"
345 #with self.assertRaisesRegexp(ofp.ProtocolError, "should be 8"):
346 # ofp.message.hello.unpack(buf)
Rich Lanea06d0c32013-03-25 08:52:03 -0700347
348 def test_echo_request_construction(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700349 msg = ofp.message.echo_request(data="abc")
350 self.assertEquals(msg.data, "abc")
351
352 def test_echo_request_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700353 msg = ofp.message.echo_request(xid=0x12345678, data="abc")
354 buf = msg.pack()
355 self.assertEquals(buf, "\x01\x02\x00\x0b\x12\x34\x56\x78\x61\x62\x63")
356
357 msg2 = ofp.message.echo_request.unpack(buf)
358 self.assertEquals(msg, msg2)
359
360 def test_echo_request_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700361 # Normal case
362 buf = "\x01\x02\x00\x0b\x12\x34\x56\x78\x61\x62\x63"
363 msg = ofp.message.echo_request(xid=0x12345678, data="abc")
364 self.assertEquals(buf, msg.pack())
365
366 # Invalid length
367 buf = "\x01\x02\x00\x07\x12\x34\x56"
368 with self.assertRaisesRegexp(ofp.ProtocolError, "buffer too short"):
369 ofp.message.echo_request.unpack(buf)
370
371 def test_echo_request_equality(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700372 msg = ofp.message.echo_request(xid=0x12345678, data="abc")
373 msg2 = ofp.message.echo_request(xid=0x12345678, data="abc")
374 #msg2 = ofp.message.echo_request.unpack(msg.pack())
375 self.assertEquals(msg, msg2)
376
377 msg2.xid = 1
378 self.assertNotEquals(msg, msg2)
379 msg2.xid = msg.xid
380
381 msg2.data = "a"
382 self.assertNotEquals(msg, msg2)
383 msg2.data = msg.data
384
385 def test_echo_request_show(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700386 expected = "echo_request { xid = 0x12345678, data = 'ab\\x01' }"
387 msg = ofp.message.echo_request(xid=0x12345678, data="ab\x01")
388 self.assertEquals(msg.show(), expected)
389
390 def test_flow_add(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700391 match = ofp.match()
392 msg = ofp.message.flow_add(xid=1,
393 match=match,
394 cookie=1,
395 idle_timeout=5,
396 flags=ofp.OFPFF_CHECK_OVERLAP,
397 actions=[
398 ofp.action.output(port=1),
399 ofp.action.output(port=2),
400 ofp.action.output(port=ofp.OFPP_CONTROLLER,
401 max_len=1024)])
402 buf = msg.pack()
403 msg2 = ofp.message.flow_add.unpack(buf)
404 self.assertEquals(msg, msg2)
405
406 def test_port_mod_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700407 msg = ofp.message.port_mod(xid=2,
408 port_no=ofp.OFPP_CONTROLLER,
409 hw_addr=[1,2,3,4,5,6],
410 config=0x90ABCDEF,
411 mask=0xFF11FF11,
412 advertise=0xCAFE6789)
413 expected = "\x01\x0f\x00\x20\x00\x00\x00\x02\xff\xfd\x01\x02\x03\x04\x05\x06\x90\xab\xcd\xef\xff\x11\xff\x11\xca\xfe\x67\x89\x00\x00\x00\x00"
414 self.assertEquals(expected, msg.pack())
415
416 def test_desc_stats_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700417 msg = ofp.message.desc_stats_reply(xid=3,
418 flags=ofp.OFPSF_REPLY_MORE,
419 mfr_desc="The Indigo-2 Community",
420 hw_desc="Unknown server",
421 sw_desc="Indigo-2 LRI pre-release",
422 serial_num="11235813213455",
423 dp_desc="Indigo-2 LRI forwarding module")
424 expected = ''.join([
425 '\x01', '\x11', # version/type
426 '\x04\x2c', # length
427 '\x00\x00\x00\x03', # xid
428 '\x00\x00', # stats_type
429 '\x00\x01', # flags
430 'The Indigo-2 Community'.ljust(256, '\x00'), # mfr_desc
431 'Unknown server'.ljust(256, '\x00'), # hw_desc
432 'Indigo-2 LRI pre-release'.ljust(256, '\x00'), # sw_desc
433 '11235813213455'.ljust(32, '\x00'), # serial_num
434 'Indigo-2 LRI forwarding module'.ljust(256, '\x00'), # dp_desc
435 ])
436 self.assertEquals(expected, msg.pack())
437
438 def test_desc_stats_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700439 buf = ''.join([
440 '\x01', '\x11', # version/type
441 '\x04\x2c', # length
442 '\x00\x00\x00\x03', # xid
443 '\x00\x00', # stats_type
444 '\x00\x01', # flags
445 'The Indigo-2 Community'.ljust(256, '\x00'), # mfr_desc
446 'Unknown server'.ljust(256, '\x00'), # hw_desc
447 'Indigo-2 LRI pre-release'.ljust(256, '\x00'), # sw_desc
448 '11235813213455'.ljust(32, '\x00'), # serial_num
449 'Indigo-2 LRI forwarding module'.ljust(256, '\x00'), # dp_desc
450 ])
451 msg = ofp.message.desc_stats_reply.unpack(buf)
452 self.assertEquals('Indigo-2 LRI forwarding module', msg.dp_desc)
453 self.assertEquals('11235813213455', msg.serial_num)
454 self.assertEquals(ofp.OFPST_DESC, msg.stats_type)
455 self.assertEquals(ofp.OFPSF_REPLY_MORE, msg.flags)
456
457 def test_port_status_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700458 desc = ofp.port_desc(port_no=ofp.OFPP_CONTROLLER,
459 hw_addr=[1,2,3,4,5,6],
460 name="foo",
461 config=ofp.OFPPC_NO_FLOOD,
462 state=ofp.OFPPS_STP_FORWARD,
463 curr=ofp.OFPPF_10MB_HD,
464 advertised=ofp.OFPPF_1GB_FD,
465 supported=ofp.OFPPF_AUTONEG,
466 peer=ofp.OFPPF_PAUSE_ASYM)
467
468 msg = ofp.message.port_status(xid=4,
469 reason=ofp.OFPPR_DELETE,
470 desc=desc)
471 expected = ''.join([
472 '\x01', '\x0c', # version/type
473 '\x00\x40', # length
474 '\x00\x00\x00\x04', # xid
475 '\x01', # reason
476 '\x00\x00\x00\x00\x00\x00\x00' # pad
477 '\xff\xfd', # desc.port_no
478 '\x01\x02\x03\x04\x05\x06', # desc.hw_addr
479 'foo'.ljust(16, '\x00'), # desc.name
480 '\x00\x00\x00\x10', # desc.config
481 '\x00\x00\x02\x00', # desc.state
482 '\x00\x00\x00\x01', # desc.curr
483 '\x00\x00\x00\x20', # desc.advertised
484 '\x00\x00\x02\x00', # desc.supported
485 '\x00\x00\x08\x00', # desc.peer
486 ])
487 self.assertEquals(expected, msg.pack())
488
489 def test_port_status_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700490 buf = ''.join([
491 '\x01', '\x0c', # version/type
492 '\x00\x40', # length
493 '\x00\x00\x00\x04', # xid
494 '\x01', # reason
495 '\x00\x00\x00\x00\x00\x00\x00' # pad
496 '\xff\xfd', # desc.port_no
497 '\x01\x02\x03\x04\x05\x06', # desc.hw_addr
498 'foo'.ljust(16, '\x00'), # desc.name
499 '\x00\x00\x00\x10', # desc.config
500 '\x00\x00\x02\x00', # desc.state
501 '\x00\x00\x00\x01', # desc.curr
502 '\x00\x00\x00\x20', # desc.advertised
503 '\x00\x00\x02\x00', # desc.supported
504 '\x00\x00\x08\x00', # desc.peer
505 ])
506 msg = ofp.message.port_status.unpack(buf)
507 self.assertEquals('foo', msg.desc.name)
508 self.assertEquals(ofp.OFPPF_PAUSE_ASYM, msg.desc.peer)
509
510 def test_port_stats_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700511 msg = ofp.message.port_stats_reply(xid=5, flags=0, entries=[
512 ofp.port_stats_entry(port_no=1, rx_packets=56, collisions=5),
513 ofp.port_stats_entry(port_no=ofp.OFPP_LOCAL, rx_packets=1, collisions=1)])
514 expected = ''.join([
515 '\x01', '\x11', # version/type
516 '\x00\xdc', # length
517 '\x00\x00\x00\x05', # xid
518 '\x00\x04', # stats_type
519 '\x00\x00', # flags
520 '\x00\x01', # entries[0].port_no
521 '\x00\x00\x00\x00\x00\x00' # entries[0].pad
522 '\x00\x00\x00\x00\x00\x00\x00\x38', # entries[0].rx_packets
523 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_packets
524 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_bytes
525 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_bytes
526 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_dropped
527 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_dropped
528 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_errors
529 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_errors
530 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_frame_err
531 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_over_err
532 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_crc_err
533 '\x00\x00\x00\x00\x00\x00\x00\x05', # entries[0].collisions
534 '\xff\xfe', # entries[1].port_no
535 '\x00\x00\x00\x00\x00\x00' # entries[1].pad
536 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].rx_packets
537 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_packets
538 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_bytes
539 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_bytes
540 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_dropped
541 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_dropped
542 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_errors
543 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_errors
544 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_frame_err
545 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_over_err
546 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_crc_err
547 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].collisions
548 ])
549 self.assertEquals(expected, msg.pack())
550
551 def test_port_stats_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700552 buf = ''.join([
553 '\x01', '\x11', # version/type
554 '\x00\xdc', # length
555 '\x00\x00\x00\x05', # xid
556 '\x00\x04', # stats_type
557 '\x00\x00', # flags
558 '\x00\x01', # entries[0].port_no
559 '\x00\x00\x00\x00\x00\x00' # entries[0].pad
560 '\x00\x00\x00\x00\x00\x00\x00\x38', # entries[0].rx_packets
561 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_packets
562 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_bytes
563 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_bytes
564 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_dropped
565 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_dropped
566 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_errors
567 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].tx_errors
568 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_frame_err
569 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_over_err
570 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[0].rx_crc_err
571 '\x00\x00\x00\x00\x00\x00\x00\x05', # entries[0].collisions
572 '\xff\xfe', # entries[1].port_no
573 '\x00\x00\x00\x00\x00\x00' # entries[1].pad
574 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].rx_packets
575 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_packets
576 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_bytes
577 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_bytes
578 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_dropped
579 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_dropped
580 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_errors
581 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].tx_errors
582 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_frame_err
583 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_over_err
584 '\x00\x00\x00\x00\x00\x00\x00\x00', # entries[1].rx_crc_err
585 '\x00\x00\x00\x00\x00\x00\x00\x01', # entries[1].collisions
586 ])
587 msg = ofp.message.port_stats_reply.unpack(buf)
588 self.assertEquals(ofp.OFPST_PORT, msg.stats_type)
589 self.assertEquals(2, len(msg.entries))
590
591 sample_flow_stats_reply_buf = ''.join([
592 '\x01', '\x11', # version/type
593 '\x00\xe4', # length
594 '\x00\x00\x00\x06', # xid
595 '\x00\x01', # stats_type
596 '\x00\x00', # flags
597 '\x00\x68', # entries[0].length
598 '\x03', # entries[0].table_id
599 '\x00', # entries[0].pad
600 '\x00\x3f\xff\xff', # entries[0].match.wildcards
601 '\x00' * 36, # remaining match fields
602 '\x00\x00\x00\x01', # entries[0].duration_sec
603 '\x00\x00\x00\x02', # entries[0].duration_nsec
604 '\x00\x64', # entries[0].priority
605 '\x00\x05', # entries[0].idle_timeout
606 '\x00\x0a', # entries[0].hard_timeout
607 '\x00' * 6, # entries[0].pad2
608 '\x01\x23\x45\x67\x89\xab\xcd\xef', # entries[0].cookie
609 '\x00\x00\x00\x00\x00\x00\x00\x0a', # entries[0].packet_count
610 '\x00\x00\x00\x00\x00\x00\x03\xe8', # entries[0].byte_count
611 '\x00\x00', # entries[0].actions[0].type
612 '\x00\x08', # entries[0].actions[0].len
613 '\x00\x01', # entries[0].actions[0].port
614 '\x00\x00', # entries[0].actions[0].max_len
615 '\x00\x00', # entries[0].actions[1].type
616 '\x00\x08', # entries[0].actions[1].len
617 '\x00\x02', # entries[0].actions[1].port
618 '\x00\x00', # entries[0].actions[1].max_len
619 '\x00\x70', # entries[1].length
620 '\x04', # entries[1].table_id
621 '\x00', # entries[1].pad
622 '\x00\x3f\xff\xff', # entries[1].match.wildcards
623 '\x00' * 36, # remaining match fields
624 '\x00\x00\x00\x01', # entries[1].duration_sec
625 '\x00\x00\x00\x02', # entries[1].duration_nsec
626 '\x00\x64', # entries[1].priority
627 '\x00\x05', # entries[1].idle_timeout
628 '\x00\x0a', # entries[1].hard_timeout
629 '\x00' * 6, # entries[1].pad2
630 '\x01\x23\x45\x67\x89\xab\xcd\xef', # entries[1].cookie
631 '\x00\x00\x00\x00\x00\x00\x00\x0a', # entries[1].packet_count
632 '\x00\x00\x00\x00\x00\x00\x03\xe8', # entries[1].byte_count
633 '\x00\x00', # entries[1].actions[0].type
634 '\x00\x08', # entries[1].actions[0].len
635 '\x00\x01', # entries[1].actions[0].port
636 '\x00\x00', # entries[1].actions[0].max_len
637 '\x00\x00', # entries[1].actions[1].type
638 '\x00\x08', # entries[1].actions[1].len
639 '\x00\x02', # entries[1].actions[1].port
640 '\x00\x00', # entries[1].actions[1].max_len
641 '\x00\x00', # entries[1].actions[2].type
642 '\x00\x08', # entries[1].actions[2].len
643 '\x00\x03', # entries[1].actions[2].port
644 '\x00\x00', # entries[1].actions[2].max_len
645 ])
646
647 def test_flow_stats_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700648 msg = ofp.message.flow_stats_reply(xid=6, flags=0, entries=[
649 ofp.flow_stats_entry(table_id=3,
650 match=ofp.match(),
651 duration_sec=1,
652 duration_nsec=2,
653 priority=100,
654 idle_timeout=5,
655 hard_timeout=10,
656 cookie=0x0123456789abcdef,
657 packet_count=10,
658 byte_count=1000,
659 actions=[ofp.action.output(port=1),
660 ofp.action.output(port=2)]),
661 ofp.flow_stats_entry(table_id=4,
662 match=ofp.match(),
663 duration_sec=1,
664 duration_nsec=2,
665 priority=100,
666 idle_timeout=5,
667 hard_timeout=10,
668 cookie=0x0123456789abcdef,
669 packet_count=10,
670 byte_count=1000,
671 actions=[ofp.action.output(port=1),
672 ofp.action.output(port=2),
673 ofp.action.output(port=3)])])
674 self.assertEquals(self.sample_flow_stats_reply_buf, msg.pack())
675
676 def test_flow_stats_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700677 msg = ofp.message.flow_stats_reply.unpack(self.sample_flow_stats_reply_buf)
678 self.assertEquals(ofp.OFPST_FLOW, msg.stats_type)
679 self.assertEquals(2, len(msg.entries))
680 self.assertEquals(2, len(msg.entries[0].actions))
681 self.assertEquals(3, len(msg.entries[1].actions))
682
683 def test_flow_add_show(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700684 expected = """\
685flow_add {
686 xid = None,
687 match = match_v1 {
688 wildcards = OFPFW_DL_SRC|OFPFW_DL_DST,
689 in_port = 3,
690 eth_src = 01:23:45:67:89:ab,
691 eth_dst = cd:ef:01:23:45:67,
692 vlan_vid = 0x0,
693 vlan_pcp = 0x0,
Rich Lanea06d0c32013-03-25 08:52:03 -0700694 eth_type = 0x0,
695 ip_dscp = 0x0,
696 ip_proto = 0x0,
Rich Lanea06d0c32013-03-25 08:52:03 -0700697 ipv4_src = 192.168.3.127,
698 ipv4_dst = 255.255.255.255,
699 tcp_src = 0x0,
700 tcp_dst = 0x0
701 },
702 cookie = 0x0,
703 idle_timeout = 0x0,
704 hard_timeout = 0x0,
705 priority = 0x0,
706 buffer_id = 0x0,
707 out_port = 0,
708 flags = 0x0,
709 actions = [
710 output { port = OFPP_FLOOD, max_len = 0x0 },
Rich Lanec2685792013-04-30 14:08:33 -0700711 nicira_dec_ttl { },
Rich Lanea06d0c32013-03-25 08:52:03 -0700712 bsn_set_tunnel_dst { dst = 0x0 }
713 ]
714}"""
715 msg = ofp.message.flow_add(
716 match=ofp.match(
717 wildcards=ofp.OFPFW_DL_SRC|ofp.OFPFW_DL_DST,
718 in_port=3,
719 ipv4_src=0xc0a8037f,
720 ipv4_dst=0xffffffff,
721 eth_src=[0x01, 0x23, 0x45, 0x67, 0x89, 0xab],
722 eth_dst=[0xcd, 0xef, 0x01, 0x23, 0x45, 0x67]),
723 actions=[
724 ofp.action.output(port=ofp.OFPP_FLOOD),
725 ofp.action.nicira_dec_ttl(),
726 ofp.action.bsn_set_tunnel_dst()])
727 self.assertEquals(msg.show(), expected)
728
729 sample_packet_out_buf = ''.join([
730 '\x01', '\x0d', # version/type
731 '\x00\x23', # length
732 '\x12\x34\x56\x78', # xid
733 '\xab\xcd\xef\x01', # buffer_id
734 '\xff\xfe', # in_port
735 '\x00\x10', # actions_len
736 '\x00\x00', # actions[0].type
737 '\x00\x08', # actions[0].len
738 '\x00\x01', # actions[0].port
739 '\x00\x00', # actions[0].max_len
740 '\x00\x00', # actions[1].type
741 '\x00\x08', # actions[1].len
742 '\x00\x02', # actions[1].port
743 '\x00\x00', # actions[1].max_len
744 'abc' # data
745 ])
746
747 def test_packet_out_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700748 msg = ofp.message.packet_out(
749 xid=0x12345678,
750 buffer_id=0xabcdef01,
751 in_port=ofp.OFPP_LOCAL,
752 actions=[
753 ofp.action.output(port=1),
754 ofp.action.output(port=2)],
755 data='abc')
756 self.assertEquals(self.sample_packet_out_buf, msg.pack())
757
758 def test_packet_out_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700759 msg = ofp.message.packet_out.unpack(self.sample_packet_out_buf)
760 self.assertEquals(0x12345678, msg.xid)
761 self.assertEquals(0xabcdef01, msg.buffer_id)
762 self.assertEquals(ofp.OFPP_LOCAL, msg.in_port)
763 self.assertEquals(2, len(msg.actions))
764 self.assertEquals(1, msg.actions[0].port)
765 self.assertEquals(2, msg.actions[1].port)
766 self.assertEquals('abc', msg.data)
767
768 sample_packet_in_buf = ''.join([
769 '\x01', '\x0a', # version/type
770 '\x00\x15', # length
771 '\x12\x34\x56\x78', # xid
772 '\xab\xcd\xef\x01', # buffer_id
773 '\x00\x09', # total_len
774 '\xff\xfe', # in_port
775 '\x01', # reason
776 '\x00', # pad
777 'abc', # data
778 ])
779
780 def test_packet_in_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700781 msg = ofp.message.packet_in(
782 xid=0x12345678,
783 buffer_id=0xabcdef01,
784 total_len=9,
785 in_port=ofp.OFPP_LOCAL,
786 reason=ofp.OFPR_ACTION,
787 data='abc')
788 self.assertEquals(self.sample_packet_in_buf, msg.pack())
789
790 def test_packet_in_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700791 msg = ofp.message.packet_in.unpack(self.sample_packet_in_buf)
792 self.assertEquals(0x12345678, msg.xid)
793 self.assertEquals(0xabcdef01, msg.buffer_id)
794 self.assertEquals(9, msg.total_len)
795 self.assertEquals(ofp.OFPP_LOCAL, msg.in_port)
796 self.assertEquals(ofp.OFPR_ACTION, msg.reason)
797 self.assertEquals('abc', msg.data)
798
799 sample_queue_get_config_reply_buf = ''.join([
800 '\x01', '\x15', # version/type
801 '\x00\x50', # length
802 '\x12\x34\x56\x78', # xid
803 '\xff\xfe', # port
804 '\x00\x00\x00\x00\x00\x00', # pad
805 '\x00\x00\x00\x01', # queues[0].queue_id
806 '\x00\x18', # queues[0].len
807 '\x00\x00', # queues[0].pad
808 '\x00\x01', # queues[0].properties[0].type
809 '\x00\x10', # queues[0].properties[0].length
810 '\x00\x00\x00\x00', # queues[0].properties[0].pad
811 '\x00\x05', # queues[0].properties[0].rate
812 '\x00\x00\x00\x00\x00\x00', # queues[0].properties[0].pad2
813 '\x00\x00\x00\x02', # queues[1].queue_id
814 '\x00\x28', # queues[1].len
815 '\x00\x00', # queues[1].pad
816 '\x00\x01', # queues[1].properties[0].type
817 '\x00\x10', # queues[1].properties[0].length
818 '\x00\x00\x00\x00', # queues[1].properties[0].pad
819 '\x00\x06', # queues[1].properties[0].rate
820 '\x00\x00\x00\x00\x00\x00', # queues[1].properties[0].pad2
821 '\x00\x01', # queues[1].properties[1].type
822 '\x00\x10', # queues[1].properties[1].length
823 '\x00\x00\x00\x00', # queues[1].properties[1].pad
824 '\x00\x07', # queues[1].properties[1].rate
825 '\x00\x00\x00\x00\x00\x00', # queues[1].properties[1].pad2
826 ])
827
828 def test_queue_get_config_reply_pack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700829 msg = ofp.message.queue_get_config_reply(
830 xid=0x12345678,
831 port=ofp.OFPP_LOCAL,
832 queues=[
833 ofp.packet_queue(queue_id=1, properties=[
834 ofp.queue_prop_min_rate(rate=5)]),
835 ofp.packet_queue(queue_id=2, properties=[
836 ofp.queue_prop_min_rate(rate=6),
837 ofp.queue_prop_min_rate(rate=7)])])
838 self.assertEquals(self.sample_queue_get_config_reply_buf, msg.pack())
839
840 def test_queue_get_config_reply_unpack(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700841 msg = ofp.message.queue_get_config_reply.unpack(self.sample_queue_get_config_reply_buf)
842 self.assertEquals(ofp.OFPP_LOCAL, msg.port)
843 self.assertEquals(msg.queues[0].queue_id, 1)
844 self.assertEquals(msg.queues[0].properties[0].rate, 5)
845 self.assertEquals(msg.queues[1].queue_id, 2)
846 self.assertEquals(msg.queues[1].properties[0].rate, 6)
847 self.assertEquals(msg.queues[1].properties[1].rate, 7)
848
849class TestParse(unittest.TestCase):
850 def test_parse_header(self):
851 import loxi
Rich Lanea06d0c32013-03-25 08:52:03 -0700852
853 msg_ver, msg_type, msg_len, msg_xid = ofp.message.parse_header("\x01\x04\xAF\xE8\x12\x34\x56\x78")
854 self.assertEquals(1, msg_ver)
855 self.assertEquals(4, msg_type)
856 self.assertEquals(45032, msg_len)
857 self.assertEquals(0x12345678, msg_xid)
858
859 with self.assertRaisesRegexp(loxi.ProtocolError, "too short"):
860 ofp.message.parse_header("\x01\x04\xAF\xE8\x12\x34\x56")
861
862 def test_parse_message(self):
863 import loxi
864 import loxi.of10 as ofp
865
866 buf = "\x01\x00\x00\x08\x12\x34\x56\x78"
867 msg = ofp.message.parse_message(buf)
868 assert(msg.xid == 0x12345678)
869
870 # Get a list of all message classes
871 test_klasses = [x for x in ofp.message.__dict__.values()
872 if type(x) == type
873 and issubclass(x, ofp.message.Message)
874 and x != ofp.message.Message]
875
876 for klass in test_klasses:
877 self.assertIsInstance(ofp.message.parse_message(klass(xid=1).pack()), klass)
878
879class TestUtils(unittest.TestCase):
Rich Lanea06d0c32013-03-25 08:52:03 -0700880 def test_pretty_wildcards(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700881 self.assertEquals("OFPFW_ALL", ofp.util.pretty_wildcards(ofp.OFPFW_ALL))
882 self.assertEquals("0", ofp.util.pretty_wildcards(0))
883 self.assertEquals("OFPFW_DL_SRC|OFPFW_DL_DST",
884 ofp.util.pretty_wildcards(ofp.OFPFW_DL_SRC|ofp.OFPFW_DL_DST))
885 self.assertEquals("OFPFW_NW_SRC_MASK&0x2000",
886 ofp.util.pretty_wildcards(ofp.OFPFW_NW_SRC_ALL))
887 self.assertEquals("OFPFW_NW_SRC_MASK&0x1a00",
888 ofp.util.pretty_wildcards(0x00001a00))
889 self.assertEquals("OFPFW_IN_PORT|0x80000000",
890 ofp.util.pretty_wildcards(ofp.OFPFW_IN_PORT|0x80000000))
891
892class TestAll(unittest.TestCase):
893 """
894 Round-trips every class through serialization/deserialization.
895 Not a replacement for handcoded tests because it only uses the
896 default member values.
897 """
898
899 def setUp(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700900 mods = [ofp.action,ofp.message,ofp.common]
901 self.klasses = [klass for mod in mods
902 for klass in mod.__dict__.values()
903 if hasattr(klass, 'show')]
904 self.klasses.sort(key=lambda x: str(x))
905
906 def test_serialization(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700907 expected_failures = []
908 for klass in self.klasses:
909 def fn():
910 obj = klass()
911 if hasattr(obj, "xid"): obj.xid = 42
912 buf = obj.pack()
913 obj2 = klass.unpack(buf)
914 self.assertEquals(obj, obj2)
915 if klass in expected_failures:
916 self.assertRaises(Exception, fn)
917 else:
918 fn()
919
920 def test_show(self):
Rich Lanea06d0c32013-03-25 08:52:03 -0700921 expected_failures = []
922 for klass in self.klasses:
923 def fn():
924 obj = klass()
925 if hasattr(obj, "xid"): obj.xid = 42
926 obj.show()
927 if klass in expected_failures:
928 self.assertRaises(Exception, fn)
929 else:
930 fn()
931
932if __name__ == '__main__':
933 unittest.main()