예제 #1
0
    def test_to_xmlrpc(self):
        testrun1 = TestRun.objects.get(pk=self.testrun_pks[0])

        result = TestRun.to_xmlrpc(query={'pk__in': self.testrun_pks})
        self.assertEqual(len(result), 2)

        # Verify fields
        sample_testrun = result[0]
        sample_fields = set([name for name in sample_testrun.keys()])
        test_fields = set(self.test_fields)
        test_result = list(sample_fields ^ test_fields)
        self.assertEqual(test_result, [])

        result = dict([(item['run_id'], item) for item in result])

        sample_testrun1 = result[self.testrun_pks[0]]

        self.assertEqual(testrun1.errata_id, sample_testrun1['errata_id'])
        self.assertEqual(testrun1.product_version.pk,
                         sample_testrun1['product_version_id'])
        self.assertEqual(testrun1.product_version.value,
                         sample_testrun1['product_version'])
        self.assertEqual(testrun1.default_tester.pk,
                         sample_testrun1['default_tester_id'])
        self.assertEqual(testrun1.default_tester.username,
                         sample_testrun1['default_tester'])

        tags = [tag.pk for tag in testrun1.tag.all()]
        tags.sort()
        sample_tags = sample_testrun1['tag']
        sample_tags.sort()
        self.assertEqual(tags, sample_tags)
예제 #2
0
파일: testrun.py 프로젝트: sugus86/Nitrate
def filter(request, values={}):
    """Performs a search and returns the resulting list of test runs.

    :param dict values: a mapping containing these criteria.

        * build: ForeignKey: TestBuild
        * cc: ForeignKey: Auth.User
        * env_value: ForeignKey: Environment Value
        * default_tester: ForeignKey: Auth.User
        * run_id: (int)
        * manager: ForeignKey: Auth.User
        * notes: (str)
        * plan: ForeignKey: TestPlan
        * summary: (str)
        * tag: ForeignKey: Tag
        * product_version: ForeignKey: Version

    :return: list of mappings of found :class:`TestRun`.
    :rtype: list

    Example::

        # Get all of runs contain 'TCMS' in summary
        TestRun.filter({'summary__icontain': 'TCMS'})
        # Get all of runs managed by xkuang
        TestRun.filter({'manager__username': '******'})
        # Get all of runs the manager name starts with x
        TestRun.filter({'manager__username__startswith': 'x'})
        # Get runs contain the case ID 1, 2, 3
        TestRun.filter({'case_run__case__case_id__in': [1, 2, 3]})
    """
    return TestRun.to_xmlrpc(values)
예제 #3
0
def filter(request, values={}):
    """Performs a search and returns the resulting list of test runs.

    :param dict values: a mapping containing these criteria.

        * build: ForeignKey: TestBuild
        * cc: ForeignKey: Auth.User
        * env_value: ForeignKey: Environment Value
        * default_tester: ForeignKey: Auth.User
        * run_id: (int)
        * manager: ForeignKey: Auth.User
        * notes: (str)
        * plan: ForeignKey: TestPlan
        * summary: (str)
        * tag: ForeignKey: Tag
        * product_version: ForeignKey: Version

    :return: list of mappings of found :class:`TestRun`.
    :rtype: list

    Example::

        # Get all of runs contain 'TCMS' in summary
        >>> TestRun.filter({'summary__icontain': 'TCMS'})
        # Get all of runs managed by xkuang
        >>> TestRun.filter({'manager__username': '******'})
        # Get all of runs the manager name starts with x
        >>> TestRun.filter({'manager__username__startswith': 'x'})
        # Get runs contain the case ID 1, 2, 3
        >>> TestRun.filter({'case_run__case__case_id__in': [1, 2, 3]})
    """
    return TestRun.to_xmlrpc(values)
예제 #4
0
파일: testrun.py 프로젝트: urba1n/Kiwi
def filter(query={}):  # pylint: disable=invalid-name
    """
    .. function:: XML-RPC TestRun.filter(query)

        Perform a search and return the resulting list of test runs.

        :param query: Field lookups for :class:`tcms.testruns.models.TestRun`
        :type query: dict
        :return: List of serialized :class:`tcms.testruns.models.TestRun` objects
        :rtype: list(dict)
    """
    return TestRun.to_xmlrpc(query)
