×

Welcome to TagMyCode

Please login or create account to add a snippet.
0
0
 
0
Language: Python
Posted by: Mihe Lanjelo
Added: Jul 16, 2017 6:53 PM
Views: 8
Tags: no tags
  1. # -*- coding: UTF-8 -*-
  2.  
  3. """
  4. based on from github "https://gist.github.com/s1ider/f13c2f163282dbec7a61"
  5. Customized for parallel scenarios
  6. Authors: i4s-pserrano
  7. """
  8.  
  9. from multiprocessing import Pool
  10. from subprocess import Popen, PIPE
  11. from subprocess import check_output, STDOUT, CalledProcessError, TimeoutExpired
  12. from glob import glob
  13. import logging
  14. import argparse
  15. import json
  16. import sys
  17. from functools import partial
  18. from datetime import datetime
  19.  
  20. logging.basicConfig(level=logging.INFO,
  21.                     format="[%(levelname)-8s %(asctime)s] %(message)s")
  22. logger = logging.getLogger(__name__)
  23.  
  24. delimiter = "_BEHAVE_PARALLEL_BDD_"
  25.  
  26. start_time = datetime.now()
  27.  
  28. def parse_arguments():
  29.     """
  30.    Parses commandline arguments
  31.    :return: Parsed arguments
  32.    """
  33.     parser = argparse.ArgumentParser('Run behave in parallel mode for scenarios')
  34.     parser.add_argument('--processes', '-p', type=int, help='Maximum number of processes. Default = 5', default=5)
  35.     parser.add_argument('--tags', '-t', help='specify behave tags to run')
  36.     parser.add_argument('--timeout', '-tout', type=int,
  37.                         help='Maximum seconds to execute each scenario. Default = 300', default=300)
  38.     args = parser.parse_args()
  39.     return args
  40.  
  41.  
  42. def _run_feature(feature_scenario, timeout):
  43.     """
  44.    Runs features/scenarios
  45.    :param feature_scenario: Feature/scenario that should be run
  46.    :type feature_scenario: str
  47.    :return: Feature/scenario and status
  48.    """
  49.     execution_code = {0: 'OK', 1: 'FAILED', 2: 'TIMEOUT'}
  50.     execution_elements = feature_scenario.split(delimiter)
  51.     logger.debug("Processing feature: {} and scenario {}".format(execution_elements[0], execution_elements[1]))
  52.     params = "--no-capture"
  53.     cmd = "behave --no-summary -k --no-junit -f plain {0} -i {1} --name \"{2}\" -o \"./reports/{1}/{2}.out\"".format(
  54.            params, execution_elements[0], execution_elements[1])
  55.     try:
  56.         r = check_output(cmd, shell=True, timeout=timeout)
  57.         code = 0
  58.     except CalledProcessError as e:
  59.         out_bytes = e.output
  60.         code = e.returncode
  61.     except TimeoutExpired:
  62.         code = 2
  63.     status = execution_code[code]
  64.     logger.info("{0:50}: {1} --> {2}".format(execution_elements[0], execution_elements[1], status))
  65.     return execution_elements[0], execution_elements[1], status
  66.  
  67.  
  68. def main():
  69.     """
  70.    Runner
  71.    """
  72.     args = parse_arguments()
  73.     pool = Pool(args.processes)
  74.     if args.tags:
  75.         cmd = 'behave -d --no-junit --f json --no-summary -t {}'.format(args.tags)
  76.     else:
  77.         cmd = 'behave -d --no-junit --f json --no-summary'
  78.  
  79.     p = Popen(cmd, stdout=PIPE, shell=True)
  80.     out, err = p.communicate()
  81.     try:
  82.         j = json.loads(out.decode())
  83.     except ValueError:
  84.         j = []
  85.     features = [e['location'].replace(r'features/', '')[:-2] for e in j]
  86.  
  87.  
  88.  
  89.     # features dictionary with scenarios
  90.     features_scenarios = [[e['location'].replace(r'features/', '')[:-2] + delimiter + i['name']
  91.                                 for i in e['elements']
  92.                                     if i['keyword'].upper() in ["scenario".upper(), "scenario outline".upper()]]
  93.                             for e in j]
  94.  
  95.     features_and_scenarios = []
  96.     for elto in features_scenarios:
  97.         features_and_scenarios = features_and_scenarios + elto
  98.  
  99.     logger.info("Found {} features".format(len(features)))
  100.     logger.info("Found {} scenarios".format(len(features_and_scenarios)))
  101.     logger.info("Timeout for each scenario {} seconds".format(args.timeout))
  102.     if args.processes > len(features_and_scenarios):
  103.         logger.info("You have defined {} and Will execute only necessary {} parallel process ".format(args.processes,
  104.                                                                                         len(features_and_scenarios)))
  105.     else:
  106.         logger.info("Will execute {} parallel process".format(args.processes))
  107.  
  108.     run_feature = partial(_run_feature, timeout=args.timeout)
  109.     logger.info("--------------------------------------------------------------------------")
  110.     output = 0
  111.     for feature, scenario, status in pool.map(run_feature, features_and_scenarios):
  112.         if status != 'OK':
  113.             if output == 0:
  114.                 if status == "FAILED":
  115.                     output = 1
  116.                 else:
  117.                     output = 2
  118.     logger.info("--------------------------------------------------------------------------")
  119.     end_time = datetime.now()
  120.  
  121.     logger.info("Duration: {}".format(format(end_time - start_time)))
  122.  
  123.     sys.exit(output)
  124.  
  125. if __name__ == '__main__':
  126.     main()