Пример #1
0
    def _fetch(self, inject=None):
        """ Initialize / refresh test plan data.

        Either fetch them from the server or use provided hash.
        """
        TCMS._fetch(self, inject)

        # Fetch the data hash from the server unless provided
        if inject is None:
            log.info("Fetching test plan " + self.identifier)
            try:
                inject = self._server.TestPlan.filter({'pk': self.id})[0]
            except IndexError as error:
                log.debug(error)
                raise TCMSError(
                    "Failed to fetch test plan TP#{0}".format(self.id))
            self._inject = inject
        # Otherwise just initialize the id from inject
        else:
            self._id = inject["plan_id"]
        log.debug("Initializing test plan " + self.identifier)
        log.data(pretty(inject))
        if "plan_id" not in inject:
            log.data(pretty(inject))
            raise TCMSError("Failed to initialize " + self.identifier)

        # Set up attributes
        self._author = User(inject["author_id"])
        if inject["owner_id"] is not None:
            self._owner = User(inject["owner_id"])
        else:
            self._owner = None
        self._name = inject["name"]
        self._product = Product({
            "id": inject["product_id"],
            "name": inject["product"]})
        self._version = Version({
            "id": inject["product_version_id"],
            "value": inject["product_version"],
            "product_id": inject["product_id"]})
        self._type = PlanType(inject["type_id"])
        self._status = PlanStatus(inject["is_active"] in ["True", True])
        if inject["parent_id"] is not None:
            self._parent = TestPlan(inject["parent_id"])
        else:
            self._parent = None

        # Initialize containers
        self._testcases = PlanCases(self)
        self._testruns = PlanRuns(self)
        self._children = ChildPlans(self)
        # If all tags are cached, initialize them directly from the inject
        if "tag" in inject and Tag._is_cached(inject["tag"]):
            self._tags = PlanTags(
                self, inset=[Tag(tag) for tag in inject["tag"]])
        else:
            self._tags = PlanTags(self)

        # Index the fetched object into cache
        self._index()
Пример #2
0
 def search(**query):
     """ Search for test cases """
     # Special handling for automated & manual attributes
     manual = automated = None
     if "automated" in query:
         automated = query["automated"]
         del query["automated"]
     if "manual" in query:
         manual = query["manual"]
         del query["manual"]
     # Map to appropriate value of 'is_automated' attribute
     if manual is not None or automated is not None:
         if automated is False and manual is False:
             raise TCMSError("Invalid search "
                             "('manual' and 'automated' cannot be both False)")
         elif automated is False:
             query["is_automated"] = 0
         elif manual is False:
             query["is_automated"] = 1
         elif automated is True and manual is True:
             query["is_automated"] = 2
         elif automated is True:
             query["is_automated__in"] = [1, 2]
         elif manual is True:
             query["is_automated__in"] = [0, 2]
     log.debug("Searching for test cases")
     log.data(pretty(query))
     return [TestCase(inject)
             for inject in TCMS()._server.TestCase.filter(dict(query))]
Пример #3
0
    def _create(self, name, product, version, type, **kwargs):
        """ Create a new test plan """

        hash = {}

        # Name
        if name is None:
            raise TCMSError("Name required for creating new test plan")
        hash["name"] = name

        # Product
        if product is None:
            raise TCMSError("Product required for creating new test plan")
        elif isinstance(product, (int, str)):
            product = Product(product)
        hash["product"] = product.id

        # Version
        if version is None:
            raise TCMSError("Version required for creating new test plan")
        elif isinstance(version, int):
            version = Version(version)
        elif isinstance(version, str):
            version = Version(name=version, product=product)
        hash["default_product_version"] = version.id

        # Type
        if type is None:
            raise TCMSError("Type required for creating new test plan")
        elif isinstance(type, (int, str)):
            type = PlanType(type)
        hash["type"] = type.id

        # Parent
        parent = kwargs.get("parent")
        if parent is not None:
            if isinstance(parent, int):
                parent = TestPlan(parent)
            hash["parent"] = parent.id

        # Document - if not explicitly specified, put empty text
        hash["text"] = kwargs.get("text", " ")

        # Workaround for BZ#725995
        hash["is_active"] = "1"

        # Submit
        log.info("Creating a new test plan")
        log.data(pretty(hash))
        inject = self._server.TestPlan.create(hash)
        log.data(pretty(inject))
        try:
            self._id = inject["plan_id"]
        except TypeError:
            log.debug("Failed to create a new test plan")
            log.data(pretty(hash))
            log.data(pretty(inject))
            raise TCMSError("Failed to create test plan")
        self._fetch(inject)
        log.info("Successfully created {0}".format(self))
