Beispiel #1
0
 def test_path_prepend(self):
     d = {
         'PATH': self.native_path('foo:bar:baz'),
     }
     env_var(d, 'PATH').prepend('apple')
     self.assertEqual(['apple', 'foo', 'bar', 'baz'],
                      env_var(d, 'PATH').path)
Beispiel #2
0
 def test_path(self):
     d = {
         'PATH': self.native_path('foo:bar:baz'),
     }
     self.assertEqual(self.native_path('foo:bar:baz'),
                      self.native_path(env_var(d, 'PATH').value))
     self.assertEqual(['foo', 'bar', 'baz'], env_var(d, 'PATH').path)
Beispiel #3
0
 def test_path_duplicates(self):
     d = {
         'PATH': self.native_path('foo:bar:baz:foo'),
     }
     self.assertEqual(self.native_path('foo:bar:baz:foo'),
                      env_var(d, 'PATH').value)
     self.assertEqual(['foo', 'bar', 'baz'], env_var(d, 'PATH').path)
Beispiel #4
0
    def _create(clazz, root_dir, exe):
        clazz._log.log_method_d()

        venv_config = path.join(root_dir, 'pyvenv.cfg')

        if path.isfile(venv_config):
            return

        cmd = [
            exe,
            '-m',
            'venv',
            root_dir,
        ]
        env = os_env.make_clean_env()
        PATH = env_var(env, 'PATH')
        PATH.prepend(path.dirname(exe))
        PYTHONPATH = env_var(env, 'PYTHONPATH')
        PYTHONPATH.unset()

        clazz._log.log_d('_create: env={}'.format(pprint.pformat(env)))
        clazz._log.log_d('_create: cmd={}'.format(' '.join(cmd)))

        with dir_cleanup(tempfile.gettempdir()) as ctx:
            rv = execute.execute(cmd, env=env, raise_error=False)
        if rv.exit_code != 0:
            raise python_error('failed to init virtual env: "{}" - {}'.format(
                ' '.join(cmd), rv.stderr))
        clazz._ensure_versioned_python_exe(root_dir, exe)
Beispiel #5
0
    def test_name(self):
        d = {
            'FOO': '666',
            'BAR': 'hello',
            'PATH': self.native_path('foo:bar:baz'),
        }
        self.assertEqual('666', env_var(d, 'FOO').value)
        self.assertEqual(['666'], env_var(d, 'FOO').path)

        e = env_var(d, 'FOO')
        e.value = '1000'
        self.assertEqual('1000', env_var(d, 'FOO').value)
Beispiel #6
0
 def transform_env(self, env):
     new_env = copy.deepcopy(env)
     for inst in self.instructions(env):
         if inst.action == action.SET:
             new_env[inst.key] = inst.value
         elif inst.action == action.UNSET:
             if inst.key in new_env:
                 del new_env[inst.key]
         elif inst.action == action.PATH_APPEND:
             v = env_var(new_env, inst.key)
             v.append(inst.value)
         elif inst.action == action.PATH_PREPEND:
             v = env_var(new_env, inst.key)
             v.prepend(inst.value)
         elif inst.action == action.PATH_REMOVE:
             v = env_var(new_env, inst.key)
             v.remove(inst.value)
     return new_env
Beispiel #7
0
 def env(self):
   'Make a clean environment for python or pip'
   clean_env = os_env.make_clean_env()
   env_var(clean_env, 'PYTHONUSERBASE').value = self.root_dir
   env_var(clean_env, 'PYTHONPATH').path = self.PYTHONPATH
   env_var(clean_env, 'PATH').prepend(self.PATH)
   env_var(clean_env, 'HOME').value = self._fake_home_dir
   env_var(clean_env, 'TMPDIR').value = self._fake_tmp_dir
   env_var(clean_env, 'TMP').value = self._fake_tmp_dir
   env_var(clean_env, 'TEMP').value = self._fake_tmp_dir
   clean_env.update(self._extra_env)
   return clean_env
