diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test_stanza_xep_0323.py | 393 | ||||
-rw-r--r-- | tests/test_stanza_xep_0325.py | 248 | ||||
-rw-r--r-- | tests/test_stream_xep_0323.py | 1250 | ||||
-rw-r--r-- | tests/test_stream_xep_0325.py | 365 |
4 files changed, 2256 insertions, 0 deletions
diff --git a/tests/test_stanza_xep_0323.py b/tests/test_stanza_xep_0323.py new file mode 100644 index 00000000..67e0daf0 --- /dev/null +++ b/tests/test_stanza_xep_0323.py @@ -0,0 +1,393 @@ +# -*- coding: utf-8 -*- + +from sleekxmpp.test import * +import sleekxmpp.plugins.xep_0323 as xep_0323 + +namespace='sn' + +class TestSensorDataStanzas(SleekTest): + + + def setUp(self): + pass + #register_stanza_plugin(Iq, xep_0323.stanza.Request) + #register_stanza_plugin(Iq, xep_0323.stanza.Accepted) + #register_stanza_plugin(Message, xep_0323.stanza.Failure) + #register_stanza_plugin(xep_0323.stanza.Failure, xep_0323.stanza.Error) + #register_stanza_plugin(Iq, xep_0323.stanza.Rejected) + #register_stanza_plugin(Message, xep_0323.stanza.Fields) + #register_stanza_plugin(Message, xep_0323.stanza.Request) + #register_stanza_plugin(Message, xep_0323.stanza.Accepted) + #register_stanza_plugin(Message, xep_0323.stanza.Failure) + # register_stanza_plugin(Message, xep_0323.stanza.Result) + # register_stanza_plugin(Message, xep_0323.stanza.Gone) + # register_stanza_plugin(Message, xep_0323.stanza.Inactive) + # register_stanza_plugin(Message, xep_0323.stanza.Paused) + + def testRequest(self): + """ + test of request stanza + """ + iq = self.Iq() + iq['type'] = 'get' + iq['from'] = 'master@clayster.com/amr' + iq['to'] = 'device@clayster.com' + iq['id'] = '1' + iq['req']['seqnr'] = '1' + iq['req']['momentary'] = 'true' + + self.check(iq,""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true'/> + </iq> + """ + ) + + def testRequestNodes(self): + """ + test of request nodes stanza + """ + iq = self.Iq() + iq['type'] = 'get' + iq['from'] = 'master@clayster.com/amr' + iq['to'] = 'device@clayster.com' + iq['id'] = '1' + iq['req']['seqnr'] = '1' + iq['req']['momentary'] = 'true' + + + iq['req'].add_node("Device02", "Source02", "CacheType"); + iq['req'].add_node("Device44"); + + self.check(iq,""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true'> + <node nodeId='Device02' sourceId='Source02' cacheType='CacheType'/> + <node nodeId='Device44'/> + </req> + </iq> + """ + ) + + iq['req'].del_node("Device02"); + + self.check(iq,""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true'> + <node nodeId='Device44'/> + </req> + </iq> + """ + ) + + iq['req'].del_nodes(); + + self.check(iq,""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true'> + </req> + </iq> + """ + ) + + def testRequestField(self): + """ + test of request field stanza + """ + iq = self.Iq() + iq['type'] = 'get' + iq['from'] = 'master@clayster.com/amr' + iq['to'] = 'device@clayster.com' + iq['id'] = '1' + iq['req']['seqnr'] = '1' + iq['req']['momentary'] = 'true' + + + iq['req'].add_field("Top temperature"); + iq['req'].add_field("Bottom temperature"); + + self.check(iq,""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true'> + <field name='Top temperature'/> + <field name='Bottom temperature'/> + </req> + </iq> + """ + ) + + iq['req'].del_field("Top temperature") + + self.check(iq,""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true'> + <field name='Bottom temperature'/> + </req> + </iq> + """ + ) + + iq['req'].del_fields() + + self.check(iq,""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true'> + </req> + </iq> + """ + ) + + + def testAccepted(self): + """ + test of request stanza + """ + iq = self.Iq() + iq['type'] = 'result' + iq['from'] = 'device@clayster.com' + iq['to'] = 'master@clayster.com/amr' + iq['id'] = '2' + iq['accepted']['seqnr'] = '2' + + self.check(iq,""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='2'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='2'/> + </iq> + """ + ) + + def testRejected(self): + """ + test of request stanza + """ + iq = self.Iq() + iq['type'] = 'error' + iq['from'] = 'device@clayster.com' + iq['to'] = 'master@clayster.com/amr' + iq['id'] = '4' + iq['rejected']['seqnr'] = '4' + iq['rejected']['error'] = 'Access denied.' + + self.check(iq,""" + <iq type='error' + from='device@clayster.com' + to='master@clayster.com/amr' + id='4'> + <rejected xmlns='urn:xmpp:iot:sensordata' seqnr='4'> + <error>Access denied.</error> + </rejected> + </iq> + """ + ) + + def testFailure(self): + """ + test of failure stanza + """ + msg = self.Message() + msg['from'] = 'device@clayster.com' + msg['to'] = 'master@clayster.com/amr' + msg['failure']['seqnr'] = '3' + msg['failure']['done'] = 'true' + msg['failure']['error']['nodeId'] = 'Device01' + msg['failure']['error']['timestamp'] = '2013-03-07T17:13:30' + msg['failure']['error']['text'] = 'Timeout.' + + self.check(msg,""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <failure xmlns='urn:xmpp:iot:sensordata' seqnr='3' done='true'> + <error nodeId='Device01' timestamp='2013-03-07T17:13:30'> + Timeout.</error> + </failure> + </message> + """ + ) + + def testFields(self): + """ + test of fields stanza + """ + msg = self.Message() + msg['from'] = 'device@clayster.com' + msg['to'] = 'master@clayster.com/amr' + msg['fields']['seqnr'] = '1' + + node = msg['fields'].add_node("Device02"); + ts = node.add_timestamp("2013-03-07T16:24:30"); + + data = ts.add_data(typename="numeric", name="Temperature", value="-12.42", unit='K'); + data['momentary'] = 'true'; + data['automaticReadout'] = 'true'; + + self.check(msg,""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId='Device02'> + <timestamp value='2013-03-07T16:24:30'> + <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/> + </timestamp> + </node> + </fields> + </message> + """ + ) + + node = msg['fields'].add_node("EmptyDevice"); + node = msg['fields'].add_node("Device04"); + ts = node.add_timestamp("EmptyTimestamp"); + + + self.check(msg,""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId='Device02'> + <timestamp value='2013-03-07T16:24:30'> + <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/> + </timestamp> + </node> + <node nodeId='EmptyDevice'/> + <node nodeId='Device04'> + <timestamp value='EmptyTimestamp'/> + </node> + </fields> + </message> + """ + ) + + node = msg['fields'].add_node("Device77"); + ts = node.add_timestamp("2013-05-03T12:00:01"); + data = ts.add_data(typename="numeric", name="Temperature", value="-12.42", unit='K'); + data['historicalDay'] = 'true'; + data = ts.add_data(typename="numeric", name="Speed", value="312.42", unit='km/h'); + data['historicalWeek'] = 'false'; + data = ts.add_data(typename="string", name="Temperature name", value="Bottom oil"); + data['historicalMonth'] = 'true'; + data = ts.add_data(typename="string", name="Speed name", value="Top speed"); + data['historicalQuarter'] = 'false'; + data = ts.add_data(typename="dateTime", name="T1", value="1979-01-01T00:00:00"); + data['historicalYear'] = 'true'; + data = ts.add_data(typename="dateTime", name="T2", value="2000-01-01T01:02:03"); + data['historicalOther'] = 'false'; + data = ts.add_data(typename="timeSpan", name="TS1", value="P5Y"); + data['missing'] = 'true'; + data = ts.add_data(typename="timeSpan", name="TS2", value="PT2M1S"); + data['manualEstimate'] = 'false'; + data = ts.add_data(typename="enum", name="top color", value="red", dataType="string"); + data['invoiced'] = 'true'; + data = ts.add_data(typename="enum", name="bottom color", value="black", dataType="string"); + data['powerFailure'] = 'false'; + data = ts.add_data(typename="boolean", name="Temperature real", value="false"); + data['historicalDay'] = 'true'; + data = ts.add_data(typename="boolean", name="Speed real", value="true"); + data['historicalWeek'] = 'false'; + + self.check(msg,""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId='Device02'> + <timestamp value='2013-03-07T16:24:30'> + <numeric name='Temperature' momentary='true' automaticReadout='true' value='-12.42' unit='K'/> + </timestamp> + </node> + <node nodeId='EmptyDevice'/> + <node nodeId='Device04'> + <timestamp value='EmptyTimestamp'/> + </node> + <node nodeId='Device77'> + <timestamp value='2013-05-03T12:00:01'> + <numeric name='Temperature' historicalDay='true' value='-12.42' unit='K'/> + <numeric name='Speed' historicalWeek='false' value='312.42' unit='km/h'/> + <string name='Temperature name' historicalMonth='true' value='Bottom oil'/> + <string name='Speed name' historicalQuarter='false' value='Top speed'/> + <dateTime name='T1' historicalYear='true' value='1979-01-01T00:00:00'/> + <dateTime name='T2' historicalOther='false' value='2000-01-01T01:02:03'/> + <timeSpan name='TS1' missing='true' value='P5Y'/> + <timeSpan name='TS2' manualEstimate='false' value='PT2M1S'/> + <enum name='top color' invoiced='true' value='red' dataType='string'/> + <enum name='bottom color' powerFailure='false' value='black' dataType='string'/> + <boolean name='Temperature real' historicalDay='true' value='false'/> + <boolean name='Speed real' historicalWeek='false' value='true'/> + </timestamp> + </node> + </fields> + </message> + """ + ) + + + def testTimestamp(self): + msg = self.Message(); + + msg['from'] = 'device@clayster.com' + msg['to'] = 'master@clayster.com/amr' + msg['fields']['seqnr'] = '1' + + node = msg['fields'].add_node("Device02"); + node = msg['fields'].add_node("Device03"); + + ts = node.add_timestamp("2013-03-07T16:24:30"); + ts = node.add_timestamp("2013-03-07T16:24:31"); + + + + self.check(msg,""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId='Device02'/> + <node nodeId='Device03'> + <timestamp value='2013-03-07T16:24:30'/> + <timestamp value='2013-03-07T16:24:31'/> + </node> + </fields> + </message> + """ + ) + + + def testStringIdsMatcher(self): + """ + test of StringIds follow spec + """ + emptyStringIdXML='<message xmlns="jabber:client"><fields xmlns="urn:xmpp:iot:sensordata" /></message>' + + msg = self.Message() + msg['fields']['stringIds'] = "Nisse" + self.check(msg,emptyStringIdXML) + msg['fields']['stringIds'] = "Nisse___nje#" + self.check(msg,emptyStringIdXML) + msg['fields']['stringIds'] = "1" + self.check(msg,emptyStringIdXML) + + + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestSensorDataStanzas) diff --git a/tests/test_stanza_xep_0325.py b/tests/test_stanza_xep_0325.py new file mode 100644 index 00000000..b15b764c --- /dev/null +++ b/tests/test_stanza_xep_0325.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +""" + SleekXMPP: The Sleek XMPP Library + Implementation of xeps for Internet of Things + http://wiki.xmpp.org/web/Tech_pages/IoT_systems + Copyright (C) 2013 Sustainable Innovation, Joachim.lindborg@sust.se, bjorn.westrom@consoden.se + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.test import * +import sleekxmpp.plugins.xep_0325 as xep_0325 + +namespace='sn' + +class TestControlStanzas(SleekTest): + + + def setUp(self): + pass + + def testSetRequest(self): + """ + test of set request stanza + """ + iq = self.Iq() + iq['type'] = 'set' + iq['from'] = 'master@clayster.com/amr' + iq['to'] = 'device@clayster.com' + iq['id'] = '1' + iq['set'].add_node("Device02", "Source02", "MyCacheType"); + iq['set'].add_node("Device15"); + iq['set'].add_data("Tjohej", "boolean", "true") + + self.check(iq,""" + <iq type='set' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <set xmlns='urn:xmpp:iot:control'> + <node nodeId='Device02' sourceId='Source02' cacheType='MyCacheType'/> + <node nodeId='Device15'/> + <boolean name='Tjohej' value='true'/> + </set> + </iq> + """ + ) + + iq['set'].del_node("Device02"); + + self.check(iq,""" + <iq type='set' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <set xmlns='urn:xmpp:iot:control'> + <node nodeId='Device15'/> + <boolean name='Tjohej' value='true'/> + </set> + </iq> + """ + ) + + iq['set'].del_nodes(); + + self.check(iq,""" + <iq type='set' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <set xmlns='urn:xmpp:iot:control'> + <boolean name='Tjohej' value='true'/> + </set> + </iq> + """ + ) + + + def testDirectSet(self): + """ + test of direct set stanza + """ + msg = self.Message() + msg['from'] = 'master@clayster.com/amr' + msg['to'] = 'device@clayster.com' + msg['set'].add_node("Device02"); + msg['set'].add_node("Device15"); + msg['set'].add_data("Tjohej", "boolean", "true") + + self.check(msg,""" + <message + from='master@clayster.com/amr' + to='device@clayster.com'> + <set xmlns='urn:xmpp:iot:control'> + <node nodeId='Device02'/> + <node nodeId='Device15'/> + <boolean name='Tjohej' value='true'/> + </set> + </message> + """ + ) + + + def testSetResponse(self): + """ + test of set response stanza + """ + iq = self.Iq() + iq['type'] = 'result' + iq['from'] = 'master@clayster.com/amr' + iq['to'] = 'device@clayster.com' + iq['id'] = '8' + iq['setResponse']['responseCode'] = "OK"; + + self.check(iq,""" + <iq type='result' + from='master@clayster.com/amr' + to='device@clayster.com' + id='8'> + <setResponse xmlns='urn:xmpp:iot:control' responseCode='OK' /> + </iq> + """ + ) + + iq = self.Iq() + iq['type'] = 'error' + iq['from'] = 'master@clayster.com/amr' + iq['to'] = 'device@clayster.com' + iq['id'] = '9' + iq['setResponse']['responseCode'] = "OtherError"; + iq['setResponse']['error']['var'] = "Output"; + iq['setResponse']['error']['text'] = "Test of other error.!"; + + + self.check(iq,""" + <iq type='error' + from='master@clayster.com/amr' + to='device@clayster.com' + id='9'> + <setResponse xmlns='urn:xmpp:iot:control' responseCode='OtherError'> + <error var='Output'>Test of other error.!</error> + </setResponse> + </iq> + """ + ) + + iq = self.Iq() + iq['type'] = 'error' + iq['from'] = 'master@clayster.com/amr' + iq['to'] = 'device@clayster.com' + iq['id'] = '9' + iq['setResponse']['responseCode'] = "NotFound"; + iq['setResponse'].add_node("Device17", "Source09"); + iq['setResponse'].add_node("Device18", "Source09"); + iq['setResponse'].add_data("Tjohopp"); + + + self.check(iq,""" + <iq type='error' + from='master@clayster.com/amr' + to='device@clayster.com' + id='9'> + <setResponse xmlns='urn:xmpp:iot:control' responseCode='NotFound'> + <node nodeId='Device17' sourceId='Source09'/> + <node nodeId='Device18' sourceId='Source09'/> + <parameter name='Tjohopp' /> + </setResponse> + </iq> + """ + ) + + def testSetRequestDatas(self): + """ + test of set request data stanzas + """ + iq = self.Iq() + iq['type'] = 'set' + iq['from'] = 'master@clayster.com/amr' + iq['to'] = 'device@clayster.com' + iq['id'] = '1' + iq['set'].add_node("Device02", "Source02", "MyCacheType"); + iq['set'].add_node("Device15"); + + iq['set'].add_data("Tjohej", "boolean", "true"); + iq['set'].add_data("Tjohej2", "boolean", "false"); + + iq['set'].add_data("TjohejC", "color", "FF00FF"); + iq['set'].add_data("TjohejC2", "color", "00FF00"); + + iq['set'].add_data("TjohejS", "string", "String1"); + iq['set'].add_data("TjohejS2", "string", "String2"); + + iq['set'].add_data("TjohejDate", "date", "2012-01-01"); + iq['set'].add_data("TjohejDate2", "date", "1900-12-03"); + + iq['set'].add_data("TjohejDateT4", "dateTime", "1900-12-03 12:30"); + iq['set'].add_data("TjohejDateT2", "dateTime", "1900-12-03 11:22"); + + iq['set'].add_data("TjohejDouble2", "double", "200.22"); + iq['set'].add_data("TjohejDouble3", "double", "-12232131.3333"); + + iq['set'].add_data("TjohejDur", "duration", "P5Y"); + iq['set'].add_data("TjohejDur2", "duration", "PT2M1S"); + + iq['set'].add_data("TjohejInt", "int", "1"); + iq['set'].add_data("TjohejInt2", "int", "-42"); + + iq['set'].add_data("TjohejLong", "long", "123456789098"); + iq['set'].add_data("TjohejLong2", "long", "-90983243827489374"); + + iq['set'].add_data("TjohejTime", "time", "23:59"); + iq['set'].add_data("TjohejTime2", "time", "12:00"); + + self.check(iq,""" + <iq type='set' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <set xmlns='urn:xmpp:iot:control'> + <node nodeId='Device02' sourceId='Source02' cacheType='MyCacheType'/> + <node nodeId='Device15'/> + <boolean name='Tjohej' value='true'/> + <boolean name='Tjohej2' value='false'/> + <color name='TjohejC' value='FF00FF'/> + <color name='TjohejC2' value='00FF00'/> + <string name='TjohejS' value='String1'/> + <string name='TjohejS2' value='String2'/> + <date name='TjohejDate' value='2012-01-01'/> + <date name='TjohejDate2' value='1900-12-03'/> + <dateTime name='TjohejDateT4' value='1900-12-03 12:30'/> + <dateTime name='TjohejDateT2' value='1900-12-03 11:22'/> + <double name='TjohejDouble2' value='200.22'/> + <double name='TjohejDouble3' value='-12232131.3333'/> + <duration name='TjohejDur' value='P5Y'/> + <duration name='TjohejDur2' value='PT2M1S'/> + <int name='TjohejInt' value='1'/> + <int name='TjohejInt2' value='-42'/> + <long name='TjohejLong' value='123456789098'/> + <long name='TjohejLong2' value='-90983243827489374'/> + <time name='TjohejTime' value='23:59'/> + <time name='TjohejTime2' value='12:00'/> + </set> + </iq> + """ + ) + +suite = unittest.TestLoader().loadTestsFromTestCase(TestControlStanzas) diff --git a/tests/test_stream_xep_0323.py b/tests/test_stream_xep_0323.py new file mode 100644 index 00000000..b10f0b0d --- /dev/null +++ b/tests/test_stream_xep_0323.py @@ -0,0 +1,1250 @@ +# -*- coding: utf-8 -*- + +import sys +import datetime +import time +import threading + +from sleekxmpp.test import * +from sleekxmpp.xmlstream import ElementBase +from sleekxmpp.plugins.xep_0323.device import Device + + +class TestStreamSensorData(SleekTest): + + """ + Test using the XEP-0323 plugin. + """ + def setUp(self): + pass + + def _time_now(self): + return datetime.datetime.now().replace(microsecond=0).isoformat(); + + def tearDown(self): + self.stream_close() + + def testRequestAccept(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device22"); + myDevice._add_field(name="Temperature", typename="numeric", unit="°C"); + myDevice._set_momentary_timestamp("2013-03-07T16:24:30") + myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}); + + self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true'/> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='1'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/> + </iq> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'> + <node nodeId='Device22'> + <timestamp value='2013-03-07T16:24:30'> + <numeric name='Temperature' momentary='true' automaticReadout='true' value='23.4' unit='°C'/> + </timestamp> + </node> + </fields> + </message> + """) + + def testRequestRejectAuth(self): + + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + self.xmpp['xep_0323']._set_authenticated("darth@deathstar.com"); + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='4'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='5' momentary='true'/> + </iq> + """) + + self.send(""" + <iq type='error' + from='device@clayster.com' + to='master@clayster.com/amr' + id='4'> + <rejected xmlns='urn:xmpp:iot:sensordata' seqnr='5'> + <error>Access denied</error> + </rejected> + </iq> + """) + + def testRequestNode(self): + + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device44"); + self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); + + print("."), + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='77'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='66' momentary='true'> + <node nodeId='Device33'/> + </req> + </iq> + """) + + self.send(""" + <iq type='error' + from='device@clayster.com' + to='master@clayster.com/amr' + id='77'> + <rejected xmlns='urn:xmpp:iot:sensordata' seqnr='66'> + <error>Invalid nodeId Device33</error> + </rejected> + </iq> + """) + + print("."), + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='8'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='7' momentary='true'> + <node nodeId='Device44'/> + </req> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='8'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='7'/> + </iq> + """) + + + def testRequestField(self): + + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device44"); + myDevice._add_field(name='Voltage', typename="numeric", unit="V"); + myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); + + self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); + + print("."), + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='7'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='6'> + <field name='Current'/> + </req> + </iq> + """) + + self.send(""" + <iq type='error' + from='device@clayster.com' + to='master@clayster.com/amr' + id='7'> + <rejected xmlns='urn:xmpp:iot:sensordata' seqnr='6'> + <error>Invalid field Current</error> + </rejected> + </iq> + """) + + print("."), + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='8'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='7'> + <field name='Voltage'/> + </req> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='8'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='7'/> + </iq> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'> + <node nodeId='Device44'> + <timestamp value='2000-01-01T00:01:02'> + <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'> + </fields> + </message> + """) + + def testRequestMultiTimestampSingleField(self): + + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device44"); + myDevice._add_field(name='Voltage', typename="numeric", unit="V"); + myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); + myDevice._add_field(name='Current', typename="numeric", unit="A"); + myDevice._add_field(name='Height', typename="string"); + myDevice._add_field_timestamp_data(name="Voltage", value="230.6", timestamp="2000-01-01T01:01:02"); + myDevice._add_field_timestamp_data(name="Height", value="115 m", timestamp="2000-01-01T01:01:02", flags={"invoiced": "true"}); + + self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); + + print("."), + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='8'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='7'> + <field name='Voltage'/> + </req> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='8'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='7'/> + </iq> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'> + <node nodeId='Device44'> + <timestamp value='2000-01-01T00:01:02'> + <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'> + <node nodeId='Device44'> + <timestamp value='2000-01-01T01:01:02'> + <numeric name='Voltage' value='230.6' unit='V'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'> + </fields> + </message> + """) + + def testRequestMultiTimestampAllFields(self): + + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device44"); + myDevice._add_field(name='Voltage', typename="numeric", unit="V"); + myDevice._add_field_timestamp_data(name="Voltage", value="230.4", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); + myDevice._add_field(name='Current', typename="numeric", unit="A"); + myDevice._add_field(name='Height', typename="string"); + myDevice._add_field_timestamp_data(name="Voltage", value="230.6", timestamp="2000-01-01T01:01:02"); + myDevice._add_field_timestamp_data(name="Height", value="115 m", timestamp="2000-01-01T01:01:02", flags={"invoiced": "true"}); + + self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); + + print("."), + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='8'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='7'/> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='8'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='7'/> + </iq> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'> + <node nodeId='Device44'> + <timestamp value='2000-01-01T00:01:02'> + <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7'> + <node nodeId='Device44'> + <timestamp value='2000-01-01T01:01:02'> + <numeric name='Voltage' value='230.6' unit='V'/> + <string name='Height' invoiced='true' value='115 m'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='7' done='true'> + </fields> + </message> + """) + + def testRequestAPI(self): + + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0323']) + + self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", callback=None); + + self.send(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1'/> + </iq> + """) + + self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=None); + + self.send(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='2'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='2'> + <node nodeId="Device33"/> + <node nodeId="Device22"/> + </req> + </iq> + """) + + self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", fields=['Temperature', 'Voltage'], callback=None); + + self.send(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='3'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='3'> + <field name="Temperature"/> + <field name="Voltage"/> + </req> + </iq> + """) + + def testRequestRejectAPI(self): + + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0323']) + + results = []; + + def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None): + if (result == "rejected") and (error_msg == "Invalid device Device22"): + results.append("rejected"); + + self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=my_callback); + + self.send(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId="Device33"/> + <node nodeId="Device22"/> + </req> + </iq> + """) + + self.recv(""" + <iq type='error' + from='you@google.com' + to='tester@localhost' + id='1'> + <rejected xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <error>Invalid device Device22</error> + </rejected> + </iq> + """) + + time.sleep(.1) + + self.failUnless(results == ["rejected"], + "Rejected callback was not properly executed"); + + def testRequestAcceptedAPI(self): + + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0323']) + + results = []; + + def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None): + results.append(result); + + self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33', 'Device22'], callback=my_callback); + + self.send(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId="Device33"/> + <node nodeId="Device22"/> + </req> + </iq> + """) + + self.recv(""" + <iq type='result' + from='you@google.com' + to='tester@localhost' + id='1'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/> + </iq> + """) + + time.sleep(.1) + + self.failUnless(results == ["accepted"], + "Accepted callback was not properly executed"); + + def testRequestFieldsAPI(self): + + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0323']) + + results = []; + callback_data = {}; + + def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None): + results.append(result); + if result == "fields": + callback_data["nodeId"] = nodeId; + callback_data["timestamp"] = timestamp; + callback_data["error_msg"] = error_msg; + for f in fields: + callback_data["field_" + f['name']] = f; + + t1= threading.Thread(name="request_data", + target=self.xmpp['xep_0323'].request_data, + kwargs={"from_jid": "tester@localhost", + "to_jid": "you@google.com", + "nodeIds": ['Device33'], + "callback": my_callback}); + t1.start(); + #self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback); + + self.send(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId="Device33"/> + </req> + </iq> + """) + + self.recv(""" + <iq type='result' + from='you@google.com' + to='tester@localhost' + id='1'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/> + </iq> + """) + + self.recv(""" + <message from='you@google.com' + to='tester@localhost'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId='Device33'> + <timestamp value='2000-01-01T00:01:02'> + <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/> + <boolean name='TestBool' value='true'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.recv(""" + <message from='you@google.com' + to='tester@localhost'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'/> + </message> + """) + + t1.join(); + time.sleep(.5) + + self.failUnlessEqual(results, ["accepted","fields","done"]); + # self.assertIn("nodeId", callback_data); + self.assertTrue(callback_data.has_key("nodeId")); + self.failUnlessEqual(callback_data["nodeId"], "Device33"); + # self.assertIn("timestamp", callback_data); + self.assertTrue(callback_data.has_key("timestamp")); + self.failUnlessEqual(callback_data["timestamp"], "2000-01-01T00:01:02"); + #self.assertIn("field_Voltage", callback_data); + self.assertTrue(callback_data.has_key("field_Voltage")); + self.failUnlessEqual(callback_data["field_Voltage"], {"name": "Voltage", "value": "230.4", "typename": "numeric", "unit": "V", "flags": {"invoiced": "true"}}); + #self.assertIn("field_TestBool", callback_data); + self.assertTrue(callback_data.has_key("field_TestBool")); + self.failUnlessEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" }); + + def testServiceDiscoveryClient(self): + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0323']); + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='tester@localhost' + id='disco1'> + <query xmlns='http://jabber.org/protocol/disco#info'/> + </iq> + """) + + self.send(""" + <iq type='result' + to='master@clayster.com/amr' + id='disco1'> + <query xmlns='http://jabber.org/protocol/disco#info'> + <identity category='client' type='bot'/> + <feature var='urn:xmpp:iot:sensordata'/> + </query> + </iq> + """) + + def testServiceDiscoveryComponent(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']); + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='tester@localhost' + id='disco1'> + <query xmlns='http://jabber.org/protocol/disco#info'/> + </iq> + """) + + self.send(""" + <iq type='result' + from='tester@localhost' + to='master@clayster.com/amr' + id='disco1'> + <query xmlns='http://jabber.org/protocol/disco#info'> + <identity category='component' type='generic'/> + <feature var='urn:xmpp:iot:sensordata'/> + </query> + </iq> + """) + + def testRequestTimeout(self): + + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0323']) + + results = []; + callback_data = {}; + + def my_callback(from_jid, result, nodeId=None, timestamp=None, error_msg=None): + results.append(result); + if result == "failure": + callback_data["nodeId"] = nodeId; + callback_data["timestamp"] = timestamp; + callback_data["error_msg"] = error_msg; + + t1= threading.Thread(name="request_data", + target=self.xmpp['xep_0323'].request_data, + kwargs={"from_jid": "tester@localhost", + "to_jid": "you@google.com", + "nodeIds": ['Device33'], + "callback": my_callback}); + t1.start(); + + self.send(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId="Device33"/> + </req> + </iq> + """) + + self.recv(""" + <iq type='result' + from='you@google.com' + to='tester@localhost' + id='1'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/> + </iq> + """) + + self.recv(""" + <message from='you@google.com' + to='tester@localhost'> + <failure xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'> + <error nodeId='Device33' timestamp='2013-03-07T17:13:30'>Timeout.</error> + </failure> + </message> + """) + + t1.join(); + time.sleep(.5) + + self.failUnlessEqual(results, ["accepted","failure"]); + # self.assertIn("nodeId", callback_data); + self.assertTrue(callback_data.has_key("nodeId")); + self.failUnlessEqual(callback_data["nodeId"], "Device33"); + # self.assertIn("timestamp", callback_data); + self.assertTrue(callback_data.has_key("timestamp")); + self.failUnlessEqual(callback_data["timestamp"], "2013-03-07T17:13:30"); + # self.assertIn("error_msg", callback_data); + self.assertTrue(callback_data.has_key("error_msg")); + self.failUnlessEqual(callback_data["error_msg"], "Timeout."); + + def testDelayedRequest(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device22"); + myDevice._add_field(name="Temperature", typename="numeric", unit="°C"); + myDevice._set_momentary_timestamp("2013-03-07T16:24:30") + myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}); + + self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); + + dtnow = datetime.datetime.now() + ts_2sec = datetime.timedelta(0,2) + dtnow_plus_2sec = dtnow + ts_2sec + when_flag = dtnow_plus_2sec.replace(microsecond=0).isoformat() + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true' when='""" + when_flag + """'/> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='1'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1' queued='true' /> + </iq> + """) + + time.sleep(2) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <started xmlns='urn:xmpp:iot:sensordata' seqnr='1' /> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'> + <node nodeId='Device22'> + <timestamp value='2013-03-07T16:24:30'> + <numeric name='Temperature' momentary='true' automaticReadout='true' value='23.4' unit='°C'/> + </timestamp> + </node> + </fields> + </message> + """) + + def testDelayedRequestFail(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device22"); + myDevice._add_field(name="Temperature", typename="numeric", unit="°C"); + myDevice._set_momentary_timestamp("2013-03-07T16:24:30") + myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}); + + self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); + + dtnow = datetime.datetime.now() + ts_2sec = datetime.timedelta(0,2) + dtnow_minus_2sec = dtnow - ts_2sec + when_flag = dtnow_minus_2sec.replace(microsecond=0).isoformat() + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true' when='""" + when_flag + """'/> + </iq> + """) + + # Remove the returned datetime to allow predictable test + xml_stanza = self._filtered_stanza_prepare() + error_text = xml_stanza['rejected']['error'] #['text'] + error_text = error_text[:error_text.find(':')] + xml_stanza['rejected']['error'] = error_text + + self._filtered_stanza_check(""" + <iq type='error' + from='device@clayster.com' + to='master@clayster.com/amr' + id='1'> + <rejected xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <error>Invalid datetime in 'when' flag, cannot set a time in the past. Current time</error> + </rejected> + </iq> + """, xml_stanza) + + + def _filtered_stanza_prepare(self, timeout=.5): + sent = self.xmpp.socket.next_sent(timeout) + if sent is None: + self.fail("No stanza was sent.") + + xml = self.parse_xml(sent) + self.fix_namespaces(xml, 'jabber:client') + sent = self.xmpp._build_stanza(xml, 'jabber:client') + return sent + + def _filtered_stanza_check(self, data, filtered, defaults=None, use_values=True, method='exact'): + self.check(filtered, data, + method=method, + defaults=defaults, + use_values=use_values) + + def testRequestFieldFrom(self): + + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device44"); + myDevice._add_field(name='Voltage', typename="numeric", unit="V"); + myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); + myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"}); + myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"}); + + self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); + + print("."), + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='6'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='6' from='2000-01-02T00:00:01'> + <field name='Voltage'/> + </req> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='6'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='6'/> + </iq> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'> + <node nodeId='Device44'> + <timestamp value='2000-02-01T00:01:02'> + <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'> + <node nodeId='Device44'> + <timestamp value='2000-03-01T00:01:02'> + <numeric name='Voltage' invoiced='true' value='230.3' unit='V'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'> + </fields> + </message> + """) + + def testRequestFieldTo(self): + + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device44"); + myDevice._add_field(name='Voltage', typename="numeric", unit="V"); + myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); + myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"}); + myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"}); + + self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); + + print("."), + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='6'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='6' to='2000-02-02T00:00:01'> + <field name='Voltage'/> + </req> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='6'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='6'/> + </iq> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'> + <node nodeId='Device44'> + <timestamp value='2000-01-01T00:01:02'> + <numeric name='Voltage' invoiced='true' value='230.1' unit='V'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'> + <node nodeId='Device44'> + <timestamp value='2000-02-01T00:01:02'> + <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'> + </fields> + </message> + """) + + def testRequestFieldFromTo(self): + + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device44"); + myDevice._add_field(name='Voltage', typename="numeric", unit="V"); + myDevice._add_field_timestamp_data(name="Voltage", value="230.1", timestamp="2000-01-01T00:01:02", flags={"invoiced": "true"}); + myDevice._add_field_timestamp_data(name="Voltage", value="230.2", timestamp="2000-02-01T00:01:02", flags={"invoiced": "true"}); + myDevice._add_field_timestamp_data(name="Voltage", value="230.3", timestamp="2000-03-01T00:01:02", flags={"invoiced": "true"}); + + self.xmpp['xep_0323'].register_node('Device44', myDevice, commTimeout=0.5); + + print("."), + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='6'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='6' from='2000-01-01T00:01:03' to='2000-02-02T00:00:01'> + <field name='Voltage'/> + </req> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='6'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='6'/> + </iq> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6'> + <node nodeId='Device44'> + <timestamp value='2000-02-01T00:01:02'> + <numeric name='Voltage' invoiced='true' value='230.2' unit='V'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.send(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='6' done='true'> + </fields> + </message> + """) + + def testDelayedRequestClient(self): + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0323']) + + results = []; + callback_data = {}; + + def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None): + results.append(result); + if result == "fields": + callback_data["nodeId"] = nodeId; + callback_data["timestamp"] = timestamp; + callback_data["error_msg"] = error_msg; + for f in fields: + callback_data["field_" + f['name']] = f; + + t1= threading.Thread(name="request_data", + target=self.xmpp['xep_0323'].request_data, + kwargs={"from_jid": "tester@localhost", + "to_jid": "you@google.com", + "nodeIds": ['Device33'], + "callback": my_callback}); + t1.start(); + #self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback); + + self.send(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId="Device33"/> + </req> + </iq> + """) + + self.recv(""" + <iq type='result' + from='you@google.com' + to='tester@localhost' + id='1'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1' queued='true'/> + </iq> + """) + + self.recv(""" + <message from='device@clayster.com' + to='master@clayster.com/amr'> + <started xmlns='urn:xmpp:iot:sensordata' seqnr='1' /> + </message> + """) + + self.recv(""" + <message from='you@google.com' + to='tester@localhost'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId='Device33'> + <timestamp value='2000-01-01T00:01:02'> + <numeric name='Voltage' invoiced='true' value='230.4' unit='V'/> + <boolean name='TestBool' value='true'/> + </timestamp> + </node> + </fields> + </message> + """) + + self.recv(""" + <message from='you@google.com' + to='tester@localhost'> + <fields xmlns='urn:xmpp:iot:sensordata' seqnr='1' done='true'/> + </message> + """) + + t1.join(); + time.sleep(.5) + + self.failUnlessEqual(results, ["queued","started","fields","done"]); + # self.assertIn("nodeId", callback_data); + self.assertTrue(callback_data.has_key("nodeId")); + self.failUnlessEqual(callback_data["nodeId"], "Device33"); + # self.assertIn("timestamp", callback_data); + self.assertTrue(callback_data.has_key("timestamp")); + self.failUnlessEqual(callback_data["timestamp"], "2000-01-01T00:01:02"); + # self.assertIn("field_Voltage", callback_data); + self.assertTrue(callback_data.has_key("field_Voltage")); + self.failUnlessEqual(callback_data["field_Voltage"], {"name": "Voltage", "value": "230.4", "typename": "numeric", "unit": "V", "flags": {"invoiced": "true"}}); + # self.assertIn("field_TestBool", callback_data); + self.assertTrue(callback_data.has_key("field_TestBool")); + self.failUnlessEqual(callback_data["field_TestBool"], {"name": "TestBool", "value": "true", "typename": "boolean" }); + + + def testRequestFieldsCancelAPI(self): + + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0323']) + + results = []; + + def my_callback(from_jid, result, nodeId=None, timestamp=None, fields=None, error_msg=None): + results.append(result); + + session = self.xmpp['xep_0323'].request_data(from_jid="tester@localhost", to_jid="you@google.com", nodeIds=['Device33'], callback=my_callback); + + self.send(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <node nodeId="Device33"/> + </req> + </iq> + """) + + self.recv(""" + <iq type='result' + from='you@google.com' + to='tester@localhost' + id='1'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1'/> + </iq> + """) + + self.xmpp['xep_0323'].cancel_request(session=session); + + self.send(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='1'> + <cancel xmlns='urn:xmpp:iot:sensordata' seqnr='1' /> + </iq> + """) + + self.recv(""" + <iq type='result' + from='tester@localhost' + to='you@google.com' + id='1'> + <cancelled xmlns='urn:xmpp:iot:sensordata' seqnr='1' /> + </iq> + """) + + time.sleep(.5) + + self.failUnlessEqual(results, ["accepted","cancelled"]); + + def testDelayedRequestCancel(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0323']) + + myDevice = Device("Device22"); + myDevice._add_field(name="Temperature", typename="numeric", unit="°C"); + myDevice._set_momentary_timestamp("2013-03-07T16:24:30") + myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}); + + self.xmpp['xep_0323'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); + + dtnow = datetime.datetime.now() + ts_2sec = datetime.timedelta(0,2) + dtnow_plus_2sec = dtnow + ts_2sec + when_flag = dtnow_plus_2sec.replace(microsecond=0).isoformat() + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <req xmlns='urn:xmpp:iot:sensordata' seqnr='1' momentary='true' when='""" + when_flag + """'/> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='1'> + <accepted xmlns='urn:xmpp:iot:sensordata' seqnr='1' queued='true' /> + </iq> + """) + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <cancel xmlns='urn:xmpp:iot:sensordata' seqnr='1' /> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='1'> + <cancelled xmlns='urn:xmpp:iot:sensordata' seqnr='1' /> + </iq> + """) + + # Test cancel of non-existing request + self.recv(""" + <iq type='get' + from='tester@localhost' + to='you@google.com' + id='1'> + <cancel xmlns='urn:xmpp:iot:sensordata' seqnr='1' /> + </iq> + """) + + self.send(""" + <iq type='error' + from='you@google.com' + to='tester@localhost' + id='1'> + <rejected xmlns='urn:xmpp:iot:sensordata' seqnr='1'> + <error>Cancel request received, no matching request is active.</error> + </rejected> + </iq> + """) + + time.sleep(2) + + # Ensure we don't get anything after cancellation + self.send(None) + + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamSensorData) + diff --git a/tests/test_stream_xep_0325.py b/tests/test_stream_xep_0325.py new file mode 100644 index 00000000..4b3250fc --- /dev/null +++ b/tests/test_stream_xep_0325.py @@ -0,0 +1,365 @@ +# -*- coding: utf-8 -*- +""" + SleekXMPP: The Sleek XMPP Library + Implementation of xeps for Internet of Things + http://wiki.xmpp.org/web/Tech_pages/IoT_systems + Copyright (C) 2013 Sustainable Innovation, Joachim.lindborg@sust.se, bjorn.westrom@consoden.se + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import datetime +import time +import threading + +from sleekxmpp.test import * +from sleekxmpp.xmlstream import ElementBase +from sleekxmpp.plugins.xep_0325.device import Device + + +class TestStreamControl(SleekTest): + + """ + Test using the XEP-0325 plugin. + """ + def setUp(self): + pass + + def _time_now(self): + return datetime.datetime.now().replace(microsecond=0).isoformat(); + + def tearDown(self): + self.stream_close() + + def testRequestSetOk(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0325']) + + myDevice = Device("Device22"); + myDevice._add_control_field(name="Temperature", typename="int", value="15"); + + self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); + + self.recv(""" + <iq type='set' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <set xmlns='urn:xmpp:iot:control'> + <int name="Temperature" value="17"/> + </set> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='1'> + <setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" /> + </iq> + """) + + self.assertEqual(myDevice._get_field_value("Temperature"), "17"); + + def testRequestSetMulti(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0325']) + + myDevice = Device("Device22"); + myDevice._add_control_field(name="Temperature", typename="int", value="15"); + myDevice._add_control_field(name="Startup", typename="date", value="2013-01-03"); + + myDevice2 = Device("Device23"); + myDevice2._add_control_field(name="Temperature", typename="int", value="19"); + myDevice2._add_control_field(name="Startup", typename="date", value="2013-01-09"); + + self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); + self.xmpp['xep_0325'].register_node(nodeId="Device23", device=myDevice2, commTimeout=0.5); + + self.recv(""" + <iq type='set' + from='master@clayster.com/amr' + to='device@clayster.com' + id='1'> + <set xmlns='urn:xmpp:iot:control'> + <node nodeId='Device22' /> + <int name="Temperature" value="17"/> + </set> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='1'> + <setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" /> + </iq> + """) + + self.assertEqual(myDevice._get_field_value("Temperature"), "17"); + self.assertEqual(myDevice2._get_field_value("Temperature"), "19"); + + self.recv(""" + <iq type='set' + from='master@clayster.com/amr' + to='device@clayster.com' + id='2'> + <set xmlns='urn:xmpp:iot:control'> + <node nodeId='Device23' /> + <node nodeId='Device22' /> + <date name="Startup" value="2013-02-01"/> + <int name="Temperature" value="20"/> + </set> + </iq> + """) + + self.send(""" + <iq type='result' + from='device@clayster.com' + to='master@clayster.com/amr' + id='2'> + <setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" /> + </iq> + """) + + self.assertEqual(myDevice._get_field_value("Temperature"), "20"); + self.assertEqual(myDevice2._get_field_value("Temperature"), "20"); + self.assertEqual(myDevice._get_field_value("Startup"), "2013-02-01"); + self.assertEqual(myDevice2._get_field_value("Startup"), "2013-02-01"); + + def testRequestSetFail(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0325']) + + myDevice = Device("Device23"); + myDevice._add_control_field(name="Temperature", typename="int", value="15"); + + self.xmpp['xep_0325'].register_node(nodeId="Device23", device=myDevice, commTimeout=0.5); + + self.recv(""" + <iq type='set' + from='master@clayster.com/amr' + to='device@clayster.com' + id='9'> + <set xmlns='urn:xmpp:iot:control'> + <int name="Voltage" value="17"/> + </set> + </iq> + """) + + self.send(""" + <iq type='error' + from='device@clayster.com' + to='master@clayster.com/amr' + id='9'> + <setResponse xmlns='urn:xmpp:iot:control' responseCode='NotFound'> + <parameter name='Voltage' /> + <error var='Output'>Invalid field Voltage</error> + </setResponse> + </iq> + """) + + self.assertEqual(myDevice._get_field_value("Temperature"), "15"); + self.assertFalse(myDevice.has_control_field("Voltage", "int")); + + def testDirectSetOk(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0325']) + + myDevice = Device("Device22"); + myDevice._add_control_field(name="Temperature", typename="int", value="15"); + + self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); + + self.recv(""" + <message + from='master@clayster.com/amr' + to='device@clayster.com'> + <set xmlns='urn:xmpp:iot:control'> + <int name="Temperature" value="17"/> + </set> + </message> + """) + + time.sleep(.5) + + self.assertEqual(myDevice._get_field_value("Temperature"), "17"); + + def testDirectSetFail(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0325']) + + myDevice = Device("Device22"); + myDevice._add_control_field(name="Temperature", typename="int", value="15"); + + self.xmpp['xep_0325'].register_node(nodeId="Device22", device=myDevice, commTimeout=0.5); + + self.recv(""" + <message + from='master@clayster.com/amr' + to='device@clayster.com'> + <set xmlns='urn:xmpp:iot:control'> + <int name="Voltage" value="17"/> + </set> + </message> + """) + + time.sleep(.5) + + self.assertEqual(myDevice._get_field_value("Temperature"), "15"); + self.assertFalse(myDevice.has_control_field("Voltage", "int")); + + + def testRequestSetOkAPI(self): + + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0325']) + + results = []; + + def my_callback(from_jid, result, nodeIds=None, fields=None, error_msg=None): + results.append(result); + + fields = [] + fields.append(("Temperature", "double", "20.5")) + fields.append(("TemperatureAlarmSetting", "string", "High")) + + self.xmpp['xep_0325'].set_request(from_jid="tester@localhost", to_jid="you@google.com", fields=fields, nodeIds=['Device33', 'Device22'], callback=my_callback); + + self.send(""" + <iq type='set' + from='tester@localhost' + to='you@google.com' + id='1'> + <set xmlns='urn:xmpp:iot:control'> + <node nodeId='Device33' /> + <node nodeId='Device22' /> + <double name="Temperature" value="20.5" /> + <string name="TemperatureAlarmSetting" value="High" /> + </set> + </iq> + """) + + self.recv(""" + <iq type='result' + from='you@google.com' + to='tester@localhost' + id='1'> + <setResponse xmlns='urn:xmpp:iot:control' responseCode="OK" /> + </iq> + """) + + time.sleep(.5) + + self.assertEqual(results, ["OK"]); + + def testRequestSetErrorAPI(self): + + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0325']) + + results = []; + + def my_callback(from_jid, result, nodeIds=None, fields=None, error_msg=None): + results.append(result); + + fields = [] + fields.append(("Temperature", "double", "20.5")) + fields.append(("TemperatureAlarmSetting", "string", "High")) + + self.xmpp['xep_0325'].set_request(from_jid="tester@localhost", to_jid="you@google.com", fields=fields, nodeIds=['Device33', 'Device22'], callback=my_callback); + + self.send(""" + <iq type='set' + from='tester@localhost' + to='you@google.com' + id='1'> + <set xmlns='urn:xmpp:iot:control'> + <node nodeId='Device33' /> + <node nodeId='Device22' /> + <double name="Temperature" value="20.5" /> + <string name="TemperatureAlarmSetting" value="High" /> + </set> + </iq> + """) + + self.recv(""" + <iq type='error' + from='you@google.com' + to='tester@localhost' + id='1'> + <setResponse xmlns='urn:xmpp:iot:control' responseCode="OtherError" > + <error var='Temperature'>Sensor error</error> + </setResponse> + </iq> + """) + + time.sleep(.5) + + self.assertEqual(results, ["OtherError"]); + + def testServiceDiscoveryClient(self): + self.stream_start(mode='client', + plugins=['xep_0030', + 'xep_0325']); + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='tester@localhost' + id='disco1'> + <query xmlns='http://jabber.org/protocol/disco#info'/> + </iq> + """) + + self.send(""" + <iq type='result' + to='master@clayster.com/amr' + id='disco1'> + <query xmlns='http://jabber.org/protocol/disco#info'> + <identity category='client' type='bot'/> + <feature var='urn:xmpp:iot:control'/> + </query> + </iq> + """) + + def testServiceDiscoveryComponent(self): + self.stream_start(mode='component', + plugins=['xep_0030', + 'xep_0325']); + + self.recv(""" + <iq type='get' + from='master@clayster.com/amr' + to='tester@localhost' + id='disco1'> + <query xmlns='http://jabber.org/protocol/disco#info'/> + </iq> + """) + + self.send(""" + <iq type='result' + from='tester@localhost' + to='master@clayster.com/amr' + id='disco1'> + <query xmlns='http://jabber.org/protocol/disco#info'> + <identity category='component' type='generic'/> + <feature var='urn:xmpp:iot:control'/> + </query> + </iq> + """) + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamControl) + |