예제 #5
0
def get_test_runs(request, plan_id):
    """Get the list of runs in this plan.

    :param int plan_id: plan ID.
    :return: list of mappings of found :class:`TestRun`.
    :rtype: list[dict]

    Example::

        >>> TestPlan.get_test_runs(1)
    """
    from tcms.testruns.models import TestRun

    query = {'plan': plan_id}
    return TestRun.to_xmlrpc(query)
예제 #6
0
def get_test_runs(request, plan_id):
    """Get the list of runs in this plan.

    :param int plan_id: plan ID.
    :return: list of mappings of found :class:`TestRun`.
    :rtype: list[dict]

    Example::

        TestPlan.get_test_runs(1)
    """
    from tcms.testruns.models import TestRun

    query = {'plan': plan_id}
    return TestRun.to_xmlrpc(query)
예제 #7
0
파일: testplan.py 프로젝트: MrSenko/Nitrate
def get_test_runs(request, plan_id):
    """
    Description: Get the list of runs in this plan.

    Params:      $plan_id - Integer: An integer representing the ID of this plan in the database

    Returns:     Array: An array of test run object hashes.

    Example:
    >>> TestPlan.get_test_runs(plan_id)
    """
    from tcms.testruns.models import TestRun

    query = {'plan': plan_id}
    return TestRun.to_xmlrpc(query)
예제 #8
0
파일: testplan.py 프로젝트: jetlyb/Kiwi
def get_test_runs(plan_id):
    """
    Description: Get the list of runs in this plan.

    Params:      $plan_id - Integer: An integer representing the ID of this plan in the database

    Returns:     Array: An array of test run object hashes.

    Example:
    >>> TestPlan.get_test_runs(plan_id)
    """
    from tcms.testruns.models import TestRun

    query = {'plan': plan_id}
    return TestRun.to_xmlrpc(query)
예제 #9
0
def filter(query=None):  # pylint: disable=redefined-builtin
    """
    .. function:: RPC TestRun.filter(query)

        Perform a search and return the resulting list of test runs.

        :param query: Field lookups for :class:`tcms.testruns.models.TestRun`
        :type query: dict
        :return: List of serialized :class:`tcms.testruns.models.TestRun` objects
        :rtype: list(dict)
    """

    if query is None:
        query = {}

    return TestRun.to_xmlrpc(query)
예제 #10
0
def get_runs(request, build_id):
    """Returns the list of runs that this Build is used in.

    :param int build_id: build ID.
    :return: list of test runs.
    :rtype: list

    Example::

        >>> Build.get_runs(1234)
    """
    from tcms.testruns.models import TestRun

    tb = TestBuild.objects.get(build_id=build_id)
    query = {'build': tb}

    return TestRun.to_xmlrpc(query)
예제 #11
0
파일: build.py 프로젝트: Aaln1986/Nitrate
def get_runs(request, build_id):
    """
    Description: Returns the list of runs that this Build is used in.

    Params:      $id -  Integer: Build ID.

    Returns:     Array: List of run object hashes.

    Example:
    >>> Build.get_runs(1234)
    """
    from tcms.testruns.models import TestRun

    tb = TestBuild.objects.get(build_id=build_id)
    query = {"build": tb}

    return TestRun.to_xmlrpc(query)
예제 #12
0
파일: build.py 프로젝트: thr27/Kiwi
def get_runs(request, build_id):
    """
    Description: Returns the list of runs that this Build is used in.

    Params:      $id -  Integer: Build ID.

    Returns:     Array: List of run object hashes.

    Example:
    >>> Build.get_runs(1234)
    """
    from tcms.testruns.models import TestRun

    tb = TestBuild.objects.get(build_id=build_id)
    query = {'build': tb}

    return TestRun.to_xmlrpc(query)