Beispiel #8
0
  def call_program(self, args, **kargs):
    'Call a program with the right environment'
    command_line.check_args_type(args)

    kargs = copy.deepcopy(kargs)
    
    self._log.log_method_d()
    self._log.log_d('call_program: args={}'.format(args))

    parsed_args = command_line.parse_args(args)
    self._log.log_d('call_program: parsed_args={}'.format(parsed_args))

    env = os_env.clone_current_env()
    env.update(self.env)
    PATH = env_var(env, 'PATH')
    PYTHONPATH = env_var(env, 'PYTHONPATH')
    
    if 'env' in kargs:
      kargs_env = kargs['env']
      del kargs['env']
      if 'PATH' in kargs_env:
        PATH.append(kargs_env['PATH'])
        del kargs_env['PATH']
      if 'PYTHONPATH' in kargs_env:
        PYTHONPATH.append(kargs_env['PYTHONPATH'])
        del kargs_env['PYTHONPATH']
      env.update(kargs_env)

    PATH.prepend(self.PATH)
    PYTHONPATH.prepend(self.PYTHONPATH)
    kargs['env'] = env
    self._log.log_d('call_program: env={}'.format(env))

    kargs['shell'] = True
    kargs['check_python_script'] = False

    for key, value in sorted(env.items()):
      self._log.log_d('call_program({}): ENV: {}={}'.format(args[0], key, value))
    
    return execute.execute(parsed_args, **kargs)
Beispiel #9
0
 def test_path_remove(self):
     d = {
         'PATH': self.native_path('foo:bar:baz'),
     }
     env_var(d, 'PATH').remove('bar')
     self.assertEqual(['foo', 'baz'], env_var(d, 'PATH').path)
Beispiel #10
0
 def update_environment(self, env, variables):
     for config in self._env_dependencies_configs:
         substituted = config.substitute(variables)
         env_var(env, 'PATH').append(substituted.data.unixpath)
         env_var(env, 'PYTHONPATH').append(substituted.data.pythonpath)
