Example #1
0
    def get_package(self):
        """Get the target package.

        Returns:
            `Package`: Package to run tests on.
        """
        if self.package is not None:
            return self.package

        if self.use_current_env:
            # get package from current context, or return None
            current_context = ResolvedContext.get_current()
            if current_context is None:
                return None

            req = Requirement(self.package_request)
            variant = current_context.get_resolved_package(req.name)
            if variant is None:
                return None

            package = variant.parent

            if not req.range.contains_version(package.version):
                return None

        else:
            # find latest package within request
            package = get_latest_package_from_string(str(self.package_request),
                                                     self.package_paths)
            if package is None:
                raise PackageNotFoundError("Could not find package to test: %s"
                                           % str(self.package_request))

        self.package = package
        return self.package
Example #2
0
    def _get_context(self, requires, quiet=False):

        # if using current env, only return current context if it meets
        # requirements, otherwise return None
        if self.use_current_env:
            current_context = ResolvedContext.get_current()
            if current_context is None:
                return None

            reqs = map(Requirement, requires)
            current_reqs = current_context.get_resolve_as_exact_requests()

            meets_requirements = (
                RequirementList(current_reqs)
                == RequirementList(current_reqs + reqs)
            )

            if meets_requirements:
                return current_context
            else:
                return None

        # create context or use cached context
        key = tuple(requires)
        context = self.contexts.get(key)

        if context is None:
            if self.verbose and not quiet:
                self._print_header(
                    "Resolving test environment: %s\n",
                    ' '.join(map(quote, requires))
                )

            with open(os.devnull, 'w') as f:
                context = ResolvedContext(
                    package_requests=requires,
                    package_paths=self.package_paths,
                    buf=(f if quiet else None),
                    timestamp=self.timestamp,
                    **self.context_kwargs
                )

            self.contexts[key] = context

        if not context.success and not quiet:
            context.print_info(buf=self.stderr)

        return context
Example #3
0
    def run_test(self, test_name):
        """Run a test.

        Runs the test in its correct environment. Note that if tests share the
        same requirements, the contexts will be reused.

        Args:
            test_name (str): Name of test to run.

        Returns:
            int: Exit code of first failed test, or 0 if none failed. If the first
                test to fail did so because it was not able to run (eg its
                environment could not be configured), -1 is returned.
        """
        package = self.get_package()
        exitcode = 0

        if test_name not in self.get_test_names():
            raise PackageTestError("Test '%s' not found in package %s" %
                                   (test_name, package.uri))

        if self.use_current_env:
            if package is None:
                self._add_test_result(
                    test_name, None, "skipped",
                    "The current environment does not contain a package "
                    "matching the request")
                return

            current_context = ResolvedContext.get_current()
            current_variant = current_context.get_resolved_package(
                package.name)
            target_variants = [current_variant]

        else:
            target_variants = self._get_target_variants(test_name)

        for variant in target_variants:

            # get test info for this variant. If None, that just means that this
            # variant doesn't provide this test. That's ok - 'tests' might be
            # implemented as a late function attribute that provides some tests
            # for some variants and not others
            #
            test_info = self._get_test_info(test_name, variant)
            if not test_info:
                self._add_test_result(
                    test_name, variant, "skipped",
                    "The test is not declared in this variant")
                continue

            command = test_info["command"]
            requires = test_info["requires"]
            on_variants = test_info["on_variants"]

            # show progress
            if self.verbose > 1:
                self._print_header("\nRunning test: %s\nPackage: %s\n%s\n",
                                   test_name, variant.uri, '-' * 80)
            elif self.verbose:
                self._print_header("\nRunning test: %s\n%s\n", test_name,
                                   '-' * 80)

            # apply variant selection filter if specified
            if isinstance(on_variants, dict):
                filter_type = on_variants["type"]
                func = getattr(self, "_on_variant_" + filter_type)
                do_test = func(variant, on_variants)

                if not do_test:
                    reason = (
                        "Test skipped as specified by on_variants '%s' filter"
                        % filter_type)

                    print_info(reason)

                    self._add_test_result(test_name, variant, "skipped",
                                          reason)

                    continue

            # add requirements to force the current variant to be resolved.
            # TODO this is not perfect, and will need to be updated when
            # explicit variant selection is added to rez (this is a new
            # feature). Until then, there's no guarantee that we'll resolve to
            # the variant we want, so we take that into account here.
            #
            requires.extend(map(str, variant.variant_requires))

            # create test runtime env
            exc = None
            try:
                context = self._get_context(requires)
            except RezError as e:
                exc = e

            fail_reason = None
            if exc is not None:
                fail_reason = "The test environment failed to resolve: %s" % exc
            elif context is None:
                fail_reason = "The current environment does not meet test requirements"
            elif not context.success:
                fail_reason = "The test environment failed to resolve"

            if fail_reason:
                self._add_test_result(test_name, variant, "failed",
                                      fail_reason)

                print_error(fail_reason)

                if not exitcode:
                    exitcode = -1

                if self.stop_on_fail:
                    self.stopped_on_fail = True
                    return exitcode

                continue

            # check that this has actually resolved the variant we want
            resolved_variant = context.get_resolved_package(package.name)
            assert resolved_variant

            if resolved_variant.handle != variant.handle:
                print_warning(
                    "Could not resolve environment for this variant (%s). This "
                    "is a known issue and will be fixed once 'explicit variant "
                    "selection' is added to rez.", variant.uri)

                self._add_test_result(
                    test_name, variant, "skipped",
                    "Could not resolve to variant (known issue)")
                continue

            # expand refs like {root} in commands
            if isinstance(command, basestring):
                command = variant.format(command)
            else:
                command = map(variant.format, command)

            # run the test in the context
            if self.verbose:
                if self.verbose > 1:
                    context.print_info(self.stdout)
                    print('')

                if isinstance(command, basestring):
                    cmd_str = command
                else:
                    cmd_str = ' '.join(map(quote, command))

                self._print_header("Running test command: %s", cmd_str)

            if self.dry_run:
                self._add_test_result(test_name, variant, "skipped",
                                      "Dry run mode")
                continue

            def _pre_test_commands(executor):
                # run package.py:pre_test_commands() if present
                pre_test_commands = getattr(variant, "pre_test_commands")
                if not pre_test_commands:
                    return

                test_ns = {"name": test_name}

                with executor.reset_globals():
                    executor.bind("this", variant)
                    executor.bind("test", RO_AttrDictWrapper(test_ns))
                    executor.execute_code(pre_test_commands)

            retcode, _, _ = context.execute_shell(
                command=command,
                actions_callback=_pre_test_commands,
                stdout=self.stdout,
                stderr=self.stderr,
                block=True)

            if retcode:
                print_warning("Test command exited with code %d", retcode)

                self._add_test_result(
                    test_name, variant, "failed",
                    "Test failed with exit code %d" % retcode)

                if not exitcode:
                    exitcode = retcode

                if self.stop_on_fail:
                    self.stopped_on_fail = True
                    return exitcode

                continue

            # test passed
            self._add_test_result(test_name, variant, "success",
                                  "Test succeeded")

            # just test against one variant in this case
            if on_variants is False:
                break

        return exitcode