create_test.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #!/usr/bin/python
  2. #
  3. # --- BEGIN COPYRIGHT BLOCK ---
  4. # Copyright (C) 2016 Red Hat, Inc.
  5. # All rights reserved.
  6. #
  7. # License: GPL (version 3 or any later version).
  8. # See LICENSE for details.
  9. # --- END COPYRIGHT BLOCK ---
  10. import optparse
  11. import os
  12. import re
  13. import sys
  14. import uuid
  15. from lib389 import topologies
  16. """This script generates a template test script that handles the
  17. non-interesting parts of a test script:
  18. - topology fixture that doesn't exist in in lib389/topologies.py
  19. - test function (to be completed by the user),
  20. - run-isolated function
  21. """
  22. def displayUsage():
  23. """Display the usage"""
  24. print ('\nUsage:\ncreate_ticket.py -t|--ticket <ticket number> ' +
  25. '-s|--suite <suite name> ' +
  26. '[ i|--instances <number of standalone instances> ' +
  27. '[ -m|--masters <number of masters> -h|--hubs <number of hubs> ' +
  28. '-c|--consumers <number of consumers> ] -o|--outputfile ]\n')
  29. print ('If only "-t" is provided then a single standalone instance is ' +
  30. 'created. Or you can create a test suite script using ' +
  31. '"-s|--suite" instead of using "-t|--ticket". The "-i" option ' +
  32. 'can add mulitple standalone instances (maximum 99). However, you' +
  33. ' can not mix "-i" with the replication options (-m, -h , -c). ' +
  34. 'There is a maximum of 99 masters, 99 hubs, and 99 consumers.')
  35. print('If "-s|--suite" option was chosen, then no topology would be added ' +
  36. 'to the test script. You can find predefined fixtures in the lib389/topologies.py ' +
  37. 'and use them or write a new one if you have a special case.')
  38. exit(1)
  39. def writeFinalizer():
  40. """Write the finalizer function - delete/stop each instance"""
  41. def writeInstanceOp(action):
  42. TEST.write(' map(lambda inst: inst.{}(), topology.all_insts.values())\n'.format(action))
  43. TEST.write('\n def fin():\n')
  44. TEST.write(' """If we are debugging just stop the instances, otherwise remove them"""\n\n')
  45. TEST.write(' if DEBUGGING:\n')
  46. writeInstanceOp('stop')
  47. TEST.write(' else:\n')
  48. writeInstanceOp('delete')
  49. TEST.write('\n request.addfinalizer(fin)')
  50. TEST.write('\n\n')
  51. def get_existing_topologies(inst, masters, hubs, consumers):
  52. """Check if the requested topology exists"""
  53. if inst:
  54. if inst == 1:
  55. i = 'st'
  56. else:
  57. i = 'i{}'.format(inst)
  58. else:
  59. i = ''
  60. if masters:
  61. ms = 'm{}'.format(masters)
  62. else:
  63. ms = ''
  64. if hubs:
  65. hs = 'h{}'.format(hubs)
  66. else:
  67. hs = ''
  68. if consumers:
  69. cs = 'c{}'.format(consumers)
  70. else:
  71. cs = ''
  72. my_topology = 'topology_{}{}{}{}'.format(i, ms, hs, cs)
  73. # Returns True in the first element of a list, if topology was found
  74. if my_topology in dir(topologies):
  75. return [True, my_topology]
  76. else:
  77. return [False, my_topology]
  78. def check_id_uniqueness(id_value):
  79. """Checks if ID is already present in other tests.
  80. create_test.py script should exist in the directory
  81. with a 'tests' dir.
  82. """
  83. tests_dir = os.path.join(os.getcwd(), 'tests')
  84. for root, dirs, files in os.walk(tests_dir):
  85. for name in files:
  86. with open(os.path.join(root, name), "r") as file:
  87. for line in file:
  88. if re.search(str(id_value), line):
  89. return False
  90. return True
  91. desc = 'Script to generate an initial lib389 test script. ' + \
  92. 'This generates the topology, test, final, and run-isolated functions.'
  93. if len(sys.argv) > 0:
  94. parser = optparse.OptionParser(description=desc, add_help_option=False)
  95. # Script options
  96. parser.add_option('-t', '--ticket', dest='ticket', default=None)
  97. parser.add_option('-s', '--suite', dest='suite', default=None)
  98. parser.add_option('-i', '--instances', dest='inst', default='0')
  99. parser.add_option('-m', '--masters', dest='masters', default='0')
  100. parser.add_option('-h', '--hubs', dest='hubs', default='0')
  101. parser.add_option('-c', '--consumers', dest='consumers', default='0')
  102. parser.add_option('-o', '--outputfile', dest='filename', default=None)
  103. # Validate the options
  104. try:
  105. (args, opts) = parser.parse_args()
  106. except:
  107. displayUsage()
  108. if args.ticket is None and args.suite is None:
  109. print('Missing required ticket number/suite name')
  110. displayUsage()
  111. if args.ticket and args.suite:
  112. print('You must choose either "-t|--ticket" or "-s|--suite", ' +
  113. 'but not both.')
  114. displayUsage()
  115. if int(args.masters) == 0:
  116. if int(args.hubs) > 0 or int(args.consumers) > 0:
  117. print('You must use "-m|--masters" if you want to have hubs ' +
  118. 'and/or consumers')
  119. displayUsage()
  120. if not args.masters.isdigit() or \
  121. int(args.masters) > 99 or \
  122. int(args.masters) < 0:
  123. print('Invalid value for "--masters", it must be a number and it can' +
  124. ' not be greater than 99')
  125. displayUsage()
  126. if not args.hubs.isdigit() or int(args.hubs) > 99 or int(args.hubs) < 0:
  127. print('Invalid value for "--hubs", it must be a number and it can ' +
  128. 'not be greater than 99')
  129. displayUsage()
  130. if not args.consumers.isdigit() or \
  131. int(args.consumers) > 99 or \
  132. int(args.consumers) < 0:
  133. print('Invalid value for "--consumers", it must be a number and it ' +
  134. 'can not be greater than 99')
  135. displayUsage()
  136. if args.inst:
  137. if not args.inst.isdigit() or \
  138. int(args.inst) > 99 or \
  139. int(args.inst) < 0:
  140. print('Invalid value for "--instances", it must be a number ' +
  141. 'greater than 0 and not greater than 99')
  142. displayUsage()
  143. if int(args.inst) > 0:
  144. if int(args.masters) > 0 or \
  145. int(args.hubs) > 0 or \
  146. int(args.consumers) > 0:
  147. print('You can not mix "--instances" with replication.')
  148. displayUsage()
  149. # Extract usable values
  150. ticket = args.ticket
  151. suite = args.suite
  152. if args.inst == '0' and args.masters == '0' and args.hubs == '0' \
  153. and args.consumers == '0':
  154. instances = 1
  155. my_topology = [True, 'topology_st']
  156. else:
  157. instances = int(args.inst)
  158. masters = int(args.masters)
  159. hubs = int(args.hubs)
  160. consumers = int(args.consumers)
  161. my_topology = get_existing_topologies(instances, masters, hubs, consumers)
  162. filename = args.filename
  163. # Create/open the new test script file
  164. if not filename:
  165. if ticket:
  166. filename = 'ticket' + ticket + '_test.py'
  167. else:
  168. filename = suite + '_test.py'
  169. try:
  170. TEST = open(filename, "w")
  171. except IOError:
  172. print("Can\'t open file:", filename)
  173. exit(1)
  174. # Write the imports
  175. if my_topology[0]:
  176. topology_import = 'from lib389.topologies import {} as topo\n'.format(my_topology[1])
  177. else:
  178. topology_import = ''
  179. TEST.write('import logging\nimport pytest\n')
  180. TEST.write('{}\n'.format(topology_import))
  181. TEST.write('DEBUGGING = os.getenv("DEBUGGING", default=False)\n')
  182. TEST.write('if DEBUGGING:\n')
  183. TEST.write(' logging.getLogger(__name__).setLevel(logging.DEBUG)\n')
  184. TEST.write('else:\n')
  185. TEST.write(' logging.getLogger(__name__).setLevel(logging.INFO)\n')
  186. TEST.write('log = logging.getLogger(__name__)\n\n\n')
  187. # Add topology function for non existing (in lib389/topologies.py) topologies only
  188. if not my_topology[0]:
  189. # Write the replication or standalone classes
  190. topologies_str = ""
  191. if masters > 0:
  192. topologies_str += " {} masters".format(masters)
  193. if hubs > 0:
  194. topologies_str += " {} hubs".format(hubs)
  195. if consumers > 0:
  196. topologies_str += " {} consumers".format(consumers)
  197. if instances > 0:
  198. topologies_str += " {} standalone instances".format(instances)
  199. # Write the 'topology function'
  200. TEST.write('@pytest.fixture(scope="module")\n')
  201. TEST.write('def topo(request):\n')
  202. TEST.write(' """Create a topology with{}"""\n\n'.format(topologies_str))
  203. TEST.write(' topology = create_topology({\n')
  204. if masters > 0:
  205. TEST.write(' ReplicaRole.MASTER: {},\n'.format(masters))
  206. if hubs > 0:
  207. TEST.write(' ReplicaRole.HUB: {},\n'.format(hubs))
  208. if consumers > 0:
  209. TEST.write(' ReplicaRole.CONSUMER: {},\n'.format(consumers))
  210. if instances > 0:
  211. TEST.write(' ReplicaRole.STANDALONE: {},\n'.format(instances))
  212. TEST.write(' })\n')
  213. TEST.write(' # You can write replica test here. Just uncomment the block and choose instances\n')
  214. TEST.write(' # replicas = Replicas(topology.ms["master1"])\n')
  215. TEST.write(' # replicas.test(DEFAULT_SUFFIX, topology.cs["consumer1"])\n')
  216. writeFinalizer()
  217. tc_id = '0'
  218. while not check_id_uniqueness(tc_id): tc_id = uuid.uuid4()
  219. # Write the test function
  220. if ticket:
  221. TEST.write('\ndef test_ticket{}(topo):\n'.format(ticket))
  222. else:
  223. TEST.write('\ndef test_something(topo):\n')
  224. TEST.write(' """Write one-line test case purpose (name) here\n\n')
  225. TEST.write(' :id: {}\n'.format(tc_id))
  226. TEST.write(' :feature: Fill in feature name here\n')
  227. TEST.write(' :setup: Fill in set up configuration here\n')
  228. TEST.write(' :steps:\n')
  229. TEST.write(' 1. Fill in test case steps here\n')
  230. TEST.write(' 2. And indent them like this (RST format requirement)\n')
  231. TEST.write(' :expectedresults:\n')
  232. TEST.write(' 1. Fill in the result that is expected\n')
  233. TEST.write(' 2. For each test step\n')
  234. TEST.write(' """\n\n')
  235. TEST.write(' # If you need any test suite initialization,\n')
  236. TEST.write(' # please, write additional fixture for that (including finalizer).\n'
  237. ' # Topology for suites are predefined in lib389/topologies.py.\n\n')
  238. TEST.write(' # If you need host, port or any other data about instance,\n')
  239. TEST.write(' # Please, use the instance object attributes for that (for example, topo.ms["master1"].serverid)\n\n')
  240. TEST.write(' if DEBUGGING:\n')
  241. TEST.write(' # Add debugging steps(if any)...\n')
  242. TEST.write(' pass\n\n\n')
  243. # Write the main function
  244. TEST.write("if __name__ == '__main__':\n")
  245. TEST.write(' # Run isolated\n')
  246. TEST.write(' # -s for DEBUG mode\n')
  247. TEST.write(' CURRENT_FILE = os.path.realpath(__file__)\n')
  248. TEST.write(' pytest.main("-s %s" % CURRENT_FILE)\n\n')
  249. # Done, close things up
  250. TEST.close()
  251. print('Created: ' + filename)