Beispiel #11
0
def main():
    DEBUG = os.environ.get('DEBUG', False)

    import bes
    vcli = version_cli(bes)
    parser = argparse.ArgumentParser()
    parser.add_argument('files',
                        action='store',
                        nargs='*',
                        help='Files or directories to rename')
    vcli.version_add_arguments(parser)
    parser.add_argument('--dry-run',
                        '-n',
                        action='store_true',
                        default=False,
                        help='Only print what files will get tests [ False ]')
    parser.add_argument(
        '--timing',
        '-t',
        action='store_true',
        default=False,
        help='Show the amount of time it takes to run tests [ False ]')
    parser.add_argument('--verbose',
                        '-v',
                        action='store_true',
                        default=False,
                        help='Verbose debug output [ False ]')
    parser.add_argument('--stop',
                        '-s',
                        action='store_true',
                        default=False,
                        help='Stop right after the first failure. [ False ]')
    parser.add_argument(
        '--randomize',
        action='store_true',
        default=False,
        help='Randomize the order in which unit tests run. [ False ]')
    parser.add_argument(
        '--python',
        action='append',
        default=[],
        help=
        'Python executable) to use.  Multiple flags can be used for running with mutiple times with different python versions [ python ]'
    )
    parser.add_argument('--page',
                        '-p',
                        action='store_true',
                        default=False,
                        help='Page output with $PAGER [ False ]')
    parser.add_argument(
        '--profile',
        action='store',
        default=None,
        help=
        'Profile the code with cProfile and store the output in the given argument [ None ]'
    )
    parser.add_argument(
        '--coverage',
        action='store',
        default=None,
        help=
        'Run coverage on the code and store the output in the given argument [ None ]'
    )
    parser.add_argument('--pager',
                        action='store',
                        default=os.environ.get('PAGER', 'more'),
                        help='Pager to use when paging [ %s ]' %
                        (os.environ.get('PAGER', 'more')))
    parser.add_argument('--iterations',
                        '-i',
                        action='store',
                        default=1,
                        type=int,
                        help='Python executable to use [ python ]')
    parser.add_argument(
        '--git',
        '-g',
        action='store_true',
        default=False,
        help='Use git status to figure out what has changed to test [ False ]')
    parser.add_argument(
        '--commit',
        '-c',
        action='store',
        type=str,
        default=None,
        help='Test only the files affected by the given git commit [ None ]')
    parser.add_argument('--pre-commit',
                        action='store_true',
                        default=False,
                        help='Run pre commit checks [ False ]')
    parser.add_argument('--print-tests',
                        action='store_true',
                        default=False,
                        help='Print the list of unit tests [ False ]')
    parser.add_argument('--print-python',
                        action='store_true',
                        default=False,
                        help='Print the detected python executable [ False ]')
    parser.add_argument('--print-files',
                        action='store_true',
                        default=False,
                        help='Print the list of unit files [ False ]')
    parser.add_argument(
        '--egg',
        action='store_true',
        default=False,
        help=
        'Make an egg of the package and run the tests against that instead the live files. [ False ]'
    )
    parser.add_argument(
        '--save-egg',
        action='store_true',
        default=False,
        help='Save the egg in the current directory. [ False ]')
    parser.add_argument('--ignore',
                        action='append',
                        default=[],
                        help='Patterns of filenames to ignore []')
    parser.add_argument(
        '--root-dir',
        action='store',
        default=None,
        help=
        'The root directory for all your projets.  By default its computed from your git struture.  [ None ]'
    )
    parser.add_argument('--dont-hack-env',
                        action='store_true',
                        default=False,
                        help='Dont hack PATH and PYTHONPATH. [ False ]')
    parser.add_argument(
        '--compile-only',
        action='store_true',
        default=False,
        help='Just compile the files to verify syntax [ False ]')
    parser.add_argument(
        '--print-deps',
        action='store_true',
        default=False,
        help='Print python dependencies for test files [ False ]')
    parser.add_argument('--print-configs',
                        action='store_true',
                        default=False,
                        help='Print testing configs found [ False ]')
    parser.add_argument('--print-root-dir',
                        action='store_true',
                        default=False,
                        help='Print the root dir [ False ]')
    parser.add_argument('--print-path',
                        action='store_true',
                        default=False,
                        help='Print sys.path [ False ]')
    parser.add_argument(
        '--file-ignore-file',
        action='append',
        default=[],
        help=
        'List of file ignore files. [ .bes_test_ignore .bes_test_internal_ignore ]'
    )
    parser.add_argument('--env',
                        action='append',
                        default=[],
                        help='Environment variables to set [ None ]')
    parser.add_argument('--no-env-deps',
                        action='store_true',
                        default=False,
                        help='Dont use env deps. [ False ]')
    parser.add_argument(
        '--temp-dir',
        action='store',
        default=None,
        help=
        'The directory to use for tmp files overriding the system default.  [ None ]'
    )
    parser.add_argument(
        '--keep-side-effects',
        action='store_true',
        default=DEBUG,
        help='Dont delete side effects - for debugging. [ False ]')
    parser.add_argument(
        '--ignore-side-effects',
        action='store_true',
        default=DEBUG,
        help='Dont delete side effects - for debugging. [ False ]')

    found_git_exe = git_exe.find_git_exe()
    if not found_git_exe:
        printer.writeln_name(
            'ERROR: No git found.  Git is needed to run bes_test.')
        return 1

    for g in parser._action_groups:
        g._group_actions.sort(key=lambda x: x.dest)

    args = parser.parse_args()

    args.python = _resolve_python_exe_list(args.python)

    if not args.python:
        python_exe = python.find_python_exe()
        if python_exe:
            args.python = [python_exe]

    if not args.python:
        printer.writeln_name(
            'ERROR: No python found.  Python is needed to run bes_test.')
        return 1

    _LOG.log_d('using python={}'.format(args.python))

    if args.git and args.commit:
        printer.writeln_name(
            'ERROR: Only one of --git or --commit can be given.')
        return 1

    if args.temp_dir:
        file_util.mkdir(args.temp_dir)
        tempfile.tempdir = args.temp_dir

    if DEBUG:
        args.verbose = True

    cwd = os.getcwd()

    if args.version:
        vcli.version_print_version()
        return 0

    args.env = _parse_args_env(args.env)

    if not args.files:
        args.files = [cwd]

    if not args.file_ignore_file:
        args.file_ignore_file = [
            '.bes_test_ignore', '.bes_test_internal_ignore'
        ]

    if args.commit:
        if args.commit in ['HEAD', 'last']:
            args.commit = git.last_commit_hash('.')

    ar = argument_resolver(cwd,
                           args.files,
                           root_dir=args.root_dir,
                           file_ignore_filename=args.file_ignore_file,
                           check_git=args.git,
                           git_commit=args.commit,
                           use_env_deps=not args.no_env_deps)
    ar.num_iterations = args.iterations
    ar.randomize = args.randomize

    ar.ignore_with_patterns(args.ignore)

    if args.compile_only:
        total_files = len(ar.all_files)
        for i, f in enumerate(ar.all_files):
            tmp = temp_file.make_temp_file()
            filename_count_blurb = ' ' + _make_count_blurb(i + 1, total_files)
            short_filename = file_util.remove_head(f, cwd)
            blurb = '%7s:%s %s ' % ('compile', filename_count_blurb,
                                    short_filename)
            printer.writeln_name(blurb)
            py_compile.compile(f, cfile=tmp, doraise=True)
        return 0

    if not ar.test_descriptions:
        return 1

    if args.print_python:
        for python_exe in args.python:
            print(python_exe)
        return 0

    if args.print_path:
        for p in sys.path:
            print(p)
        return 0

    if args.print_configs:
        ar.print_configs()
        return 0

    if args.print_root_dir:
        print(ar.root_dir)
        return 0

    if args.print_files:
        ar.print_files()
        return 0

    if args.print_tests:
        ar.print_tests()
        return 0

    if args.print_deps or args.pre_commit and not ar.supports_test_dependency_files(
    ):
        printer.writeln_name(
            'ERROR: Cannot figure out dependencies.  snakefood missing.')
        return 1

    if args.print_deps:
        dep_files = ar.test_dependency_files()
        for filename in sorted(dep_files.keys()):
            print(filename)
            for dep_file in dep_files[filename]:
                print('  %s' % (dep_file.filename))
        return 0

    # Read ~/.bes_test/bes_test.config (or use a default config)
    bes_test_config = _read_config_file()
    keep_patterns = bes_test_config.get_value_string_list(
        'environment', 'keep_patterns')

    # Start with a clean environment so unit testing can be deterministic and not subject
    # to whatever the user happened to have exported.  PYTHONPATH and PATH for dependencies
    # are set below by iterating the configs
    keep_keys = bes_test_config.get_value_string_list('environment',
                                                      'keep_keys')
    if args.dont_hack_env:
        keep_keys.extend(['PATH', 'PYTHONPATH'])

    keep_keys.extend(['TMPDIR', 'TEMP', 'TMP'])
    env = os_env.make_clean_env(
        keep_keys=keep_keys,
        keep_func=lambda key: _env_var_should_keep(key, keep_patterns))
    env_var(env, 'PATH').prepend(path.dirname(found_git_exe))
    for python_exe in args.python:
        env_var(env, 'PATH').prepend(path.dirname(python_exe))
    env['PYTHONDONTWRITEBYTECODE'] = 'x'

    variables = {
        'rebuild_dir': path.expanduser('~/.rebuild'),
        'system': host.SYSTEM,
    }

    if not args.dont_hack_env:
        for var in ar.env_dependencies_variables():
            ov = os_env_var(var)
            if ov.is_set:
                value = ov.value
            else:
                value = ''
            variables[var] = value
        ar.update_environment(env, variables)

    # Update env with whatever was given in --env
    env.update(args.env)

    # Use a custom TMP dir so that we can catch temporary side effects and flag them
    tmp_tmp = temp_file.make_temp_dir(prefix='bes_test_',
                                      suffix='.tmp.tmp.dir',
                                      delete=False)
    env.update({
        'TMPDIR': tmp_tmp,
        'TEMP': tmp_tmp,
        'TMP': tmp_tmp,
    })
    side_effects = {}

    num_passed = 0
    num_failed = 0
    num_executed = 0
    num_tests = len(ar.test_descriptions)
    failed_tests = []

    # Remove current dir from sys.path to avoid side effects
    if cwd in sys.path:
        sys.path.remove(cwd)

    if args.egg:
        pythonpath = env_var(env, 'PYTHONPATH')
        pythonpath.remove(cwd)
        for config in ar.env_dependencies_configs:
            setup_dot_py = path.join(config.root_dir, 'setup.py')
            if not path.isfile(setup_dot_py):
                raise RuntimeError('No setup.py found in %s to make the egg.' %
                                   (cwd))
            egg_zip = egg.make(config.root_dir,
                               'master',
                               setup_dot_py,
                               untracked=False)
            pythonpath.prepend(egg_zip)
            printer.writeln_name('using tmp egg: %s' % (egg_zip))
            if args.save_egg:
                file_util.copy(egg_zip, path.join(cwd, path.basename(egg_zip)))

    if args.pre_commit:
        missing_from_git = []
        for filename, dep_files in ar.test_dependency_files().items():
            for dep_file in dep_files:
                if dep_file.config and not dep_file.git_tracked:
                    missing_from_git.append(dep_file.filename)
        if missing_from_git:
            for f in missing_from_git:
                printer.writeln_name('PRE_COMMIT: missing from git: %s' %
                                     (path.relpath(f)))
            return 1
        return 0

    ar.cleanup_python_compiled_files()

    # Do all our work with a temporary working directory to be able to check for side effects
    tmp_cwd = temp_file.make_temp_dir(prefix='bes_test_',
                                      suffix='.tmp.cwd.dir',
                                      delete=False)
    tmp_home = temp_file.make_temp_dir(prefix='bes_test_',
                                       suffix='.tmp.home.dir',
                                       delete=False)
    os.environ['HOME'] = tmp_home
    os.chdir(tmp_cwd)

    # Use what the OS thinks the path is (to deal with symlinks and virtual tmpfs things)
    tmp_cwd = os.getcwd()

    if not args.dry_run and args.page:
        printer.OUTPUT = tempfile.NamedTemporaryFile(prefix='bes_test',
                                                     delete=True,
                                                     mode='w')

    total_tests = _count_tests(ar.inspect_map, ar.test_descriptions)
    total_files = len(ar.test_descriptions)

    total_num_tests = 0

    if args.profile:
        args.profile = path.abspath(args.profile)
        if not _check_program('cprofilev'):
            return 1

    if args.coverage:
        args.coverage = path.abspath(args.coverage)
        coverage_exe = _check_program('coverage')
        if not coverage_exe:
            return 1
        args.python = [coverage_exe]

    if args.profile and args.coverage:
        printer.writeln_name(
            'ERROR: --profile and --coverage are mutually exclusive.')
        return 1

    options = test_options(args.dry_run, args.verbose, args.stop, args.timing,
                           args.profile, args.coverage, args.python,
                           args.temp_dir, tmp_home)

    timings = {}

    total_time_start = time.time()

    stopped = False
    for i, test_desc in enumerate(ar.test_descriptions):
        file_info = test_desc.file_info
        filename = file_info.filename
        if not filename in timings:
            timings[filename] = []
        for python_exe in args.python:
            result = _test_execute(python_exe, ar.inspect_map, filename,
                                   test_desc.tests, options, i + 1,
                                   total_files, cwd, env)
            _collect_side_effects(side_effects, filename, tmp_home, 'home',
                                  args.keep_side_effects)
            _collect_side_effects(side_effects, filename, tmp_tmp, 'tmp',
                                  args.keep_side_effects)
            _collect_side_effects(side_effects, filename, os.getcwd(), 'cwd',
                                  args.keep_side_effects)
            timings[filename].append(result.elapsed_time)
            total_num_tests += result.num_tests_run
            num_executed += 1
            if result.success:
                num_passed += 1
            else:
                num_failed += 1
                failed_tests.append((python_exe, filename, result))
            if args.stop and not result.success:
                stopped = True
        if stopped:
            break
    total_elapsed_time = 1000 * (time.time() - total_time_start)

    if args.dry_run:
        return 0
    num_skipped = num_tests - num_executed
    summary_parts = []

    if total_num_tests == total_tests:
        function_summary = '(%d %s)' % (total_tests,
                                        _make_test_string(total_tests))
    else:
        function_summary = '(%d of %d %s)' % (total_num_tests, total_tests,
                                              _make_test_string(total_tests))

    if num_failed > 0:
        summary_parts.append('%d of %d fixtures FAILED' %
                             (num_failed, num_tests))
    summary_parts.append('%d of %d passed %s' %
                         (num_passed, num_tests, function_summary))
    if num_skipped > 0:
        summary_parts.append('%d of %d skipped' % (num_skipped, num_tests))

    summary = '; '.join(summary_parts)
    printer.writeln_name('%s' % (summary))
    if failed_tests:
        longest_python_exe = max(
            [len(path.basename(p)) for p in options.interpreters])
        for python_exe, filename, result in failed_tests:
            if len(options.interpreters) > 1:
                python_exe_blurb = path.basename(python_exe).rjust(
                    longest_python_exe)
            else:
                python_exe_blurb = ''
            error_status = unit_test_output.error_status(result.output)
            for error in error_status.errors:
                printer.writeln_name('%5s: %s %s :%s.%s' %
                                     (error.error_type, python_exe_blurb,
                                      file_util.remove_head(filename, cwd),
                                      error.fixture, error.function))

    if num_failed > 0:
        rv = 1
    else:
        rv = 0

    if args.timing:
        filenames = sorted(timings.keys())
        num_filenames = len(filenames)
        for i, filename in zip(range(0, num_filenames), filenames):
            short_filename = file_util.remove_head(filename, cwd)
            all_timings = timings[filename]
            num_timings = len(all_timings)
            avg_ms = _timing_average(all_timings) * 1000.0
            if num_timings > 1:
                run_blurb = '(average of %d runs)' % (num_timings)
            else:
                run_blurb = ''
            if num_filenames > 1:
                count_blurb = '[%s of %s] ' % (i + 1, num_filenames)
            else:
                count_blurb = ''

            printer.writeln_name(
                'timing: %s%s - %2.2f ms %s' %
                (count_blurb, short_filename, avg_ms, run_blurb))
        if total_elapsed_time >= 1000.0:
            printer.writeln_name('total time: %2.2f s' %
                                 (total_elapsed_time / 1000.0))
        else:
            printer.writeln_name('total time: %2.2f ms' % (total_elapsed_time))

    if args.page:
        subprocess.call([args.pager, printer.OUTPUT.name])

    current_cwd = os.getcwd()
    if current_cwd != tmp_cwd:
        rv = 1
        printer.writeln_name(
            'SIDE EFFECT: working directory was changed from %s to %s' %
            (tmp_cwd, current_cwd))

    if not args.ignore_side_effects:
        for test, items in sorted(side_effects.items()):
            for item in items:
                rv = 1
                filename = item.filename
                print('SIDE EFFECT [{}] {} {}'.format(
                    item.label, test.replace(cwd + os.sep, ''), filename))

    os.chdir('/tmp')

    if not args.keep_side_effects:
        file_util.remove(tmp_cwd)
        file_util.remove(tmp_home)
        file_util.remove(tmp_tmp)

    return rv