예제 #13
0
파일: build.py 프로젝트: tkdchen/Nitrate
def get_runs(request, build_id):
    """Returns the list of runs that this Build is used in.

    :param int build_id: build ID.
    :return: list of test runs.
    :rtype: list

    Example::

        >>> Build.get_runs(1234)
    """
    from tcms.testruns.models import TestRun

    tb = TestBuild.objects.get(build_id=build_id)
    query = {'build': tb}

    return TestRun.to_xmlrpc(query)
예제 #14
0
def get_runs(build_id):
    """
    .. function:: XML-RPC Build.get_runs(build_id)

        Returns the list of TestRuns that this Build is used in.

        :param build_id: the object ID
        :type build_id: int
        :return: List of serialized :class:`tcms.testruns.models.TestRun` objects
        :rtype: list(dict)
        :raises: TestBuild.DoesNotExist if build not found
    """
    from tcms.testruns.models import TestRun

    tb = TestBuild.objects.get(build_id=build_id)
    query = {'build': tb}

    return TestRun.to_xmlrpc(query)
예제 #15
0
파일: product.py 프로젝트: zumbi/Nitrate
def get_runs(request, product):
    """Get the list of runs associated with this product.

    :params product: product ID or name.
    :type product: int or str
    :return: a list of mappings of test runs.
    :rtype: list

    Example::

        # Get with product id
        >>> Product.get_runs(1)
        # Get with product name
        >>> Product.get_runs('product name')
    """
    from tcms.testruns.models import TestRun

    p = pre_check_product(values=product)
    query = {'build__product': p}
    return TestRun.to_xmlrpc(query)
예제 #16
0
파일: product.py 프로젝트: tkdchen/Nitrate
def get_runs(request, product):
    """Get the list of runs associated with this product.

    :params product: product ID or name.
    :type product: int or str
    :return: a list of mappings of test runs.
    :rtype: list

    Example::

        # Get with product id
        >>> Product.get_runs(1)
        # Get with product name
        >>> Product.get_runs('product name')
    """
    from tcms.testruns.models import TestRun

    p = pre_check_product(values=product)
    query = {'build__product': p}
    return TestRun.to_xmlrpc(query)
예제 #17
0
파일: product.py 프로젝트: Aaln1986/Nitrate
def get_runs(request, product):
    """
    Description: Get the list of runs associated with this product.

    Params:      $product - Integer/String
                            Integer: product_id of the product in the Database
                            String: Product name

    Returns:     Array: Returns an array of Test Run objects.

    Example:
    # Get with product id
    >>> Product.get_runs(61)
    # Get with product name
    >>> Product.get_runs('Red Hat Enterprise Linux 5')
    """
    from tcms.testruns.models import TestRun

    p = pre_check_product(values=product)
    query = {'build__product': p}
    return TestRun.to_xmlrpc(query)
예제 #18
0
def get_runs(request, product):
    """
    Description: Get the list of runs associated with this product.

    Params:      $product - Integer/String
                            Integer: product_id of the product in the Database
                            String: Product name

    Returns:     Array: Returns an array of Test Run objects.

    Example:
    # Get with product id
    >>> Product.get_runs(61)
    # Get with product name
    >>> Product.get_runs('Red Hat Enterprise Linux 5')
    """
    from tcms.testruns.models import TestRun

    p = pre_check_product(values=product)
    query = {'build__product': p}
    return TestRun.to_xmlrpc(query)