Пример #4
0
 def _fetch(self, inset=None):
     """ Fetch currently attached tags from the server """
     # If data initialized from the inset ---> we're done
     if Container._fetch(self, inset):
         return
     log.info("Fetching tags for {0}".format(self._identifier))
     injects = self._server.Tag.filter({'case': self.id})
     log.debug(pretty(injects))
     self._current = set([Tag(inject) for inject in injects])
     self._original = set(self._current)
Пример #5
0
    def _fetch(self, inject=None):
        """ Initialize / refresh test run data.

        Either fetch them from the server or use the provided hash.
        """
        TCMS._fetch(self, inject)

        # Fetch the data hash from the server unless provided
        if inject is None:
            log.info("Fetching test run {0}".format(self.identifier))
            try:
                inject = self._server.TestRun.filter({'pk': self.id})[0]
            except IndexError as error:
                log.debug(error)
                raise TCMSError(
                    "Failed to fetch test run TR#{0}".format(self.id))
            self._inject = inject
        else:
            self._id = inject["run_id"]
        log.debug("Initializing test run {0}".format(self.identifier))
        log.data(pretty(inject))

        # Set up attributes
        self._build = Build(inject["build_id"])
        self._manager = User(inject["manager_id"])
        self._notes = inject["notes"]
        self._status = RunStatus(inject["stop_date"])
        self._old_status = self._status
        self._summary = inject["summary"]
        self._tester = User(inject["default_tester_id"])
        self._testplan = TestPlan(inject["plan_id"])
        self._time = inject["estimated_time"]
        try:
            self._started = datetime.datetime.strptime(
                inject["start_date"], "%Y-%m-%d %H:%M:%S")
        except TypeError:
            self._started = None
        try:
            self._finished = datetime.datetime.strptime(
                inject["stop_date"], "%Y-%m-%d %H:%M:%S")
        except TypeError:
            self._finished = None

        # Initialize containers
        self._caseruns = RunCaseRuns(self)
        self._testcases = RunCases(self)
        self._tags = RunTags(self)

        # Index the fetched object into cache
        self._index()
