Commit 82951ddd authored by Michael Weinrich's avatar Michael Weinrich
Browse files

documentation update

parent 9d298418
/* Body color */
body { background: #b0d0c8; color: #000000; }
/* Body color */
body { background: #ffffff; color: #000000; }
/* Tables */
table.summary, table.details, table.index
{ background: #c8e8e0; color: #000000; }
tr.summary { background: #dcfcf4; color: #000000;
text-align: left; font-size: 120%; }
tr.details, tr.index
{ background: #dcfcf4; color: #000000;
text-align: center; font-size: 120%; }
tr.group { background: #bae0d8; color: #000000;
{ background: #e8f0f8; color: #000000; }
tr.summary, tr.details, tr.index
{ background: #70b0f0; color: #000000;
text-align: left; font-size: 120%; }
tr.group { background: #c0e0f8; color: #000000;
text-align: left; font-size: 120%;
font-style: italic; }
/* Documentation page titles */
h2.module { margin-top: 0.2em; }
h2.class { margin-top: 0.2em ; }
h2.class { margin-top: 0.2em; }
/* Headings */
h1.heading { font-size: +140%; font-style: italic;
font-weight: bold; color: #006020; }
font-weight: bold; }
h2.heading { font-size: +125%; font-style: italic;
font-weight: bold; color: #006020; }
font-weight: bold; }
h3.heading { font-size: +110%; font-style: italic;
font-weight: normal; color: #006020; }
font-weight: normal; }
/* Base tree */
pre.base-tree { font-size: 80%; margin: 0; }
/* Details Sections */
table.func-details { background: #c8e8e0; color: #000000;
table.func-details { background: #e8f0f8; color: #000000;
border: 2px groove #c0d0d0;
padding: 0 1em 0 1em; margin: 0.4em 0 0 0; }
h3.func-detail { background: transparent; color: #000000;
margin: 0 0 1em 0; }
table.var-details { background: #c8e8e0; color: #000000;
border: 2px groove #ffffff;
table.var-details { background: #e8f0f8; color: #000000;
border: 2px groove #c0d0d0;
padding: 0 1em 0 1em; margin: 0.4em 0 0 0; }
h3.var-details { background: transparent; color: #000000;
margin: 0 0 1em 0; }
......@@ -44,60 +43,58 @@ h3.var-details { background: transparent; color: #000000;
/* Function signatures */
.sig { background: transparent; color: #000000;
font-weight: bold; }
.sig-name { background: transparent; color: #008060; }
.sig-name { background: transparent; color: #006080; }
.sig-arg, .sig-kwarg, .sig-vararg
{ background: transparent; color: #006080; }
.sig-default { background: transparent; color: #600020; }
{ background: transparent; color: #008060; }
.sig-default { background: transparent; color: #602000; }
.summary-sig { background: transparent; color: #000000; }
.summary-sig-name { background: transparent; color: #084028; }
.summary-sig-name { background: transparent; color: #204080; }
.summary-sig-arg, .summary-sig-kwarg, .summary-sig-vararg
{ background: transparent; color: #006080; }
{ background: transparent; color: #008060; }
/* Doctest blocks */
.py-src { background: transparent; color: #000000; }
.py-prompt { background: transparent; color: #007060;
.py-prompt { background: transparent; color: #005050;
font-weight: bold;}
.py-string { background: transparent; color: #005070; }
.py-comment { background: transparent; color: #008040; }
.py-keyword { background: transparent; color: #800000; }
.py-output { background: transparent; color: #484848; }
pre.doctestblock { background: #c8e8e0; color: #000000;
.py-string { background: transparent; color: #006030; }
.py-comment { background: transparent; color: #003060; }
.py-keyword { background: transparent; color: #600000; }
.py-output { background: transparent; color: #404040; }
pre.doctestblock { background: #f4faff; color: #000000;
padding: .5em; margin: 1em;
border: 1px solid #709088; }
border: 1px solid #708890; }
table pre.doctestblock
{ background: #c0e0d8; color: #000000;
{ background: #dce4ec; color: #000000;
padding: .5em; margin: 1em;
border: 1px solid #709088; }
border: 1px solid #708890; }
/* Variable values */
pre.variable { background: #c0e0d8; color: #000000;
pre.variable { background: #dce4ec; color: #000000;
padding: .5em; margin: 0;
border: 1px solid #709088; }
.variable-linewrap { background: transparent; color: #700050; }
.variable-ellipsis { background: transparent; color: #700050; }
.variable-quote { background: transparent; color: #700050; }
border: 1px solid #708890; }
.variable-linewrap { background: transparent; color: #604000; }
.variable-ellipsis { background: transparent; color: #604000; }
.variable-quote { background: transparent; color: #604000; }
.re { background: transparent; color: #000000; }
.re-char { background: transparent; color: #005070; }
.re-op { background: transparent; color: #800000; }
.re-group { background: transparent; color: #008040; }
.re-ref { background: transparent; color: #484848; }
.re-char { background: transparent; color: #006030; }
.re-op { background: transparent; color: #600000; }
.re-group { background: transparent; color: #003060; }
.re-ref { background: transparent; color: #404040; }
/* Navigation bar */
table.navbar { background: #688880; color: #d8f0f0;
table.navbar { background: #a0c0ff; color: #0000ff;
border: 2px groove #c0d0d0; }
th.navbar { background: #688880; color: #d8f0f0;
font-weight: normal; }
th.navselect { background: #88a8a0; color: #000000;
font-weight: normal; }
th.navbar { background: #a0c0ff; color: #0000ff; }
th.navselect { background: #70b0ff; color: #000000; }
.nomargin { margin: 0; }
/* Links */
a:link { background: transparent; color: #106040; }
a:visited { background: transparent; color: #084028; }
a.navbar:link { background: transparent; color: #d8f0f0;
a:link { background: transparent; color: #0000ff; }
a:visited { background: transparent; color: #204080; }
a.navbar:link { background: transparent; color: #0000ff;
text-decoration: none; }
a.navbar:visited { background: transparent; color: #204080;
text-decoration: none; }
a.navbar:visited { background: transparent; color: #d8f0f0;
text-decoration: none; }
/* Lists */
ul { margin-top: 0; }
......@@ -25,17 +25,10 @@ class UPnTGUI:
self.window.destroy()
def destroy(self, widget, data=None):
print 'Ausmachen...'
reactor.stop()
#print 'Warten auf die Langsamen...'
#time.sleep(5)
#kill reactor if some deferreds are not responding
print 'Lange genug gewartet!'
#gtk.main_quit()
reactor.crash()
print 'Crashed!'
del self.UPnT
print 'Fertsch!'
def updateUpnpDevices(self, ip):
model = self.upnp_devices_treeview.get_model()
......@@ -56,11 +49,11 @@ class UPnTGUI:
while result:
result = model.remove(children_iter)
host = self.UPnT.known_hosts[ip]
host = self.UPnT.knownHosts[ip]
for uuid, device in host._devices.items():
device_iter = model.append(current_parent, [self.device_icon, device.deviceType])
device_iter = model.append(current_parent, [self.device_icon, device.deviceType + ':' + device.deviceVersion])
for uuid, service in device._services.items():
model.append(device_iter, [self.service_icon, service.serviceType])
model.append(device_iter, [self.service_icon, service.serviceType + ':' + service.serviceVersion])
def runControlTest(self, widget, data=None):
print 'muh'
......
__version_info__ = (0,1,0)
__version_info__ = (0,5,0)
__version__ = '%d.%d.%d' % (__version_info__[0],__version_info__[1],__version_info__[2],)
......@@ -23,10 +23,10 @@ class UPnT(log.Loggable):
log.init()
self.known_hosts = {}
self.knownHosts = {}
self.runningDeferreds = []
self.shutdown_signal = None
self._hosts_to_remove = []
self.shutdownSignal = None
self._hostsToRemove = []
self._config = config
self._coherence = Coherence(self._config);
......@@ -52,12 +52,12 @@ class UPnT(log.Loggable):
validation method.
"""
if not self.known_hosts.has_key(ip):
self.known_hosts[ip] = Host(ip, self._config, self._coherence)
if not self.knownHosts.has_key(ip):
self.knownHosts[ip] = Host(ip, self._config, self._coherence)
self.info('Host added', ip)
self.debug('Calling dispatchDiscoveryPacketData')
#try:
self.known_hosts[ip].dispatchDiscoveryPacketData(data)
self.knownHosts[ip].dispatchDiscoveryPacketData(data)
#except Exception, error:
# self.warning(error)
......@@ -69,11 +69,11 @@ class UPnT(log.Loggable):
Remove host from the known hosts list
"""
self.debug('hostRemoved(%r)' % ip)
self._hosts_to_remove.append(ip)
self.info('hostRemoved(%r)' % ip)
self._hostsToRemove.append(ip)
def removeHosts(self):
self.known_hosts = None
self.knownHosts = None
def showInfoMessage(self, message):
print message
......
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# Copyright 2007, Michael Weinrich <testsuite@michael-weinrich.de>
from coherence import log
import louie
from twisted.internet import reactor
class ServerControl(log.Loggable):
logCategory = 'UPnT_ServerControl'
class ClientControl(log.Loggable):
logCategory = 'UPnT_ServerControl'
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# Copyright 2007, Michael Weinrich <testsuite@michael-weinrich.de>
from coherence import log
import louie
from twisted.internet import reactor
from lxml import etree
class ServerControl(log.Loggable):
"""
Implements methods to invoke all actions that a service offers and to check
the responses.
"""
logCategory = 'UPnT_ServerControl'
def __init__(self, config, service_type, service_version, uuid):
self._testcasesXml = None
self._uuidXml = None
self._ready = True
if service_type != "" and \
service_version != "" and \
uuid != "":
test_file_dir = config.get('test_file_dir', None)
if test_file_dir is not None:
try:
self._testcasesXml = \
etree.parse(test_file_dir + 'common/' +
service_type + service_version + '.SyntaxTests.xml')
try:
self._uuidXml = \
etree.parse(test_file_dir + 'uuid/' +
service_type + service_version + '.' + uuid + '.SyntaxTests.xml')
except IOError, error:
self.info('Specific file for %s:%s on uuid:%s not found' % (service_type, service_version, uuid))
self.info(error)
self._ready = False
except IOError, error:
self.info('Common file for %s:%s not found' % (service_type, service_version))
self.info(error)
self._ready = False
if not self._ready:
return
self._testcases = {}
self.parseTestcases()
def parseTestcases(self):
"""
Parse the file with common information about the testcases. Afterwards
filter that input using the UUID specific information.
"""
self.debug('parseTestcases')
# get all testcases from the common file
testcases = self._testcasesXml.xpath('/ServiceControlSyntaxTestCases/TestCaseList/TestCase')
for testcase in testcases:
id = testcase.findtext('Id')
self._testcases[id] = {}
self._testcases[id]['Category'] = testcase.findtext('Category')
self._testcases[id]['ActionName'] = testcase.findtext('ActionName')
in_args = testcase.findall('./InArgs/*')
if len(in_args) > 0:
self._testcases[id]['InArgs'] = {}
for argument in in_args:
self._testcases[id]['InArgs'][argument.tag] = argument.text
self._testcases[id]['ExpectedReturnCode'] = testcase.findtext('ExpectedReturnCode')
# read the device specific file and make according changes
specific_changes = self._uuidXml.xpath('/ServiceControlSyntaxTestCases/TestCaseList/TestCase')
for change in specific_changes:
id = change.findtext('Id')
if self._testcases.has_key(id):
category = change.findtext('Category')
action_name = change.findtext('ActionName')
in_args = change.findall('./InArgs/*')
expected_return_code = change.findtext('ExpectedReturnCode')
bool1 = self._testcases[id].has_key('InArgs') and len(in_args) == len(self._testcases[id]['InArgs'])
bool2 = not self._testcases[id].has_key('InArgs') and len(in_args) == 0
if category != "" and action_name != "" and expected_return_code != "" and \
((bool1 and not bool2) or (not bool1 and bool2)):
self._testcases[id]['Category'] = category
self._testcases[id]['ActionName'] = action_name
self._testcases[id]['ExpectedReturnCode'] = expected_return_code
if bool1:
for argument in in_args:
self._testcases[id]['InArgs'][argument.tag] = argument.text
def startTesting(self, loop=False):
"""
Start cycling through all listed action tests.
When using loop=True the cycle starts again at the beginning of the
method list.
"""
if not self._ready:
return
def stopTesting(self):
"""
If some request is still running, wait for completion
"""
pass
class ClientControl(log.Loggable):
"""
Device to check is a control point so we can only perform passive checks on
the messages sent by the device.
"""
logCategory = 'UPnT_ClientControl'
......@@ -22,9 +22,8 @@ class ServerEventMessageChecks(log.Loggable):
logCategory = 'UPnT_ClientEventMessageChecks'
def __init__(self, service, config):
self.notify_seq = {}
self.notifySeq = {}
self._service = service
self._xmlschema = None
......@@ -34,6 +33,7 @@ class ServerEventMessageChecks(log.Loggable):
self._xmlschema = etree.XMLSchema(xmlschema_doc)
self._host = ''
self._renew_func = None
self.isHostAvailable()
def isHostAvailable(self):
......@@ -44,9 +44,18 @@ class ServerEventMessageChecks(log.Loggable):
louie.connect(self.checkEventMessage, 'UPnT.event.message_received', louie.Any, weak=False)
self._control_point = ControlPoint(self._service.parentDevice.host._coherence)
self._event_Server = event.EventServer(self._control_point)
event.subscribe(self._service)
self.renewSubscription()
self.debug('ClientEventMessageCheck initialized for service %r' % str(self._service))
def renewSubscription(self):
event.subscribe(self._service)
self._renew_func = reactor.callLater(290, self.renewSubscription)
def cancelSubscription(self):
if self._renew_func.active():
self._renew_func.cancel()
event.unsubscribe(self._service)
def checkEventMessage(self, command, header, data):
#self.debug('checkEventMessage (%r)' % packet_data)
self.debug('checkEventMessage')
......@@ -67,22 +76,22 @@ class ServerEventMessageChecks(log.Loggable):
service_ident = service_host + service_url
# chekc for dict index
if not self.notify_seq.has_key(service_ident):
self.notify_seq[service_ident] = {}
if not self.notify_seq[service_ident].has_key(service_sid):
self.notify_seq[service_ident][service_sid] = 0
if not self.notifySeq.has_key(service_ident):
self.notifySeq[service_ident] = {}
if not self.notifySeq[service_ident].has_key(service_sid):
self.notifySeq[service_ident][service_sid] = 0
# calc new value, wrap to one if value too big (UDA S.67)
self.notify_seq[service_ident][service_sid] += 1
if self.notify_seq[service_ident][service_sid] > 429496729:
self.notify_seq[service_ident][service_sid] = 1
self.notifySeq[service_ident][service_sid] += 1
if self.notifySeq[service_ident][service_sid] > 429496729:
self.notifySeq[service_ident][service_sid] = 1
def removeEventSeq(self, service):
"""
Remove the sequence number from the dictionary so that it starts again
at 0 when some device subscribes the next time
"""
self.debug('removeEventSeq (%r)' % self.notify_seq)
self.debug('removeEventSeq (%r)' % self.notifySeq)
#if service.scpdUrl == self._service.scpdUrl:
#del self.notify_seq[self._host + self._service.event]
......@@ -199,11 +208,11 @@ class ServerEventMessageChecks(log.Loggable):
else:
self.calcNextEventSeq(header['host'], command['path'], header['sid'])
service_ident = header['host'] + command['path']
if header['seq'] != self.notify_seq[service_ident][header['sid']]:
if header['seq'] != self.notifySeq[service_ident][header['sid']]:
louie.send(
'UPnT.event.notify_incorrect',
None,
"Notify: SEQ header has to be incremented starting with 0 (%r, should be %r)" % (header['seq'], self.notify_seq[service_ident]),
"Notify: SEQ header has to be incremented starting with 0 (%r, should be %r)" % (header['seq'], self.notifySeq[service_ident]),
[command['path'], header['host']]
)
......@@ -222,7 +231,8 @@ class ServerEventMessageChecks(log.Loggable):
louie.send('UPnT.infoMessage', None, 'No XML Schema to compare description against!')
elif self._xmlschema.validate(doc) == 1:
louie.send('UPnT.infoMessage', None, 'Body of notify message valid! (%s)' % service_info)
#louie.send('UPnT.infoMessage', None, 'Body of notify message valid! (%s)' % service_info)
pass
else:
error = self._xmlschema.error_log.last_error
......
......@@ -30,7 +30,7 @@ class Host(log.Loggable):
self._ip = ip
self._devices = {}
self._services = {}
self._create_dev_tree_func = None
self._createDevTreeFunc = None
self._coherence = coherence
def getIp(self):
......@@ -120,8 +120,8 @@ class Host(log.Loggable):
else:
upnp_type = upnp_type_split[1]
self.info('Processing USN %s from %s' % (headers['usn'], self.ip))
self.info('Processing UUID %s from %s' % (uuid, self.ip))
self.debug('Processing USN %s from %s' % (headers['usn'], self.ip))
self.debug('Processing UUID %s from %s' % (uuid, self.ip))
if headers.has_key('nts'):
action = headers['nts']
......@@ -140,15 +140,15 @@ class Host(log.Loggable):
else:
if self._devices.has_key(uuid):
self._devices[uuid].update(headers)
self._devices[uuid].update(headers, self._devices)
else:
self._devices[uuid] = ServerDevice(headers, self._config)
self._devices[uuid] = ServerDevice(headers, self._devices, self._config)
self._devices[uuid].host = self
if self._create_dev_tree_func is None:
self._create_dev_tree_func = reactor.callLater(5, self.createAndCheckDeviceTree)
if self._createDevTreeFunc is None:
self._createDevTreeFunc = reactor.callLater(3, self.createAndCheckDeviceTree)
else:
self._create_dev_tree_func.reset(5)
self._createDevTreeFunc.reset(3)
elif action == 'ssdp:byebye':
......@@ -163,17 +163,33 @@ class Host(log.Loggable):
else:
louie.send('UPnT.infoMessage', None, 'Wrong action. Something went really wrong here.')
louie.send('UPnT.ending_deferred', None, d)
def showDeviceTree(self, result, device, level=1):
if level == 1:
print
if level > 1:
print (level-1)*' ' + '+-D: ' + device.deviceType + ':' + device.deviceVersion
else:
print level*' ' + 'D: ' + device.deviceType + ':' + device.deviceVersion
#self.debug(device.services)
for _, service in device.services.items():
print level*' ' + '+-S: ' + service.serviceType + ':' + service.serviceVersion
#self.debug(device.subDevices)
for _, sub_device in device.subDevices.items():
self.showDeviceTree(result, sub_device, level + 1)
def createAndCheckDeviceTree(self):
"""Tell the root device to create its subdevice tree"""
self._create_dev_tree_func = None
self._createDevTreeFunc = None
for uuid, device in self._devices.items():
if device.isRootDevice():
device.checkDescriptions(self._devices, self._services)
d = device.checkDescriptions(self._devices, self._services)
d.addCallback(self.showDeviceTree, device)
def checkNotificationPacket(self, header, payload):
"""checks common content of a notification packet for errors and returns
......
......@@ -23,18 +23,18 @@ class ServerDevice(log.Loggable):
logCategory = 'UPnT_ServerDevice'
def __init__(self, headers, config):
def __init__(self, headers, all_devices, config):
self._uuid = ''
self._parent = None
self._host = None
self._root_device = False
self._rootDevice = False
self._schema = ''
self._device_type = ''
self._device_version = ''
self._desc_location = ''
self._url_base = ''
self._sub_devices = {}
self._deviceType = ''
self._deviceVersion = ''
self._descLocation = ''
self._urlBase = ''
self._subDevices = {}
self._services = {}
self._xmlschema = None
......@@ -43,22 +43,26 @@ class ServerDevice(log.Loggable):
xmlschema_doc = etree.parse(schema_file_dir + 'device.xsd')
self._xmlschema = etree.XMLSchema(xmlschema_doc)
_expiration_notification_func = None
self._expirationNotificationFunc = None
# parse data
self.update(headers, True)
self.update(headers, all_devices, True)
def __str__(self):
return self.UUID + '::' + self.deviceType
return self._uuid + '::' + self._deviceType + ':' + self._deviceVersion
def getLocation(self):
return self._desc_location
return self._descLocation
location = property(getLocation)
def getDeviceType(self):
return self._device_type + ':' + self._device_version
return self._deviceType
deviceType = property(getDeviceType)
def getDeviceVersion(self):
return self._deviceVersion
deviceVersion = property(getDeviceVersion)
def getUuid(self):
return self._uuid
UUID = property(getUuid)
......@@ -78,12 +82,20 @@ class ServerDevice(log.Loggable):
"""Set host"""
self._host = host