예제 #19
0
파일: testrun.py 프로젝트: MrSenko/Nitrate
def filter(request, values={}):
    """
    Description: Performs a search and returns the resulting list of test runs.

    Params:      $values - Hash: keys must match valid search fields.

        +--------------------------------------------------------+
        |                 Run Search Parameters                  |
        +--------------------------------------------------------+
        |        Key          |          Valid Values            |
        | build               | ForeignKey: Build                |
        | cc                  | ForeignKey: Auth.User            |
        | env_value           | ForeignKey: Environment Value    |
        | default_tester      | ForeignKey: Auth.User            |
        | run_id              | Integer                          |
        | manager             | ForeignKey: Auth.User            |
        | notes               | String                           |
        | plan                | ForeignKey: Test Plan            |
        | summary             | String                           |
        | tag                 | ForeignKey: Tag                  |
        | product_version     | ForeignKey: Version              |
        +--------------------------------------------------------+

    Returns:     Array: Matching test runs are retuned in a list of run object hashes.

    Example:
    # Get all of runs contain 'TCMS' in summary
    >>> TestRun.filter({'summary__icontain': 'TCMS'})
    # Get all of runs managed by xkuang
    >>> TestRun.filter({'manager__username': '******'})
    # Get all of runs the manager name starts with x
    >>> TestRun.filter({'manager__username__startswith': 'x'})
    # Get runs contain the case ID 12345, 23456, 34567
    >>> TestRun.filter({'case_run__case__case_id__in': [12345, 23456, 34567]})
    """
    return TestRun.to_xmlrpc(values)
예제 #20
0
def filter(request, values={}):
    """
    Description: Performs a search and returns the resulting list of test runs.

    Params:      $values - Hash: keys must match valid search fields.

        +--------------------------------------------------------+
        |                 Run Search Parameters                  |
        +--------------------------------------------------------+
        |        Key          |          Valid Values            |
        | build               | ForeignKey: Build                |
        | cc                  | ForeignKey: Auth.User            |
        | env_value           | ForeignKey: Environment Value    |
        | default_tester      | ForeignKey: Auth.User            |
        | run_id              | Integer                          |
        | manager             | ForeignKey: Auth.User            |
        | notes               | String                           |
        | plan                | ForeignKey: Test Plan            |
        | summary             | String                           |
        | tag                 | ForeignKey: Tag                  |
        | product_version     | ForeignKey: Version              |
        +--------------------------------------------------------+

    Returns:     Array: Matching test runs are retuned in a list of run object hashes.

    Example:
    # Get all of runs contain 'TCMS' in summary
    >>> TestRun.filter({'summary__icontain': 'TCMS'})
    # Get all of runs managed by xkuang
    >>> TestRun.filter({'manager__username': '******'})
    # Get all of runs the manager name starts with x
    >>> TestRun.filter({'manager__username__startswith': 'x'})
    # Get runs contain the case ID 12345, 23456, 34567
    >>> TestRun.filter({'case_run__case__case_id__in': [12345, 23456, 34567]})
    """
    return TestRun.to_xmlrpc(values)