Пример #6
0
    def _fetch(self, inject=None):
        """ Fetch user data from the server """
        TCMS._fetch(self, inject)

        if inject is None:
            # Search by id
            if self._id is not TCMSNone:
                try:
                    log.info("Fetching user " + self.identifier)
                    inject = self._server.User.filter({"id": self.id})[0]
                except IndexError:
                    raise TCMSError("Cannot find user for " + self.identifier)
            # Search by login
            elif self._login is not TCMSNone:
                try:
                    log.info("Fetching user for login '{0}'".format(
                        self.login))
                    inject = self._server.User.filter({"username":
                                                       self.login})[0]
                except IndexError:
                    raise TCMSError("No user found for login '{0}'".format(
                        self.login))
            # Search by email
            elif self._email is not TCMSNone:
                try:
                    log.info("Fetching user for email '{0}'".format(
                        self.email))
                    inject = self._server.User.filter({"email": self.email})[0]
                except IndexError:
                    raise TCMSError("No user found for email '{0}'".format(
                        self.email))
            # Otherwise initialize to the current user
            else:
                log.info("Fetching the current user")
                inject = self._server.User.get()
                self._index("i-am-current-user")

        # Initialize data from the inject and index into cache
        log.debug("Initializing user UID#{0}".format(inject["id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["id"]
        self._login = inject["username"]
        self._email = inject["email"]
        if inject["first_name"] and inject["last_name"]:
            self._name = inject["first_name"] + " " + inject["last_name"]
        else:
            self._name = None
        self._index(self.login, self.email)
Пример #7
0
 def _fetch(self, inset=None):
     """ Save cache timestamp and initialize from inset if given """
     TCMS._fetch(self)
     # Create copies of the initial set (if given)
     if inset is not None:
         log.debug("Initializing {0} for {1} from the inset".format(
             self.__class__.__name__, self._identifier))
         log.debug(pretty(inset))
         self._current = set(inset)
         self._original = set(inset)
     # cache into container class
     if config.get_cache_level() >= config.CACHE_OBJECTS:
         self.__class__._cache[self._id] = self
     # Return True if the data are already initialized
     return inset is not None
Пример #8
0
    def _create(self, testcase, testrun, **kwargs):
        """ Create a new case run """

        hash = {}

        # TestCase
        if testcase is None:
            raise TCMSError("Case ID required for new case run")
        elif isinstance(testcase, str):
            testcase = TestCase(testcase)
        hash["case"] = testcase.id

        # TestRun
        if testrun is None:
            raise TCMSError("Run ID required for new case run")
        elif isinstance(testrun, str):
            testrun = TestRun(testrun)
        hash["run"] = testrun.id

        # Build is required by XMLRPC
        build = testrun.build
        hash["build"] = build.id

        # Submit
        log.info("Creating new case run")
        log.data(pretty(hash))
        inject = self._server.TestCaseRun.create(hash)
        log.data(pretty(inject))
        try:
            self._id = inject["case_run_id"]
        except TypeError:
            log.debug("Failed to create new case run")
            log.data(pretty(hash))
            log.data(pretty(inject))
            raise TCMSError("Failed to create case run")
        self._fetch(inject)
        log.info("Successfully created {0}".format(self))

        # And finally add to testcases and caseruns containers
        self.testrun.testcases._fetch(
            [self.testcase] + list(self.testrun.testcases))
        self.testrun.caseruns._fetch(
            [self] + list(self.testrun.caseruns))
Пример #9
0
    def _server(self):
        """ Connection to the server """

        # Connect to the server unless already connected
        if TCMS._connection is None:
            log.debug("Contacting server {0}".format(Config().tcms.url))
            if hasattr(Config().tcms,
                       'use_mod_kerb') and Config().tcms.use_mod_kerb:
                # use Kerberos
                TCMS._connection = xmlrpc.TCMSKerbXmlrpc(
                    Config().tcms.url).server
            else:
                # use plain authentication otherwise
                TCMS._connection = xmlrpc.TCMSXmlrpc(Config().tcms.username,
                                                     Config().tcms.password,
                                                     Config().tcms.url).server

        # Return existing connection
        return TCMS._connection
Пример #10
0
    def _fetch(self, inject=None):
        """ Fetch version data from the server """
        TCMS._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.debug("Processing Version ID#{0} inject".format(inject["id"]))
        # Search by version id
        elif self._id is not TCMSNone:
            try:
                log.info("Fetching version {0}".format(self.identifier))
                inject = self._server.Product.filter_versions({'id':
                                                               self.id})[0]
            except IndexError:
                raise TCMSError("Cannot find version for {0}".format(
                    self.identifier))
        # Search by product and name
        else:
            try:
                log.info("Fetching version '{0}' of '{1}'".format(
                    self.name, self.product.name))
                inject = self._server.Product.filter_versions({
                    'product':
                    self.product.id,
                    'value':
                    self.name
                })[0]
            except IndexError:
                raise TCMSError("Cannot find version for '{0}'".format(
                    self.name))
        # Initialize data from the inject and index into cache
        log.debug("Initializing Version ID#{0}".format(inject["id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["id"]
        self._name = inject["value"]
        self._product = Product(inject["product_id"])
        # Index by product name & version name (if product is cached)
        if self.product._name is not TCMSNone:
            self._index("{0}---in---{1}".format(self.name, self.product.name))
        # Otherwise index by id only
        else:
            self._index()
Пример #11
0
    def _fetch(self, inject=None, **kwargs):
        """ Initialize / refresh test case run data.

        Either fetch them from the server or use the supplied hashes.
        """
        TCMS._fetch(self, inject)

        # Fetch the data from the server unless inject provided
        if inject is None:
            log.info("Fetching case run {0}".format(self.identifier))
            inject = self._server.TestCaseRun.filter({'pk': self.id})[0]
            self._inject = inject
        else:
            self._id = inject["case_run_id"]
        log.debug("Initializing case run {0}".format(self.identifier))
        log.data(pretty(inject))

        # Set up attributes
        self._assignee = User(inject["assignee_id"])
        self._build = Build(inject["build_id"])
        self._notes = inject["notes"]
        if inject["sortkey"] is not None:
            self._sortkey = int(inject["sortkey"])
        else:
            self._sortkey = None
        self._status = Status(inject["case_run_status_id"])
        self._testrun = TestRun(inject["run_id"])
        # Initialize attached test case (from dict, object or id)
        testcaseinject = kwargs.get("testcaseinject", None)
        if testcaseinject and isinstance(testcaseinject, dict):
            self._testcase = TestCase(testcaseinject)
        elif testcaseinject and isinstance(testcaseinject, TestCase):
            self._testcase = testcaseinject
        else:
            self._testcase = TestCase(inject["case_id"])

        # Initialize containers
        self._bugs = CaseRunBugs(self)

        # Index the fetched object into cache
        self._index()
Пример #12
0
    def _fetch(self, inject=None):
        """ Get the missing test plan type data """
        TCMS._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.info("Processing PlanType ID#{0} inject".format(inject["id"]))
        # Search by test plan type id
        elif self._id is not TCMSNone:
            try:
                log.info("Fetching test plan type " + self.identifier)
                inject = self._server.TestPlan.get_plan_type(self.id)
            except xmlrpc.client.Fault as error:
                log.debug(error)
                raise TCMSError("Cannot find test plan type for " +
                                self.identifier)
        # Search by test plan type name
        else:
            try:
                log.info("Fetching test plan type '{0}'".format(self.name))
                inject = self._server.TestPlan.check_plan_type(self.name)
            except xmlrpc.client.Fault as error:
                log.debug(error)
                raise TCMSError("PlanType '{0}' not found".format(self.name))
        # Initialize data from the inject and index into cache
        log.debug("Initializing PlanType ID#{0}".format(inject["id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["id"]
        self._name = inject["name"]
        self._index(self.name)
Пример #13
0
    def _fetch(self, inject=None):
        """ Fetch product data from the server """
        TCMS._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.debug("Initializing Product ID#{0}".format(inject["id"]))
            log.data(pretty(inject))
            self._id = inject["id"]
            self._name = inject["name"]
        # Search by product id
        elif self._id is not TCMSNone:
            try:
                log.info("Fetching product " + self.identifier)
                inject = self._server.Product.filter({'id': self.id})[0]
                log.debug("Initializing product " + self.identifier)
                log.data(pretty(inject))
                self._inject = inject
                self._name = inject["name"]
            except IndexError:
                raise TCMSError("Cannot find product for " + self.identifier)
        # Search by product name
        else:
            try:
                log.info("Fetching product '{0}'".format(self.name))
                inject = self._server.Product.filter({'name': self.name})[0]
                log.debug("Initializing product '{0}'".format(self.name))
                log.data(pretty(inject))
                self._inject = inject
                self._id = inject["id"]
            except IndexError:
                raise TCMSError("Cannot find product for '{0}'".format(
                    self.name))
        # Index the fetched object into cache
        self._index(self.name)
Пример #14
0
    def _fetch(self, inject=None):
        """ Fetch tag data from the server """
        TCMS._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.debug("Initializing Tag ID#{0}".format(inject["id"]))
            log.data(pretty(inject))
            self._id = inject["id"]
            self._name = inject["name"]
        # Search by tag id
        elif self._id is not TCMSNone:
            try:
                log.info("Fetching tag " + self.identifier)
                inject = self._server.Tag.get_tags({'ids': [self.id]})
                log.debug("Initializing tag " + self.identifier)
                log.data(pretty(inject))
                self._inject = inject
                self._name = inject[0]["name"]
            except IndexError:
                raise TCMSError("Cannot find tag for {0}".format(
                    self.identifier))
        # Search by tag name
        else:
            try:
                log.info("Fetching tag '{0}'".format(self.name))
                inject = self._server.Tag.get_tags({'names': [self.name]})
                log.debug("Initializing tag '{0}'".format(self.name))
                log.data(pretty(inject))
                self._inject = inject
                self._id = inject[0]["id"]
            except IndexError:
                raise TCMSError("Cannot find tag '{0}'".format(self.name))
        # Index the fetched object into cache
        self._index(self.name)
Пример #15
0
    def _fetch(self, inject=None):
        """ Get the missing build data """
        TCMS._fetch(self, inject)
        # Directly fetch from the initial object dict
        if inject is not None:
            log.info("Processing build ID#{0} inject".format(
                inject["build_id"]))
        # Search by build id
        elif self._id is not TCMSNone:
            try:
                log.info("Fetching build " + self.identifier)
                inject = self._server.Build.filter({'pk': self.id})[0]
            except IndexError as error:
                log.debug(error)
                raise TCMSError("Cannot find build for " + self.identifier)
        # Search by build name and product
        else:
            try:
                log.info("Fetching build '{0}' of '{1}'".format(
                    self.name, self.product.name))
                inject = self._server.Build.filter({
                    'name': self.name,
                    'product': self.product.id
                })[0]
                self._id = inject["build_id"]
            except IndexError as error:
                log.debug(error)
                raise TCMSError("Build '{0}' not found in '{1}'".format(
                    self.name, self.product.name))
            except KeyError:
                if "args" in inject:
                    log.debug(inject["args"])
                raise TCMSError("Build '{0}' not found in '{1}'".format(
                    self.name, self.product.name))

        # Initialize data from the inject and index into cache
        log.debug("Initializing Build ID#{0}".format(inject["build_id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["build_id"]
        self._name = inject["name"]
        self._product = Product({
            "id": inject["product_id"],
            "name": inject["product"]
        })
        self._index("{0}---in---{1}".format(self.name, self.product.name))
Пример #16
0
    def _fetch(self, inject=None):
        """ Get the missing category data """
        TCMS._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.info("Processing category ID#{0} inject".format(inject["id"]))
        # Search by category id
        elif self._id is not TCMSNone:
            try:
                log.info("Fetching category {0}".format(self.identifier))
                inject = self._server.Product.get_category(self.id)
            except xmlrpc.client.Fault as error:
                log.debug(error)
                raise TCMSError("Cannot find category for " + self.identifier)
        # Search by category name and product
        else:
            try:
                log.info("Fetching category '{0}' of '{1}'".format(
                    self.name, self.product.name))
                inject = self._server.Product.check_category(
                    self.name, self.product.id)
            except xmlrpc.client.Fault as error:
                log.debug(error)
                raise TCMSError("Category '{0}' not found in"
                                " '{1}'".format(self.name, self.product.name))

        # Initialize data from the inject and index into cache
        log.debug("Initializing category ID#{0}".format(inject["id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["id"]
        self._name = inject["name"]
        self._product = Product({
            "id": inject["product_id"],
            "name": inject["product"]
        })
        self._index("{0}---in---{1}".format(self.name, self.product.name))
Пример #17
0
    def _fetch(self, inject=None):
        """ Initialize / refresh test case data.

        Either fetch them from the server or use provided hash.
        """
        TCMS._fetch(self, inject)

        # Fetch the data hash from the server unless provided
        if inject is None:
            log.info("Fetching test case " + self.identifier)
            try:
                inject = self._server.TestCase.filter({'pk': self.id})[0]
            except IndexError as error:
                log.debug(error)
                raise TCMSError(
                    "Failed to fetch test case TC#{0}".format(self.id))
            self._inject = inject
        else:
            self._id = inject["case_id"]
        log.debug("Initializing test case " + self.identifier)
        log.data(pretty(inject))

        # Set up attributes
        self._arguments = inject["arguments"]
        self._author = User(inject["author_id"])
        self._category = Category(inject["category_id"])
        if isinstance(inject["create_date"], str):
            self._created = datetime.datetime.strptime(
                inject["create_date"], "%Y-%m-%d %H:%M:%S")
        else:
            self._created = inject["create_date"]
        self._link = inject["extra_link"]
        self._notes = inject["notes"]
        self._priority = Priority(inject["priority_id"])
        self._requirement = inject["requirement"]
        self._script = inject["script"]
        self._status = CaseStatus(inject["case_status_id"])
        self._summary = inject["summary"]
        self._time = inject["estimated_time"]
        if inject["default_tester_id"] is not None:
            self._tester = User(inject["default_tester_id"])
        else:
            self._tester = None

        # Handle manual, automated and autoproposed
        self._automated = inject["is_automated"] in [1, '1', 2, '2']
        self._manual = inject["is_automated"] in [0, '0', 2, '2']
        self._autoproposed = inject["is_automated_proposed"]

        # Empty script or arguments to be handled same as None
        if self._script == "":
            self._script = None
        if self._arguments == "":
            self._arguments = None

        # Test case documentation
        for attribute in ["setup", "action", "effect", "breakdown"]:
            if "text" in inject:
                setattr(self, "_" + attribute, inject["text"][attribute])
            else:
                setattr(self, "_" + attribute, None)

        # Initialize containers
        self._bugs = CaseBugs(self)
        self._testplans = CasePlans(self)
        self._components = CaseComponents(self)
        # If all tags are cached, initialize them directly from the inject
        if "tag" in inject and Tag._is_cached(inject["tag"]):
            self._tags = CaseTags(
                self, inset=[Tag(tag) for tag in inject["tag"]])
        else:
            self._tags = CaseTags(self)

        # Index the fetched object into cache
        self._index()
Пример #18
0
    def _create(self, summary, category, **kwargs):
        """ Create a new test case """

        hash = {}

        # Summary
        hash["summary"] = summary

        # If category provided as text, we need product as well
        product = kwargs.get("product")
        if isinstance(category, str) and not kwargs.get("product"):
            raise TCMSError(
                "Need product when category specified by name")
        # Category & Product
        if isinstance(category, str):
            category = Category(category=category, product=product)
        elif not isinstance(category, Category):
            raise TCMSError("Invalid category '{0}'".format(category))
        hash["category"] = category.id
        hash["product"] = category.product.id

        # Priority
        priority = kwargs.get("priority")
        if priority is None:
            priority = Priority("P3")
        elif not isinstance(priority, Priority):
            priority = Priority(priority)
        hash["priority"] = priority.id

        # User
        tester = kwargs.get("tester")
        if tester:
            if isinstance(tester, str):
                tester = User(login=tester)
            hash["default_tester"] = tester.login

        # Script, arguments, requirement & reference link
        hash["script"] = kwargs.get("script")
        hash["arguments"] = kwargs.get("arguments")
        hash["requirement"] = kwargs.get("requirement")
        hash["extra_link"] = kwargs.get("link")

        # Case Status
        status = kwargs.get("status")
        if status:
            if isinstance(status, str):
                status = CaseStatus(status)
            hash["case_status"] = status.id

        # Manual, automated and autoproposed
        automated = kwargs.get("automated", True)
        autoproposed = kwargs.get("autoproposed", False)
        manual = kwargs.get("manual", False)
        if automated and manual:
            hash["is_automated"] = 2
        elif automated:
            hash["is_automated"] = 1
        else:
            hash["is_automated"] = 0
        hash["is_automated_proposed"] = autoproposed

        # Estimated time
        hash["estimated_time"] = kwargs.get("time", '00:00:00')

        # Notes
        notes = kwargs.get("notes")
        if notes:
            hash["notes"] = notes

        # Submit
        log.info("Creating a new test case")
        log.data(pretty(hash))
        testcasehash = self._server.TestCase.create(hash)
        log.data(pretty(testcasehash))
        try:
            self._id = testcasehash["case_id"]
        except TypeError:
            log.debug("Failed to create a new test case")
            log.data(pretty(hash))
            log.data(pretty(testcasehash))
            raise TCMSError("Failed to create test case")
        self._fetch(testcasehash)
        log.info("Successfully created {0}".format(self))
Пример #19
0
    def _create(self, testplan, product=None, version=None, build=None,
                summary=None, notes=None, manager=None, tester=None, **kwargs):
        """ Create a new test run """

        hash = {}

        # Test plan
        if isinstance(testplan, int):
            testplan = TestPlan(testplan)
        hash["plan"] = testplan.id

        # Product & version
        if product is None:
            product = testplan.product
        elif not isinstance(product, Product):
            product = Product(product)
        hash["product"] = product.id
        if version is None:
            version = testplan.version
        elif isinstance(version, int):
            version = Version(version)
        else:
            version = Version(name=version, product=product)
        hash["product_version"] = version.id

        # Build
        if build is None:
            build = "unspecified"
        if isinstance(build, str):
            build = Build(build=build, product=product)
        hash["build"] = build.id

        # Summary & notes
        if summary is None:
            summary = "{0} on {1}".format(testplan.name, build)
        if notes is None:
            notes = ""
        hash["summary"] = summary
        hash["notes"] = notes

        # Manager & tester (current user by default)
        if not isinstance(manager, User):
            manager = User(manager)
        if not isinstance(tester, User):
            tester = User(tester)
        hash["manager"] = manager.id
        hash["default_tester"] = tester.id

        # Submit to the server and initialize
        log.info("Creating a new test run based on {0}".format(testplan))
        log.data(pretty(hash))
        testrunhash = self._server.TestRun.create(hash)
        log.data(pretty(testrunhash))
        try:
            self._id = testrunhash["run_id"]
        except TypeError:
            log.debug("Failed to create a new test run based on {0}".format(
                      testplan))
            log.data(pretty(hash))
            log.data(pretty(testrunhash))
            raise TCMSError("Failed to create test run")
        self._fetch(testrunhash)
        # Add newly created test run to testplan.testruns container
        if PlanRuns._is_cached(testplan.testruns):
            testplan.testruns._fetch(list(testplan.testruns) + [self])
        log.info("Successfully created {0}".format(self))