PageRenderTime 1067ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/adapterlib/testrunner.py

https://github.com/jaaskel9/tema-adapterlib
Python | 501 lines | 331 code | 68 blank | 102 comment | 55 complexity | 3e21e74b47f85bf68a9ce30fe11593c2 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2006-2010 Tampere University of Technology
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining
  5. # a copy of this software and associated documentation files (the
  6. # "Software"), to deal in the Software without restriction, including
  7. # without limitation the rights to use, copy, modify, merge, publish,
  8. # distribute, sublicense, and/or sell copies of the Software, and to
  9. # permit persons to whom the Software is furnished to do so, subject to
  10. # the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be
  13. # included in all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. """
  23. Module for running keyword-driven tests
  24. """
  25. from __future__ import with_statement
  26. import time
  27. import datetime
  28. import re
  29. from adapterlib.ToolProtocol import *
  30. from adapterlib.ToolProtocolHTTP import *
  31. import adapterlib.keyword as keyword
  32. import adapterlib.keywordproxy as keywordproxy
  33. from adapterlib.logger import KeywordLogger
  34. class AdapterCompleter(object):
  35. """ Simple class for doing tab-completion in interactive mode"""
  36. def __init__(self, keywords ):
  37. self.keywords = sorted(keywords)
  38. def complete(self, text, state ):
  39. response = None
  40. if state == 0:
  41. if text:
  42. self.matches = [s for s in self.keywords if s and s.startswith(text)]
  43. else:
  44. self.matches = self.keywords[:]
  45. try:
  46. response = self.matches[state]
  47. except IndexError:
  48. response = None
  49. return response
  50. class Target(object):
  51. def __init__(self,name,):
  52. self.__name = name
  53. def setup(self):
  54. raise NotImplementedError()
  55. def cleanup(self):
  56. raise NotImplementedError()
  57. @property
  58. def name(self):
  59. return self.__name
  60. def takeScreenShot(self, path):
  61. return False
  62. class TestRunner(object):
  63. """
  64. TestRunner class is used to run Keyword-driven tests.
  65. The class allows test to be run interactively (given through stdin), from
  66. file or from server.
  67. To run tests from a server, TestRunner uses classes ToolProtocol and
  68. ToolProtocolHTTP.
  69. """
  70. def __init__(self, targets, delay, record = False ):
  71. """
  72. Initializer.
  73. @type targets: list
  74. @param targets: list of System under test (SUT) identifiers.
  75. @type delay: float
  76. @param delay: Wait-time between consequtive keywords (in seconds)
  77. @type record: boolean
  78. @param record: Is the test recorded to html-file
  79. """
  80. self._targetNames = targets
  81. self._targets = []
  82. self.delay = delay
  83. self._rec_process = None
  84. self._kwCount = 1
  85. self._logger = None
  86. self._separator = " "
  87. if record:
  88. self._logger = KeywordLogger()
  89. self._kw_cache = {}
  90. # Special commands listed here for interactive mode completer
  91. self._commands = {}
  92. self._commands["exit"] = ["quit","q","exit"]
  93. self._commands["kws"] = ["list","kws","list full","kws full"]
  94. self._commands["info"] = ["info"]
  95. self._commands["special"] = []
  96. def _setupTestAutomation(self):
  97. """Sets up test automation environment
  98. @rtype: boolean
  99. @returns: True if success, False otherwise
  100. """
  101. raise NotImplementedError()
  102. def _cleanupTestAutomation(self):
  103. """Cleans up test automation environment"""
  104. raise NotImplementedError()
  105. def __setTarget(self,targetName):
  106. if re.match("['\"].*['\"]",targetName):
  107. targetName = targetName[1:-1]
  108. if targetName == "test" or targetName == "testi":
  109. print "Warning: 'test' and 'testi' considered dummy targets."
  110. return True
  111. for t in self._targets:
  112. if t.name == targetName:
  113. self._activeTarget = t
  114. return True
  115. return False
  116. def initTest(self):
  117. """
  118. Inits a test run.
  119. Creates a log file and starts recording if defined.
  120. """
  121. print "Setting up testing environment..."
  122. if not self._setupTestAutomation():
  123. return False
  124. print "setup complete"
  125. self._activeTarget = self._targets[0]
  126. if self._logger:
  127. print "Recording test to a file"
  128. self._logger.startLog()
  129. return True
  130. def _stopTest(self):
  131. """
  132. Stops a test run.
  133. Closes the log-file and stops recording process.
  134. """
  135. print "Cleaning up testing environment..."
  136. self._cleanupTestAutomation()
  137. print "clean up complete"
  138. if self._logger:
  139. self._logger.endLog()
  140. print "Test finished"
  141. def endTest(self):
  142. print "Shutting down"
  143. self._stopTest()
  144. def keywordInfo(self, kw ):
  145. kws = self._getKeywords()
  146. if kw in kws:
  147. print kw
  148. self.printKw(kw,"#",kws[kw][1])
  149. def printKw(self,kw,header,text):
  150. print header*len(kw)
  151. print
  152. docstring = text.splitlines()
  153. strip_len = 0
  154. if len(docstring[0]) == 0:
  155. docstring = docstring[1:]
  156. for line in docstring:
  157. if len(line.strip()) > 0:
  158. first_line = line.lstrip()
  159. strip_len = len(line) - len(first_line)
  160. break
  161. for line in docstring:
  162. print line[strip_len:].rstrip()
  163. print
  164. def listKeywords(self, basekw = keyword.Keyword,full=False,header="#"):
  165. kws = self._getKeywords({},basekw)
  166. kws_keys = sorted(kws.keys())
  167. for kw in kws_keys:
  168. print kw
  169. if full:
  170. self.printKw(kw,header,kws[kw][1])
  171. def _getKeywords(self, kw_dictionary = {}, basekw = keyword.Keyword):
  172. use_cache = len(kw_dictionary) == 0
  173. if use_cache and basekw in self._kw_cache:
  174. return self._kw_cache[basekw]
  175. for kw in basekw.__subclasses__():
  176. kw_name = str(kw)[str(kw).rfind('.')+1:str(kw).rfind("'")]
  177. if not kw_name.endswith("Keyword"):
  178. kw_dictionary[kw_name] = (str(kw.__module__),str(kw.__doc__))
  179. self._getKeywords(kw_dictionary,kw)
  180. if use_cache:
  181. self._kw_cache[basekw] = kw_dictionary
  182. return kw_dictionary
  183. def __instantiateKeywordProxyObject(self,kwproxy, kwName,kwAttr,kwproxy_class):
  184. kwobject = None
  185. try:
  186. kwmodule = __import__(kwproxy_class, globals(), locals(), [kwproxy], -1)
  187. # kwobject = eval("kwmodule." + kw + "()")
  188. kwobject = getattr(kwmodule,kwproxy)()
  189. if not kwobject.initialize(kwName, kwAttr,self._activeTarget):
  190. kwobject = None
  191. if kwobject:
  192. print 'Recognized keyword: %s' % kwName
  193. print 'Attributes: %s' % kwAttr
  194. except Exception, e:
  195. print e
  196. print "Error: KeywordProxy error"
  197. kwobject = None
  198. return kwobject
  199. def __instantiateKeywordObject(self,kw_name,attributes,kw_class):
  200. kwobject = None
  201. try:
  202. kwmodule = __import__(kw_class, globals(), locals(), [kw_name], -1)
  203. # kwobject = eval("kwmodule." + kw + "()")
  204. kwobject = getattr(kwmodule,kw_name)()
  205. print 'Recognized keyword: %s' % kw_name
  206. print 'Attributes: %s' % attributes
  207. if not kwobject.initialize(attributes,self._activeTarget):
  208. print "Invalid parameters"
  209. kwobject = None
  210. except Exception, e:
  211. print e
  212. print "Error: Keyword not recognized!"
  213. kwobject = None
  214. return kwobject
  215. def _instantiateKeyword(self, kwName, kwAttr):
  216. kw_dictionary = self._getKeywords()
  217. kwproxy_dictionary = self._getKeywords({}, keywordproxy.KeywordProxy)
  218. kwobject = None
  219. for kw in kw_dictionary:
  220. if kw.lower() == kwName.lower():
  221. kwobject = self.__instantiateKeywordObject(kw,kwAttr,kw_dictionary[kw][0])
  222. break
  223. else:
  224. for kwproxy in kwproxy_dictionary:
  225. kwobject = self.__instantiateKeywordProxyObject(kwproxy, kwName,kwAttr,kwproxy_dictionary[kwproxy][0])
  226. if kwobject:
  227. break
  228. if not kwobject:
  229. print "Error: Keyword not recognized!"
  230. return kwobject
  231. def __executeKeyword(self, kw):
  232. """
  233. Executes a single keyword.
  234. Searches a corresponding keyword object from the list of keywords and executes the keyword with that object.
  235. @type kw: string
  236. @param kw: executed keyword
  237. @rtype: boolean or string
  238. @return: True if execution was succesfull; False if execution was succesdfull, but the keyword returned False;
  239. Error if there was problems in the execution.
  240. """
  241. print ""
  242. print "Executing keyword: %s" % kw
  243. #Which keyword
  244. result = False
  245. kw = kw.strip()
  246. if kw.startswith("kw_"):
  247. kw = kw[3:].strip()
  248. # Testengine-note: generate-taskswitcher uses space as separator
  249. if kw.startswith("LaunchApp") or kw.startswith("SetTarget"):
  250. if not (kw.startswith("LaunchApp#") or kw.startswith("SetTarget#")):
  251. kw = kw.replace(" ",self._separator,1)
  252. kw_split = kw.split(self._separator,1)
  253. kwName = kw_split[0].strip()
  254. if len(kw_split) == 2:
  255. kwAttr = kw_split[1].strip()
  256. else:
  257. kwAttr = ""
  258. #Changing target
  259. if kwName.lower() == "settarget":
  260. result = self.__setTarget(kwAttr)
  261. print 'result: %s' % str(result)
  262. return result
  263. kwobject = self._instantiateKeyword(kwName,kwAttr)
  264. if not kwobject:
  265. return "ERROR"
  266. startTime = datetime.datetime.now()
  267. result = kwobject.execute()
  268. execTime = datetime.datetime.now() - startTime
  269. print 'result: %s' % str(result)
  270. kwDelay = kwobject.delay
  271. if kwDelay != -1:
  272. if self.delay > kwDelay:
  273. kwDelay = self.delay
  274. time.sleep(kwDelay)
  275. if self._logger:
  276. self._logger.logKeyword(self._activeTarget, kwobject, result, str(execTime))
  277. self.kwCount = self._kwCount + 1
  278. return result
  279. def _handleSpecialCommands(self,command):
  280. return False
  281. def runInteractive(self):
  282. """
  283. Runs an interactive test.
  284. Keywords are read from stdin.
  285. """
  286. # Only import here, so that we can use completion mechanism
  287. # Readline only available in unix
  288. try:
  289. import readline
  290. kws = self._getKeywords({}, keyword.Keyword).keys()
  291. for command_list in self._commands.values():
  292. kws.extend(command_list)
  293. readline.set_completer(AdapterCompleter(kws).complete)
  294. readline.parse_and_bind('tab: complete')
  295. except:
  296. pass
  297. while True:
  298. try:
  299. kw = raw_input(">").strip()
  300. if kw in self._commands["exit"]:
  301. return
  302. elif kw == "":
  303. continue
  304. kw_split = kw.split(" ")
  305. if kw_split[0] in self._commands["kws"]:
  306. if len(kw_split) > 1 and kw_split[1]=="full" and " ".join(kw_split[0:2] )in self._commands["kws"]:
  307. if len(kw_split) == 3:
  308. char = kw_split[2]
  309. else:
  310. char = "#"
  311. self.listKeywords(full=True,header=char)
  312. else:
  313. self.listKeywords(full=False)
  314. elif kw_split[0] in self._commands["info"] and len(kw_split) == 2:
  315. self.keywordInfo(kw_split[1])
  316. elif not self._handleSpecialCommands(kw):
  317. self.__executeKeyword(kw)
  318. except EOFError:
  319. break
  320. def runFromServer(self, address, port, username = None, protocol= None ):
  321. """
  322. Runs a test from server.
  323. @type address: string
  324. @param address: Address of the server
  325. @type port: integer
  326. @param port: Port of the server
  327. @type username: string
  328. @param username: Username is required when using http or https protocol
  329. @type protocol: string
  330. @param protocol: Protocol that is used in the connection. Options are http and https.
  331. Plain socketis used if parameter not given.
  332. """
  333. toolProtocol = None
  334. #while True:
  335. if(address != None and port != None):
  336. if(protocol):
  337. base,path = address.split("/",1)
  338. toolProtocol = ToolProtocolHTTP()
  339. toolProtocol.init(base,path,port,username,protocol)
  340. else:
  341. toolProtocol = ToolProtocol()
  342. toolProtocol.init(address,port)
  343. if toolProtocol.hasConnection() == False:
  344. #print "Connection to the MBT server failed, reconnecting..."
  345. print "Connection to the MBT server failed."
  346. # time.sleep(5)
  347. return
  348. #else:
  349. # break
  350. while True:
  351. kw = ""
  352. #if passive:
  353. # kw = toolProtocol.receiveKeyword()
  354. #else:
  355. kw = toolProtocol.getKeyword()
  356. if (kw == '' or kw =='\n' or kw == "ERROR"):
  357. return
  358. result = self.__executeKeyword(kw)
  359. if(result == "ERROR"):
  360. toolProtocol.putResult(False)
  361. toolProtocol.bye()
  362. return
  363. toolProtocol.putResult(result)
  364. def runFromFile(self, fileName ):
  365. """
  366. Runs a test from file.
  367. @type fileName: string
  368. @param fileName: path to the file that contains the test
  369. """
  370. try:
  371. with open(fileName,'r') as inputFile:
  372. for line in inputFile:
  373. kw = line.strip()
  374. if not kw:
  375. break
  376. result = self.__executeKeyword(kw)
  377. if(result == "ERROR"):
  378. break
  379. except IOError:
  380. print "Error when reading file: %s" % fileName