예제 #21
0
def update(request, run_ids, values):
    """Updates the fields of the selected test run.

    :param run_ids: give one or more run IDs. It could be an integer, a
        string containing comma separated IDs, or a list of int each of them is
        a run ID.
    :type run_ids: int, str or list
    :param dict values: a mapping containing these data to update specified
        runs.

        * plan: (int) TestPlan.plan_id
        * product: (int) Product.id
        * build: (int) Build.id
        * errata_id: (int) Errata.id
        * manager: (int) Auth.User.id
        * default_tester: Intege Auth.User.id
        * summary: (str)
        * estimated_time: (TimeDelta) in format ``2h30m30s`` which is recommended or ``HH:MM:SS``.
        * product_version: (int)
        * plan_text_version: (int)
        * notes: (str)
        * status: (int) 0:RUNNING 1:FINISHED

    :return: list of mappings of the updated test runs.
    :rtype: list[dict]

    Example::

        # Update status to finished for run 1 and 2
        >>> TestRun.update([1, 2], {'status': 1})
    """
    from datetime import datetime
    from tcms.core import forms
    from tcms.testruns.forms import XMLRPCUpdateRunForm

    if (values.get('product_version') and not values.get('product')):
        raise ValueError('Field "product" is required by product_version')

    if values.get('estimated_time'):
        values['estimated_time'] = pre_process_estimated_time(
            values.get('estimated_time'))

    form = XMLRPCUpdateRunForm(values)
    if values.get('product_version'):
        form.populate(product_id=values['product'])

    if form.is_valid():
        trs = TestRun.objects.filter(pk__in=pre_process_ids(value=run_ids))
        _values = dict()
        if form.cleaned_data['plan']:
            _values['plan'] = form.cleaned_data['plan']

        if form.cleaned_data['build']:
            _values['build'] = form.cleaned_data['build']

        if form.cleaned_data['errata_id']:
            _values['errata_id'] = form.cleaned_data['errata_id']

        if form.cleaned_data['manager']:
            _values['manager'] = form.cleaned_data['manager']

        if 'default_tester' in values:
            if values.get('default_tester') and \
                    form.cleaned_data['default_tester']:
                _values['default_tester'] = form.cleaned_data['default_tester']
            else:
                _values['default_tester'] = None

        if form.cleaned_data['summary']:
            _values['summary'] = form.cleaned_data['summary']

        if values.get('estimated_time') is not None:
            _values['estimated_time'] = form.cleaned_data['estimated_time']

        if form.cleaned_data['product_version']:
            _values['product_version'] = form.cleaned_data['product_version']

        if 'notes' in values:
            if values['notes'] in (None, ''):
                _values['notes'] = values['notes']
            if form.cleaned_data['notes']:
                _values['notes'] = form.cleaned_data['notes']

        if form.cleaned_data['plan_text_version']:
            _values['plan_text_version'] = form.cleaned_data[
                'plan_text_version']

        if isinstance(form.cleaned_data['status'], int):
            if form.cleaned_data['status']:
                _values['stop_date'] = datetime.now()
            else:
                _values['stop_date'] = None

        trs.update(**_values)
    else:
        raise ValueError(forms.errors_to_list(form))

    query = {'pk__in': trs.values_list('pk', flat=True)}
    return TestRun.to_xmlrpc(query)
예제 #22
0
파일: testrun.py 프로젝트: sugus86/Nitrate
def update(request, run_ids, values):
    """Updates the fields of the selected test run.

    :param run_ids: give one or more run IDs. It could be an integer, a
        string containing comma separated IDs, or a list of int each of them is
        a run ID.
    :type run_ids: int, str or list
    :param dict values: a mapping containing these data to update specified
        runs.

        * plan: (int) TestPlan.plan_id
        * product: (int) Product.id
        * build: (int) Build.id
        * manager: (int) Auth.User.id
        * default_tester: Intege Auth.User.id
        * summary: (str)
        * estimated_time: (TimeDelta) in format ``2h30m30s`` which is recommended or ``HH:MM:SS``.
        * product_version: (int)
        * plan_text_version: (int)
        * notes: (str)
        * status: (int) 0:RUNNING 1:FINISHED

    :return: list of mappings of the updated test runs.
    :rtype: list[dict]

    .. versionchanged:: 4.5
       Argument ``errata_id`` is removed.

    Example::

        # Update status to finished for run 1 and 2
        TestRun.update([1, 2], {'status': 1})
    """
    from datetime import datetime
    from tcms.core import forms
    from tcms.testruns.forms import XMLRPCUpdateRunForm

    if (values.get('product_version') and not values.get('product')):
        raise ValueError('Field "product" is required by product_version')

    if values.get('estimated_time'):
        values['estimated_time'] = pre_process_estimated_time(
            values.get('estimated_time'))

    form = XMLRPCUpdateRunForm(values)
    if values.get('product_version'):
        form.populate(product_id=values['product'])

    if form.is_valid():
        trs = TestRun.objects.filter(pk__in=pre_process_ids(value=run_ids))
        _values = dict()
        if form.cleaned_data['plan']:
            _values['plan'] = form.cleaned_data['plan']

        if form.cleaned_data['build']:
            _values['build'] = form.cleaned_data['build']

        if form.cleaned_data['manager']:
            _values['manager'] = form.cleaned_data['manager']

        if 'default_tester' in values:
            default_tester = form.cleaned_data['default_tester']
            if values.get('default_tester') and default_tester:
                _values['default_tester'] = default_tester
            else:
                _values['default_tester'] = None

        if form.cleaned_data['summary']:
            _values['summary'] = form.cleaned_data['summary']

        if values.get('estimated_time') is not None:
            _values['estimated_time'] = form.cleaned_data['estimated_time']

        if form.cleaned_data['product_version']:
            _values['product_version'] = form.cleaned_data['product_version']

        if 'notes' in values:
            if values['notes'] in (None, ''):
                _values['notes'] = values['notes']
            if form.cleaned_data['notes']:
                _values['notes'] = form.cleaned_data['notes']

        if form.cleaned_data['plan_text_version']:
            _values['plan_text_version'] = form.cleaned_data[
                'plan_text_version']

        if isinstance(form.cleaned_data['status'], int):
            if form.cleaned_data['status']:
                _values['stop_date'] = datetime.now()
            else:
                _values['stop_date'] = None

        trs.update(**_values)
    else:
        raise ValueError(forms.errors_to_list(form))

    query = {'pk__in': trs.values_list('pk', flat=True)}
    return TestRun.to_xmlrpc(query)
