709 lines
31 KiB
Python
Executable file
709 lines
31 KiB
Python
Executable file
#!/usr/bin/python
|
|
#
|
|
# Copyright (C) 2010 Intel Corporation
|
|
#
|
|
# This library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2.1 of the License, or (at your option) version 3.
|
|
#
|
|
# This library is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this library; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
# 02110-1301 USA
|
|
|
|
'''
|
|
Automatically trying different configurations for a phone to sync with
|
|
SyncEvolution.
|
|
'''
|
|
import sys, optparse, os, time, tempfile
|
|
import shutil
|
|
import ConfigParser
|
|
import glob
|
|
|
|
# source names as commonly used in SyncEvolution
|
|
allSources = ['addressbook', 'calendar', 'todo', 'memo', 'calendar+todo']
|
|
# valid SyncMLVersion values
|
|
allVersions = ['1.2', '1.1', '1.0']
|
|
|
|
########################### cmdline options ##########################################
|
|
parser = optparse.OptionParser()
|
|
parser.add_option("-b", "--bt-address", action = "store", type = "string",
|
|
dest = "btaddr", help = "The Bluetooth mac address for the testing phone", default = "")
|
|
parser.add_option ("-p", "--protocol-version", action = "store", type = "string",
|
|
dest="version", help = "The SyncML protocal version for testing, can be one of " +
|
|
"|".join(allVersions) + ", by default it will try all versions one by one",
|
|
default = "")
|
|
parser.add_option ("-s", "--source", action = "store", type = "string", dest=
|
|
"source", help = "The local database for testing, can be one of " + "|".join(allSources),
|
|
default = "")
|
|
parser.add_option ("-u", "--uri", action = "store", type = "string", dest =
|
|
"uri", help = "The URI for testing the selected source, invalid when no specific source is selected via --source", default = "")
|
|
parser.add_option ("-t", "--type", action = "store", type = "string", dest =
|
|
"type", help = "The content type for testing the selected source, invalid when no specific source is selected via --source"
|
|
,default = "")
|
|
parser.add_option ("-i", "--identifier", action = "store", type="string",
|
|
dest = "identifier", help = "The identifier used when contacting the phone, can be arbitray string. By default it will try 'PC Suite','Nokia PC Suite' and empty string",
|
|
default = "")
|
|
parser.add_option ("", "--without-ctcap", action = "store_true", default =False,
|
|
dest = "ctcap", help = "Testing without sending CTCap information")
|
|
parser.add_option ("-v", "--verbose", action = "count",
|
|
dest = "verbose", help = "Increase amount of output")
|
|
parser.add_option ("-a", "--advanced", action = "store_true", default = False,
|
|
dest = "advanced", help = "More extensive test with sending/receving data, WARNING: will destroy your data on the tested phone")
|
|
parser.add_option("-c", "--create-config", action ="store", type = "string",
|
|
dest = "create", help = "If set, a configuration file with the name will be created based on the testing result",
|
|
default ="")
|
|
(options, args) = parser.parse_args()
|
|
|
|
####################semantic check for cmdline options #######################################
|
|
if not options.btaddr:
|
|
parser.error ("Please provide the Bluetooth MAC address for the phone with -b/--bt-address.")
|
|
if options.version and options.version not in allVersions:
|
|
parser.error("Option -p/--protocol-version can only be one of " + "|".join(allVersions) + ".")
|
|
if options.source and options.source not in allSources:
|
|
parser.error("Option -s/--source can only be one of " + "|".join(allSources) + ".")
|
|
if options.uri and not options.source:
|
|
parser.error ("Option -u/--uri only works in combination with -s/--source.")
|
|
if options.type and not options.source:
|
|
parser.error ("Option -t/--type only works in combination with -s/--source.")
|
|
|
|
#######################some global parameters ######################
|
|
syncevoCmd = 'syncevolution'
|
|
configName = 'test-phone' # inside temporary testConfig dir
|
|
# real paths set in main() inside temporary directory
|
|
testFolder = '/dev/null/data'
|
|
testResult = '/dev/null/cache'
|
|
testConfig = '/dev/null/config'
|
|
|
|
|
|
#################### Configuration Parameter #######################
|
|
class ConfigurationParameter:
|
|
def __init__ (self, version, source, uri, type, ctcap, identifier):
|
|
self.version = version
|
|
self.source = source
|
|
self.uri = uri
|
|
self.type = type
|
|
self.ctcap = ctcap
|
|
self.identifier = identifier
|
|
|
|
def printMe(self):
|
|
print "Test parameter: "
|
|
print "With CTCap: %s" %(self.ctcap,)
|
|
print "Identifier: %s" %(self.identifier,)
|
|
print "SyncML version: %s" %(self.version,)
|
|
print "Sync Source: %s" %(self.source,)
|
|
print "URI: %s" %(self.uri,)
|
|
print "Content Type: %s" %(self.type,)
|
|
|
|
def __str__(self):
|
|
res = []
|
|
if self.ctcap:
|
|
res.append("with CTCap")
|
|
else:
|
|
res.append("without CTCap")
|
|
res.append(self.identifier)
|
|
res.append(self.version)
|
|
res.append("%s = %s + %s" % (self.source, self.uri, self.type))
|
|
return ", ".join(res)
|
|
|
|
def equalWith(self, config):
|
|
return (config and \
|
|
self.ctcap == config.ctcap and \
|
|
self.identifier == config.identifier and \
|
|
self.version == config.version and \
|
|
self.uri == config.uri and \
|
|
self.type == config.type)
|
|
|
|
###################### utility functions ####################
|
|
def clearLocalSyncData(sources):
|
|
for source in sources:
|
|
dirname = "%s/%s" % (testFolder, source)
|
|
rm_r(dirname)
|
|
os.makedirs(dirname)
|
|
|
|
def insertLocalSyncData(sources, type):
|
|
for source in sources:
|
|
testcase = getTestCase (source, type)
|
|
cmd = "echo \"%s\" > %s/%s/0 ;echo 'insertLocalSyncData'" % (testcase, testFolder, source)
|
|
runCommand(cmd)
|
|
|
|
def getTestCase(source, type):
|
|
if (source == 'addressbook' and (type == 'text/vcard:3.0' or type == 'text/vcard')):
|
|
return "BEGIN:VCARD\n"\
|
|
+"VERSION:3.0\n"\
|
|
+"TITLE:tester\n"\
|
|
+"FN:John Doe\n"\
|
|
+"N:Doe;John;;;\n"\
|
|
+"TEL;TYPE=WORK;TYPE=VOICE:business 1\n"\
|
|
+"X-EVOLUTION-FILE-AS:Doe\\, John\n"\
|
|
+"X-MOZILLA-HTML:FALSE\n"\
|
|
+"NOTE:test-phone\n"\
|
|
+"END:VCARD\n"
|
|
if (source == 'addressbook' and (type == 'text/x-vcard:2.1' or type == 'text/x-vcard')):
|
|
return "BEGIN:VCARD\n"\
|
|
+"VERSION:2.1\n"\
|
|
+"TITLE:tester\n"\
|
|
+"FN:John Doe\n"\
|
|
+"N:Doe;John;;;\n"\
|
|
+"TEL;TYPE=WORK;TYPE=VOICE:business 1\n"\
|
|
+"X-MOZILLA-HTML:FALSE\n"\
|
|
+"NOTE:REVISION\n"\
|
|
+"END:VCARD\n"
|
|
if (source == 'calendar' and (type == 'text/calendar:2.0' or type == 'text/calendar')):
|
|
return "BEGIN:VCALENDAR\n"\
|
|
+"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"\
|
|
+"VERSION:2.0\n"\
|
|
+"METHOD:PUBLISH\n"\
|
|
+"BEGIN:VEVENT\n"\
|
|
+"SUMMARY:phone meeting\n"\
|
|
+"DTEND:20060406T163000Z\n"\
|
|
+"DTSTART:20060406T160000Z\n"\
|
|
+"DTSTAMP:20060406T211449Z\n"\
|
|
+"LAST-MODIFIED:20060409T213201\n"\
|
|
+"CREATED:20060409T213201\n"\
|
|
+"LOCATION:my office\n"\
|
|
+"DESCRIPTION:let's talkREVISION\n"\
|
|
+"END:VEVENT\n"\
|
|
+"END:VCALENDAR\n"
|
|
if (source == 'calendar' and (type =='text/x-vcalendar:1.0' or type == 'text/x-vcalendar')):
|
|
return "BEGIN:VCALENDAR\n"\
|
|
+"VERSION:1.0\n"\
|
|
+"BEGIN:VEVENT\n"\
|
|
+"SUMMARY:phone meeting\n"\
|
|
+"DTEND:20060406T163000Z\n"\
|
|
+"DTSTART:20060406T160000Z\n"\
|
|
+"DTSTAMP:20060406T211449Z\n"\
|
|
+"LOCATION:my office\n"\
|
|
+"DESCRIPTION:let's talkREVISION\n"\
|
|
+"END:VEVENT\n"\
|
|
+"END:VCALENDAR\n"
|
|
if (source == 'todo' and (type == 'text/calendar:2.0' or type == 'text/calendar')):
|
|
return "BEGIN:VCALENDAR\n"\
|
|
+"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"\
|
|
+"VERSION:2.0\n"\
|
|
+"METHOD:PUBLISH\n"\
|
|
+"BEGIN:VTODO\n"\
|
|
+"DTSTAMP:20060417T173712Z\n"\
|
|
+"SUMMARY:do me\n"\
|
|
+"DESCRIPTION:to be doneREVISION\n"\
|
|
+"CREATED:20060417T173712\n"\
|
|
+"LAST-MODIFIED:20060417T173712\n"\
|
|
+"END:VTODO\n"\
|
|
+"END:VCALENDAR\n"
|
|
if (source == 'todo' and (type == 'text/x-vcalendar:1.0' or type == 'text/x-vcalendar')):
|
|
return "BEGIN:VCALENDAR\n"\
|
|
+"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"\
|
|
+"VERSION:1.0\n"\
|
|
+"METHOD:PUBLISH\n"\
|
|
+"BEGIN:VTODO\n"\
|
|
+"DTSTAMP:20060417T173712Z\n"\
|
|
+"SUMMARY:do me\n"\
|
|
+"DESCRIPTION:to be doneREVISION\n"\
|
|
+"CREATED:20060417T173712\n"\
|
|
+"LAST-MODIFIED:20060417T173712\n"\
|
|
+"END:VTODO\n"\
|
|
+"END:VCALENDAR\n"
|
|
if (source == 'memo'):
|
|
return "SUMMARY\n"\
|
|
+"BODY TEXT\n"
|
|
return ""
|
|
|
|
# Get the keyword to be matched with for each test case
|
|
def getTestCaseKeywords(source, type):
|
|
if (source == 'addressbook' and (type == 'text/vcard:3.0' or type == 'text/vcard')):
|
|
return ["VCARD", "TITLE:tester", "Doe", "John"]
|
|
if (source == 'addressbook' and (type == 'text/x-vcard:2.1' or type == 'text/x-vcard')):
|
|
return ["VCARD", "TITLE:tester", "Doe", "John"]
|
|
if (source == 'calendar' and (type == 'text/calendar:2.0' or type == 'text/calendar')):
|
|
return ["VCALENDAR", "VEVENT", "phone meeting", "my office"]
|
|
if (source == 'calendar' and (type =='text/x-vcalendar:1.0' or type == 'text/x-vcalendar')):
|
|
return ["VCALENDAR", "VEVENT", "phone meeting", "my office"]
|
|
if (source == 'todo' and (type == 'text/calendar:2.0' or type == 'text/calendar')):
|
|
return ["VCALENDAR", "VTODO", "do me"]
|
|
if (source == 'todo' and (type == 'text/x-vcalendar:1.0' or type == 'text/x-vcalendar')):
|
|
return ["VCALENDAR", "VTODO", "do me"]
|
|
if (source == 'memo'):
|
|
return ["SUMMARY"]
|
|
return ""
|
|
|
|
# Compare the received data with the sent data, we only match selected keywords in received
|
|
# data for a basic sanity test
|
|
def compareSyncData(sources, type):
|
|
for source in sources:
|
|
testcase = getTestCase (source, type)
|
|
received = ''
|
|
recFile = "%s/%s/0" %(testFolder, source)
|
|
try:
|
|
rf = open (recFile)
|
|
received = rf.read()
|
|
except:
|
|
return False
|
|
keys = getTestCaseKeywords(source, type)
|
|
if (options.verbose > 1):
|
|
print "comparing received file:"
|
|
print received
|
|
print "with built in keywords in test case:"
|
|
print keys
|
|
for key in keys:
|
|
if (received.find(key) <0):
|
|
return False
|
|
return True
|
|
|
|
# wrapper of running a shell command
|
|
def runCommand(cmd, exception = True):
|
|
"""Log and run the given command, throwing an exception if it fails."""
|
|
if (options.verbose > 1):
|
|
print "%s: %s" % (os.getcwd(), cmd)
|
|
else:
|
|
cmd += ' >/dev/null'
|
|
sys.stdout.flush()
|
|
result = os.system(cmd)
|
|
if result != 0 and exception:
|
|
raise Exception("%s: failed (return code %d)" % (cmd, result>>8))
|
|
|
|
def runSync(sync):
|
|
rm_r("%s/syncevolution" % testResult)
|
|
status = True
|
|
interrupt = False
|
|
|
|
try:
|
|
runCommand (sync)
|
|
except:
|
|
status = False
|
|
pass
|
|
|
|
# session name is unknown, but we know there is only one, so let
|
|
# glob find it for us
|
|
resultFile = glob.glob("%s/syncevolution/*/status.ini" % testResult)[0]
|
|
|
|
# inject [main] at start of file for ConfigParser,
|
|
# because SyncEvolution doesn't write sections
|
|
class IniFile:
|
|
def __init__(self, filename):
|
|
self.fp = open(filename, "r")
|
|
self.read = False
|
|
def readline(self):
|
|
if not self.read:
|
|
self.read = True
|
|
return "[main]"
|
|
else:
|
|
return self.fp.readline()
|
|
|
|
ini = ConfigParser.ConfigParser({"status": "0", "error": ""})
|
|
ini.readfp(IniFile(resultFile))
|
|
statuscode = ini.get("main", "status")
|
|
if statuscode == "20015":
|
|
# aborted by user, stop testing
|
|
status = False
|
|
interrupt = True
|
|
if statuscode == "22002":
|
|
# syncevolution failed (for example, kill -9), warn and abort
|
|
print "\nSyncEvolution binary died prematurely, aborting testing."
|
|
status = False
|
|
interrupt = True
|
|
return (status, interrupt)
|
|
|
|
# recursive directory removal, without throwing an error if directory does not exist
|
|
def rm_r(dirname):
|
|
if os.path.isdir(dirname):
|
|
shutil.rmtree(dirname)
|
|
|
|
def hash2ini(hash):
|
|
"""convert key/value pairs into .ini file without sections"""
|
|
res = []
|
|
for key, value in hash.items():
|
|
res.append("%s = %s" % (key, value))
|
|
return "\n".join(res)
|
|
|
|
##############################TestConfiguration##################################
|
|
class TestingConfiguration():
|
|
def __init__(self, versions, sources, uris, types, ctcaps, identifiers, btaddr):
|
|
if (versions):
|
|
self.versions = versions
|
|
else:
|
|
self.versions = allVersions
|
|
|
|
# If "calendar+todo" is tested, then "calendar" and "todo"
|
|
# must be tested first. If they lead to the same result,
|
|
# then they have to be combined for "calendar+todo".
|
|
# "calendar+todo" itself is never tested directly.
|
|
if sources:
|
|
self.sources = sources
|
|
else:
|
|
self.sources = allSources
|
|
if "calendar+todo" in self.sources:
|
|
self.sources.remove("calendar+todo")
|
|
if not "calendar" in self.sources:
|
|
self.sources.append("calendar")
|
|
if not "todo" in self.sources:
|
|
self.sources.append("todo")
|
|
|
|
if (uris):
|
|
self.uris = uris
|
|
else:
|
|
self.uris = {}
|
|
self.uris['addressbook'] = ['Contact', 'contact', 'Contacts', 'contacts', 'Addressbook', 'addressbook']
|
|
self.uris['calendar'] = ['Calendar', 'calendar', 'Agenda','agenda']
|
|
self.uris['todo'] = self.uris['calendar'] + ['Task', 'task', 'Tasks', 'tasks', 'Todo','todo']
|
|
self.uris['memo'] = ['Memo', 'memo', 'Notes', 'notes', 'Note', 'note']
|
|
|
|
if (types):
|
|
self.types = types
|
|
else:
|
|
self.types = {}
|
|
self.types['addressbook'] = ['text/vcard:3.0', 'text/x-vcard:2.1']
|
|
self.types['calendar'] = self.types['todo'] = ['text/calendar:2.0', 'text/x-vcalendar:1.0']
|
|
self.types['memo'] = ['text/plain:1.0']
|
|
|
|
if (ctcaps):
|
|
self.ctcaps = ctcaps
|
|
else:
|
|
self.ctcaps = [True, False]
|
|
if (identifiers):
|
|
self.identifiers = identifiers
|
|
else:
|
|
self.identifiers = ['PC Suite','','Nokia PC Suite']
|
|
self.btaddr = btaddr
|
|
|
|
|
|
#before each configuration is really tested, prepare is called.
|
|
#returns True if we decide current configuration need not be tested
|
|
def prepare (self, allconfigs, curconfig):
|
|
# Decide whether this config should be skipped (because we already found
|
|
# a working configuration
|
|
# Test is skipped either because
|
|
# 1) we already found a working configuration for the data source;
|
|
# 2) based on the working configuration for source A, we can reasonably
|
|
# guess a working configuration for source B must have the same
|
|
# 'identifier', 'ctcap' and 'SyncMLVersion' setting.
|
|
# 3) we already found a working configuration for combined calendar and
|
|
# task, thus seperate testing for calendar and task is not needed.
|
|
skip = False
|
|
for source, config in self.wConfigs.items():
|
|
if (config):
|
|
if ( (config.source == self.source) or (config.identifier != self.identifier ) or (config.ctcap != self.ctcap) or (config.version != self.version)):
|
|
skip = True
|
|
if (skip):
|
|
if (options.verbose > 1):
|
|
print "Test %d/%d skipped because already found a working configuration" % (curconfig, allconfigs)
|
|
elif options.verbose > 0:
|
|
print "Test %d/%d skipped" %(curconfig, allconfigs), \
|
|
ConfigurationParameter(self.version, self.source, self.uri, self.type, self.ctcap, self.identifier)
|
|
else:
|
|
print "Test %d/%d skipped" %(curconfig, allconfigs)
|
|
else:
|
|
print ("Start %d/%d test" % (curconfig, allconfigs)),
|
|
if (options.verbose > 0):
|
|
config = ConfigurationParameter(self.version, self.source, self.uri, self.type, self.ctcap, self.identifier)
|
|
if (options.verbose > 1):
|
|
print
|
|
config.printMe()
|
|
else:
|
|
print config
|
|
else:
|
|
print
|
|
|
|
return skip
|
|
|
|
|
|
#run the real sync test with current configuration parameter
|
|
#if advanced option is set and the basic test succeed, it will contintue with
|
|
#the advanced test
|
|
def testWithCurrentConfiguration(self):
|
|
""" Prepare the configuration and run a sync session, Returns true if
|
|
the test was successful, otherwise false"""
|
|
rm_r(testConfig)
|
|
cmdPrefix = "XDG_CACHE_HOME=%s XDG_CONFIG_HOME=%s " %(testResult, testConfig)
|
|
syncevoTest = "%s %s --daemon=no" % (cmdPrefix, syncevoCmd)
|
|
runCommand ("%s -c --template 'SyncEvolution Client' --sync-property peerIsClient=1 %s" % (syncevoTest, configName))
|
|
# set the local database
|
|
filesource = testFolder+'/'+self.source
|
|
configCmd = "%s --configure --source-property evolutionsource='file:///%s' %s %s" %(syncevoTest, filesource, configName, self.source)
|
|
runCommand (configCmd)
|
|
|
|
configCmd = "%s --configure --sync-property logLevel=5 --sync-property SyncURL=obex-bt://%s --sync-property SyncMLVersion=%s %s" % (syncevoTest, self.btaddr,self.version, configName)
|
|
runCommand (configCmd)
|
|
|
|
if (self.identifier):
|
|
configCmd = "%s --configure --sync-property remoteIdentifier='%s' %s" %(syncevoTest, self.identifier, configName)
|
|
runCommand (configCmd)
|
|
|
|
configCmd = "%s --configure --source-property type=%s --source-property uri=%s %s %s" %(syncevoTest, "file:"+self.type, self.uri, configName, self.source)
|
|
runCommand (configCmd)
|
|
|
|
""" start the sync session """
|
|
if (not self.ctcap):
|
|
cmdPrefix += "SYNCEVOLUTION_NOCTCAP=t "
|
|
cmdPrefix += "SYNCEVOLUTION_NO_SYNC_SIGNALS=1"
|
|
syncCmd = " ".join((cmdPrefix, syncevoCmd, "--daemon=no", configName, self.source))
|
|
(status,interrupt) = runSync(syncCmd)
|
|
if (options.advanced and status and not interrupt):
|
|
(status,interrupt)= self.advancedTestWithCurrentConfiguration()
|
|
return (status, interrupt)
|
|
|
|
'''Basic test for sending/receiving data
|
|
It will work as:
|
|
Clear local data and data on the phone via 'slow-sync' and 'two-way' sync
|
|
Send local test case to the phone via 'two-way'
|
|
Clear local data and get the data from the phone via 'slow-sync'
|
|
compare the sent data with the received data to decide whether the test was successful
|
|
|
|
Note that this depends on the phone support 'slow-sync' and 'two-way' sync and
|
|
implements the semantics correctly as specified in the spec. Otherwise the results will
|
|
be undefined.
|
|
'''
|
|
def advancedTestWithCurrentConfiguration (self):
|
|
"""
|
|
Sending/receving real data for basic sanity test
|
|
"""
|
|
sources = []
|
|
sources.append(self.source)
|
|
|
|
#step 1: clean the data both locally and remotely using a 'slow-sync' and 'two-way'
|
|
clearLocalSyncData(sources)
|
|
cmdPrefix="XDG_CACHE_HOME=%s XDG_CONFIG_HOME=%s " % (testResult, testConfig)
|
|
if (not self.ctcap):
|
|
cmdPrefix += "SYNCEVOLUTION_NOCTCAP=t "
|
|
syncevoTest = "%s %s --daemon=no" % (cmdPrefix, syncevoCmd)
|
|
syncCmd = "%s --sync slow %s %s" % (syncevoTest, configName, self.source)
|
|
status,interrupt = runSync(syncCmd)
|
|
if (not status or interrupt):
|
|
return (status, interrupt)
|
|
clearLocalSyncData(sources)
|
|
syncCmd = "%s --sync two-way %s %s" % (syncevoTest, configName, self.source)
|
|
status,interrupt = runSync(syncCmd)
|
|
if (not status or interrupt):
|
|
return (status, interrupt)
|
|
|
|
#step 2: insert testcase to local data and sync with 'two-way'
|
|
insertLocalSyncData(sources, self.type)
|
|
syncCmd = "%s --sync two-way %s %s" % (syncevoTest, configName, self.source)
|
|
status,interrupt = runSync(syncCmd)
|
|
if (not status or interrupt):
|
|
return (status, interrupt)
|
|
|
|
#step 3: delete local data and sync with 'slow-sync'
|
|
clearLocalSyncData(sources)
|
|
syncCmd = "%s --sync slow %s %s" % (syncevoTest, configName, self.source)
|
|
status,interrupt = runSync(syncCmd)
|
|
if (not status or interrupt):
|
|
return (status, interrupt)
|
|
|
|
#step 4: compare the received data with test case
|
|
status = compareSyncData(sources, self.type)
|
|
return (status, interrupt)
|
|
|
|
'''
|
|
The test driver iterating all possible test combinations and try them one by one
|
|
'''
|
|
def run(self):
|
|
#first round of iterating, calculating all possible configuration numbers
|
|
allconfigs = 0
|
|
for self.ctcap in self.ctcaps:
|
|
for self.identifier in self.identifiers:
|
|
for self.version in self.versions:
|
|
for self.source in self.sources:
|
|
for self.uri in self.uris[self.source]:
|
|
for self.type in self.types[self.source]:
|
|
allconfigs +=1
|
|
print "Starting test for %d configurations..." %(allconfigs,)
|
|
|
|
curconfig = 0
|
|
self.wConfigs = {}
|
|
for source in self.sources:
|
|
self.wConfigs[source] = None
|
|
|
|
#second round of iterating, test for each configuration
|
|
interrupt = False
|
|
for self.source in self.sources:
|
|
if(interrupt):
|
|
break
|
|
for self.ctcap in self.ctcaps:
|
|
if(interrupt):
|
|
break
|
|
for self.version in self.versions:
|
|
if(interrupt):
|
|
break
|
|
for self.identifier in self.identifiers:
|
|
if(interrupt):
|
|
break
|
|
for self.uri in self.uris[self.source]:
|
|
if(interrupt):
|
|
break
|
|
for self.type in self.types[self.source]:
|
|
curconfig +=1
|
|
skip = self.prepare (allconfigs, curconfig)
|
|
if (not skip):
|
|
(status, interrupt) = self.testWithCurrentConfiguration ()
|
|
if (status and not interrupt):
|
|
self.wConfigs[self.source] = ConfigurationParameter (self.version, self.source, self.uri, self.type, self.ctcap, self.identifier)
|
|
print "Found a working configuration for %s" % (self.source,)
|
|
if (options.verbose > 0):
|
|
self.wConfigs[self.source].printMe()
|
|
if (interrupt):
|
|
break;
|
|
if(interrupt):
|
|
print "Test Interrupted"
|
|
return 1
|
|
|
|
print "Test Ended"
|
|
|
|
#Test finished, print summary and generating configurations
|
|
print "****************SUMMARY****************"
|
|
found = False
|
|
for source,config in self.wConfigs.items():
|
|
if (config):
|
|
found = True
|
|
print "------------------------------------------"
|
|
print "Configuration parameter for %s:" % (source,)
|
|
config.printMe()
|
|
|
|
if (not found):
|
|
print "No working configuration found"
|
|
else:
|
|
have_combined = \
|
|
self.wConfigs.has_key('calendar') and \
|
|
self.wConfigs.has_key('todo') and \
|
|
self.wConfigs['calendar'].uri == self.wConfigs['todo'].uri
|
|
|
|
if (options.create):
|
|
#first remove the previous configuration if there is a configuration with the same name
|
|
create = options.create
|
|
cmd = "%s --remove '%s'" %(syncevoCmd, create)
|
|
try:
|
|
runCommand (cmd)
|
|
except:
|
|
pass
|
|
cmd = "%s -c --template 'SyncEvolution Client' --sync-property peerIsClient=1 %s" %(syncevoCmd, create)
|
|
runCommand (cmd)
|
|
#disable all sources by default
|
|
for source in allSources:
|
|
if source == 'calendar+todo':
|
|
continue
|
|
cmd = "%s -c --source-property sync='disabled' %s %s" %(syncevoCmd, create, source)
|
|
runCommand(cmd)
|
|
|
|
syncCreated = False
|
|
for source,config in self.wConfigs.items():
|
|
if (config):
|
|
if (not syncCreated):
|
|
#set the sync parameter
|
|
cmd = "%s --configure --sync-property syncURL='obex-bt://%s' --sync-property remoteIdentifier='%s' --sync-property SyncMLVersion='%s' '%s'" %(syncevoCmd, self.btaddr, config.identifier, config.version, create)
|
|
syncCreated = True
|
|
runCommand (cmd)
|
|
#set each source parameter
|
|
ltype = config.type.split(':')[0]
|
|
cmd = "%s --configure --source-property sync='two-way' --source-property URI='%s' --source-property type='%s:%s' '%s' '%s'" %(syncevoCmd, config.uri, source, ltype, create, config.source)
|
|
runCommand(cmd)
|
|
if have_combined:
|
|
ltype = self.wConfigs['calendar'].type.split(':')[0]
|
|
uri = self.wConfigs['calendar'].uri
|
|
cmd = "%s --configure --source-property sync='two-way' --source-property URI='%s' --source-property type='virtual:%s' '%s' calendar+todo" %(syncevoCmd, uri, ltype, create)
|
|
runCommand(cmd)
|
|
for source in ('calendar', 'todo'):
|
|
cmd = "%s --configure --source-property sync='none' --source-property URI='%s' '%s' calendar+todo" %(syncevoCmd, uri, create)
|
|
runCommand(cmd)
|
|
|
|
|
|
if (options.advanced):
|
|
print ""
|
|
print "We have conducted basic test by sending and receiving"
|
|
print "data to the phone. You can help the SyncEvolution project"
|
|
print "and other users by submitting the following configuration"
|
|
print "template at http://syncevolution.org/wiki/phone-compatibility-template"
|
|
print ""
|
|
|
|
configini = { "peerIsClient": "1" }
|
|
sourceConfigInis = {}
|
|
syncCreated = False
|
|
if (config.identifier):
|
|
configini["remoteIdentifier"] = config.identifier
|
|
if (config.version != '1.2'):
|
|
configini["SyncMLVersion"] = config.version
|
|
|
|
for source,config in self.wConfigs.items():
|
|
if(config):
|
|
sourceini = {}
|
|
sourceini["sync"] = "two-way"
|
|
sourceini["uri"] = config.uri
|
|
sourceini["type"] = "%s:%s" % (source, config.type.split(':')[0])
|
|
sourceConfigInis[source] = sourceini
|
|
|
|
# create 'calendar+todo' entry, disable separate 'calendar' and 'todo'?
|
|
if have_combined:
|
|
sourceini = {}
|
|
sourceini["evolutionsource"] = "calendar,todo"
|
|
sourceini["uri"] = self.wConfigs['calendar'].uri
|
|
sourceini["type"] = "virtual:%s" % self.wConfigs['calendar'].type.split(':')[0]
|
|
sourceConfigInis['calendar+todo'] = sourceini
|
|
# disable the sub datasources
|
|
for source in ('calendar', 'todo'):
|
|
sourceConfigInis[source]["sync"] = "none"
|
|
sourceConfigInis[source].pop("uri")
|
|
|
|
# print template to stdout
|
|
sep = "--------------------> snip <--------------------"
|
|
print sep
|
|
print "=== template.ini ==="
|
|
print "fingerprint = <Model> <Manufacturer>"
|
|
print "=== config.ini ==="
|
|
print hash2ini(configini)
|
|
for source, configini in sourceConfigInis.items():
|
|
print "=== sources/%s/config.ini ===" % source
|
|
print hash2ini(configini)
|
|
print sep
|
|
else:
|
|
print ""
|
|
print "We just conducted minimum test by syncing with the phone"
|
|
print "without checking received data. For more reliable result,"
|
|
print "use the --advanced option, but beware that it will overwrite"
|
|
print "contacts, events, tasks and memos on the phone."
|
|
|
|
if (options.create):
|
|
print ""
|
|
print "Created configuration: %s" %(options.create)
|
|
print "You may start syncing with: syncevolution %s" %(options.create)
|
|
|
|
def main():
|
|
versions = []
|
|
sources = []
|
|
ctcaps = []
|
|
identifiers = []
|
|
uris = {}
|
|
types = {}
|
|
if (options.version):
|
|
versions.append (options.version)
|
|
if (options.source):
|
|
sources.append (options.source)
|
|
if (options.uri):
|
|
uris[sources[0]] = []
|
|
uris[sources[0]].append(options.uri)
|
|
if (options.type):
|
|
types[sources[0]] = []
|
|
types[sources[0]].append(options.type)
|
|
if (options.ctcap):
|
|
ctcaps.append (options.ctcap)
|
|
if (options.identifier):
|
|
identifiers.append (options.identifier)
|
|
|
|
config = TestingConfiguration (versions, sources, uris, types, ctcaps,
|
|
identifiers, options.btaddr)
|
|
|
|
tmpdir = tempfile.mkdtemp(prefix="syncevo-phone-config")
|
|
global testFolder
|
|
global testResult
|
|
global testConfig
|
|
testFolder = tmpdir+'/data'
|
|
testResult = tmpdir+'/cache'
|
|
testConfig = tmpdir+'/config'
|
|
print "Running test with test data inside %s and test results inside %s" %(testFolder, testResult)
|
|
config.run()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|