예제 #23
0
파일: testrun.py 프로젝트: MrSenko/Nitrate
def update(request, run_ids, values):
    """
    Description: Updates the fields of the selected test run.

    Params:      $run_ids - Integer/Array/String: An integer or alias representing the ID in the database,
                            an array of run_ids, or a string of comma separated run_ids.

                 $values - Hash of keys matching TestRun fields and the new values
                           to set each field to. See params of TestRun.create for description
    +-------------------+----------------+--------------------------------+
    | Field             | Type           | Description                    |
    +-------------------+----------------+--------------------------------+
    | plan              | Integer        | TestPlan.plan_id               |
    | product           | Integer        | Product.id                     |
    | build             | Integer        | Build.id                       |
    | errata_id         | Integer        | Errata.id                      |
    | manager           | Integer        | Auth.User.id                   |
    | default_tester    | Intege         | Auth.User.id                   |
    | summary           | String         |                                |
    | estimated_time    | TimeDelta      | 2h30m30s(recommend) or HH:MM:SS|
    | product_version   | Integer        |                                |
    | plan_text_version | Integer        |                                |
    | notes             | String         |                                |
    | status            | Integer        | 0:RUNNING 1:FINISHED           |
    +-------------------+----------------+ -------------------------------+
    Returns:     Hash: The updated test run object.

    Example:
    # Update status to finished for run 1193 and 1194
    >>> TestRun.update([1193, 1194], {'status': 1})
    """
    from datetime import datetime
    from tcms.core import forms
    from tcms.testruns.forms import XMLRPCUpdateRunForm

    if (values.get('product_version') and not values.get('product')):
        raise ValueError('Field "product" is required by product_version')

    if values.get('estimated_time'):
        values['estimated_time'] = pre_process_estimated_time(
            values.get('estimated_time'))

    form = XMLRPCUpdateRunForm(values)
    if values.get('product_version'):
        form.populate(product_id=values['product'])

    if form.is_valid():
        trs = TestRun.objects.filter(pk__in=pre_process_ids(value=run_ids))
        _values = dict()
        if form.cleaned_data['plan']:
            _values['plan'] = form.cleaned_data['plan']

        if form.cleaned_data['build']:
            _values['build'] = form.cleaned_data['build']

        if form.cleaned_data['errata_id']:
            _values['errata_id'] = form.cleaned_data['errata_id']

        if form.cleaned_data['manager']:
            _values['manager'] = form.cleaned_data['manager']

        if 'default_tester' in values:
            if values.get('default_tester') and \
                    form.cleaned_data['default_tester']:
                _values['default_tester'] = form.cleaned_data['default_tester']
            else:
                _values['default_tester'] = None

        if form.cleaned_data['summary']:
            _values['summary'] = form.cleaned_data['summary']

        if values.get('estimated_time') is not None:
            _values['estimated_time'] = form.cleaned_data['estimated_time']

        if form.cleaned_data['product_version']:
            _values['product_version'] = form.cleaned_data['product_version']

        if 'notes' in values:
            if values['notes'] in (None, ''):
                _values['notes'] = values['notes']
            if form.cleaned_data['notes']:
                _values['notes'] = form.cleaned_data['notes']

        if form.cleaned_data['plan_text_version']:
            _values['plan_text_version'] = form.cleaned_data[
                'plan_text_version']

        if isinstance(form.cleaned_data['status'], int):
            if form.cleaned_data['status']:
                _values['stop_date'] = datetime.now()
            else:
                _values['stop_date'] = None

        trs.update(**_values)
    else:
        raise ValueError(forms.errors_to_list(form))

    query = {'pk__in': trs.values_list('pk', flat=True)}
    return TestRun.to_xmlrpc(query)
예제 #24
0
def update(request, run_ids, values):
    """
    Description: Updates the fields of the selected test run.

    Params:      $run_ids - Integer/Array/String: An integer or alias representing the ID in the database,
                            an array of run_ids, or a string of comma separated run_ids.

                 $values - Hash of keys matching TestRun fields and the new values
                           to set each field to. See params of TestRun.create for description
    +-------------------+----------------+--------------------------------+
    | Field             | Type           | Description                    |
    +-------------------+----------------+--------------------------------+
    | plan              | Integer        | TestPlan.plan_id               |
    | product           | Integer        | Product.id                     |
    | build             | Integer        | Build.id                       |
    | manager           | Integer        | Auth.User.id                   |
    | default_tester    | Intege         | Auth.User.id                   |
    | summary           | String         |                                |
    | estimated_time    | TimeDelta      | 2h30m30s(recommend) or HH:MM:SS|
    | product_version   | Integer        |                                |
    | plan_text_version | Integer        |                                |
    | notes             | String         |                                |
    | status            | Integer        | 0:RUNNING 1:FINISHED           |
    +-------------------+----------------+ -------------------------------+
    Returns:     Hash: The updated test run object.

    Example:
    # Update status to finished for run 1193 and 1194
    >>> TestRun.update([1193, 1194], {'status': 1})
    """
    from datetime import datetime
    from tcms.core import forms
    from tcms.testruns.forms import XMLRPCUpdateRunForm

    if (values.get('product_version') and not values.get('product')):
        raise ValueError('Field "product" is required by product_version')

    if values.get('estimated_time'):
        values['estimated_time'] = pre_process_estimated_time(
            values.get('estimated_time'))

    form = XMLRPCUpdateRunForm(values)
    if values.get('product_version'):
        form.populate(product_id=values['product'])

    if form.is_valid():
        trs = TestRun.objects.filter(pk__in=pre_process_ids(value=run_ids))
        _values = dict()
        if form.cleaned_data['plan']:
            _values['plan'] = form.cleaned_data['plan']

        if form.cleaned_data['build']:
            _values['build'] = form.cleaned_data['build']

        if form.cleaned_data['manager']:
            _values['manager'] = form.cleaned_data['manager']

        if 'default_tester' in values:
            if values.get('default_tester') and \
                    form.cleaned_data['default_tester']:
                _values['default_tester'] = form.cleaned_data['default_tester']
            else:
                _values['default_tester'] = None

        if form.cleaned_data['summary']:
            _values['summary'] = form.cleaned_data['summary']

        if values.get('estimated_time') is not None:
            _values['estimated_time'] = form.cleaned_data['estimated_time']

        if form.cleaned_data['product_version']:
            _values['product_version'] = form.cleaned_data['product_version']

        if 'notes' in values:
            if values['notes'] in (None, ''):
                _values['notes'] = values['notes']
            if form.cleaned_data['notes']:
                _values['notes'] = form.cleaned_data['notes']

        if form.cleaned_data['plan_text_version']:
            _values['plan_text_version'] = form.cleaned_data[
                'plan_text_version']

        if isinstance(form.cleaned_data['status'], int):
            if form.cleaned_data['status']:
                _values['stop_date'] = datetime.now()
            else:
                _values['stop_date'] = None

        trs.update(**_values)
    else:
        raise ValueError(forms.errors_to_list(form))

    query = {'pk__in': trs.values_list('pk', flat=True)}
    return TestRun.to_xmlrpc(query)