Home | History | Annotate | Download | only in tko
      1 #!/usr/bin/python
      2 
      3 import re, unittest
      4 import common
      5 from autotest_lib.frontend import setup_django_environment
      6 from autotest_lib.frontend import setup_test_environment
      7 from autotest_lib.client.common_lib.test_utils import mock
      8 from django.db import connection
      9 from autotest_lib.frontend.tko import models, rpc_interface
     10 
     11 # this will need to be updated when the view changes for the test to be
     12 # consistent with reality
     13 _CREATE_TEST_VIEW = """
     14 CREATE VIEW tko_test_view_2 AS
     15 SELECT  tko_tests.test_idx AS test_idx,
     16         tko_tests.job_idx AS job_idx,
     17         tko_tests.test AS test_name,
     18         tko_tests.subdir AS subdir,
     19         tko_tests.kernel_idx AS kernel_idx,
     20         tko_tests.status AS status_idx,
     21         tko_tests.reason AS reason,
     22         tko_tests.machine_idx AS machine_idx,
     23         tko_tests.invalid AS invalid,
     24         tko_tests.invalidates_test_idx AS invalidates_test_idx,
     25         tko_tests.started_time AS test_started_time,
     26         tko_tests.finished_time AS test_finished_time,
     27         tko_jobs.tag AS job_tag,
     28         tko_jobs.label AS job_name,
     29         tko_jobs.username AS job_owner,
     30         tko_jobs.queued_time AS job_queued_time,
     31         tko_jobs.started_time AS job_started_time,
     32         tko_jobs.finished_time AS job_finished_time,
     33         tko_jobs.afe_job_id AS afe_job_id,
     34         tko_machines.hostname AS hostname,
     35         tko_machines.machine_group AS platform,
     36         tko_machines.owner AS machine_owner,
     37         tko_kernels.kernel_hash AS kernel_hash,
     38         tko_kernels.base AS kernel_base,
     39         tko_kernels.printable AS kernel,
     40         tko_status.word AS status
     41 FROM tko_tests
     42 INNER JOIN tko_jobs ON tko_jobs.job_idx = tko_tests.job_idx
     43 INNER JOIN tko_machines ON tko_machines.machine_idx = tko_jobs.machine_idx
     44 INNER JOIN tko_kernels ON tko_kernels.kernel_idx = tko_tests.kernel_idx
     45 INNER JOIN tko_status ON tko_status.status_idx = tko_tests.status;
     46 """
     47 
     48 # this will need to be updated if the table schemas change (or removed if we
     49 # add proper primary keys)
     50 _CREATE_ITERATION_ATTRIBUTES = """
     51 CREATE TABLE "tko_iteration_attributes" (
     52     "test_idx" integer NOT NULL REFERENCES "tko_tests" ("test_idx"),
     53     "iteration" integer NOT NULL,
     54     "attribute" varchar(90) NOT NULL,
     55     "value" varchar(300) NOT NULL
     56 );
     57 """
     58 
     59 _CREATE_ITERATION_RESULTS = """
     60 CREATE TABLE "tko_iteration_result" (
     61     "test_idx" integer NOT NULL REFERENCES "tko_tests" ("test_idx"),
     62     "iteration" integer NOT NULL,
     63     "attribute" varchar(90) NOT NULL,
     64     "value" numeric(12, 31) NULL
     65 );
     66 """
     67 
     68 
     69 def setup_test_view():
     70     """
     71     Django has no way to actually represent a view; we simply create a model for
     72     TestView.   This means when we syncdb, Django will create a table for it.
     73     So manually remove that table and replace it with a view.
     74     """
     75     cursor = connection.cursor()
     76     cursor.execute('DROP TABLE tko_test_view_2')
     77     cursor.execute(_CREATE_TEST_VIEW)
     78 
     79 
     80 def fix_iteration_tables():
     81     """
     82     Since iteration tables don't have any real primary key, we "fake" one in the
     83     Django models.  So fix up the generated schema to match the real schema.
     84     """
     85     cursor = connection.cursor()
     86     cursor.execute('DROP TABLE tko_iteration_attributes')
     87     cursor.execute(_CREATE_ITERATION_ATTRIBUTES)
     88     cursor.execute('DROP TABLE tko_iteration_result')
     89     cursor.execute(_CREATE_ITERATION_RESULTS)
     90 
     91 
     92 class TkoTestMixin(object):
     93     def _patch_sqlite_stuff(self):
     94         self.god.stub_with(models.TempManager, '_get_column_names',
     95                            self._get_column_names_for_sqlite3)
     96         self.god.stub_with(models.TempManager, '_cursor_rowcount',
     97                            self._cursor_rowcount_for_sqlite3)
     98 
     99         # add some functions to SQLite for MySQL compatibility
    100         connection.cursor() # ensure connection is alive
    101         connection.connection.create_function('if', 3, self._sqlite_if)
    102         connection.connection.create_function('find_in_set', 2,
    103                                               self._sqlite_find_in_set)
    104 
    105         fix_iteration_tables()
    106 
    107 
    108     def _cursor_rowcount_for_sqlite3(self, cursor):
    109         return len(cursor.fetchall())
    110 
    111 
    112     def _sqlite_find_in_set(self, needle, haystack):
    113         return needle in haystack.split(',')
    114 
    115 
    116     def _sqlite_if(self, condition, true_result, false_result):
    117         if condition:
    118             return true_result
    119         return false_result
    120 
    121 
    122     # sqlite takes any columns that don't have aliases and names them
    123     # "table_name"."column_name".  we map these to just column_name.
    124     _SQLITE_AUTO_COLUMN_ALIAS_RE = re.compile(r'".+"\."(.+)"')
    125 
    126 
    127     def _get_column_names_for_sqlite3(self, cursor):
    128         names = [column_info[0] for column_info in cursor.description]
    129 
    130         # replace all "table_name"."column_name" constructs with just
    131         # column_name
    132         for i, name in enumerate(names):
    133             match = self._SQLITE_AUTO_COLUMN_ALIAS_RE.match(name)
    134             if match:
    135                 names[i] = match.group(1)
    136 
    137         return names
    138 
    139 
    140     def _create_initial_data(self):
    141         machine = models.Machine.objects.create(hostname='myhost')
    142 
    143         # create basic objects
    144         kernel_name = 'mykernel1'
    145         kernel1 = models.Kernel.objects.create(kernel_hash=kernel_name,
    146                                                base=kernel_name,
    147                                                printable=kernel_name)
    148 
    149         kernel_name = 'mykernel2'
    150         kernel2 = models.Kernel.objects.create(kernel_hash=kernel_name,
    151                                                base=kernel_name,
    152                                                printable=kernel_name)
    153 
    154         good_status = models.Status.objects.create(word='GOOD')
    155         failed_status = models.Status.objects.create(word='FAILED')
    156 
    157         job1 = models.Job.objects.create(tag='1-myjobtag1', label='myjob1',
    158                                          username='myuser', machine=machine,
    159                                          afe_job_id=1)
    160         job2 = models.Job.objects.create(tag='2-myjobtag2', label='myjob2',
    161                                          username='myuser', machine=machine,
    162                                          afe_job_id=2)
    163 
    164         job1_test1 = models.Test.objects.create(job=job1, test='mytest1',
    165                                                 kernel=kernel1,
    166                                                 status=good_status,
    167                                                 machine=machine)
    168         self.first_test = job1_test1
    169         job1_test2 = models.Test.objects.create(job=job1, test='mytest2',
    170                                                 kernel=kernel1,
    171                                                 status=failed_status,
    172                                                 machine=machine)
    173         job2_test1 = models.Test.objects.create(job=job2, test='kernbench',
    174                                                 kernel=kernel2,
    175                                                 status=good_status,
    176                                                 machine=machine)
    177 
    178         job1.jobkeyval_set.create(key='keyval_key', value='keyval_value')
    179 
    180         # create test attributes, test labels, and iterations
    181         # like Noah's Ark, include two of each...just in case there's a bug with
    182         # multiple related items
    183         models.TestAttribute.objects.create(test=job1_test1, attribute='myattr',
    184                                             value='myval')
    185         models.TestAttribute.objects.create(test=job1_test1,
    186                                             attribute='myattr2', value='myval2')
    187 
    188         self._add_iteration_keyval('tko_iteration_attributes', test=job1_test1,
    189                                    iteration=1, attribute='iattr',
    190                                    value='ival')
    191         self._add_iteration_keyval('tko_iteration_attributes', test=job1_test1,
    192                                    iteration=1, attribute='iattr2',
    193                                    value='ival2')
    194         self._add_iteration_keyval('tko_iteration_result', test=job1_test1,
    195                                    iteration=1, attribute='iresult', value=1)
    196         self._add_iteration_keyval('tko_iteration_result', test=job1_test1,
    197                                    iteration=1, attribute='iresult2', value=2)
    198         self._add_iteration_keyval('tko_iteration_result', test=job1_test1,
    199                                    iteration=2, attribute='iresult', value=3)
    200         self._add_iteration_keyval('tko_iteration_result', test=job1_test1,
    201                                    iteration=2, attribute='iresult2', value=4)
    202 
    203         label1 = models.TestLabel.objects.create(name='testlabel1')
    204         label2 = models.TestLabel.objects.create(name='testlabel2')
    205 
    206         label1.tests.add(job1_test1)
    207         label2.tests.add(job1_test1)
    208 
    209 
    210     def _add_iteration_keyval(self, table, test, iteration, attribute, value):
    211         cursor = connection.cursor()
    212         cursor.execute('INSERT INTO %s ' 'VALUES (%%s, %%s, %%s, %%s)' % table,
    213                        (test.test_idx, iteration, attribute, value))
    214 
    215 
    216 class RpcInterfaceTest(unittest.TestCase, TkoTestMixin):
    217     def setUp(self):
    218         self.god = mock.mock_god()
    219 
    220         setup_test_environment.set_up()
    221         self._patch_sqlite_stuff()
    222         setup_test_view()
    223         self._create_initial_data()
    224 
    225 
    226     def tearDown(self):
    227         setup_test_environment.tear_down()
    228         self.god.unstub_all()
    229 
    230 
    231     def _check_for_get_test_views(self, test):
    232         self.assertEquals(test['test_name'], 'mytest1')
    233         self.assertEquals(test['job_tag'], '1-myjobtag1')
    234         self.assertEquals(test['job_name'], 'myjob1')
    235         self.assertEquals(test['job_owner'], 'myuser')
    236         self.assertEquals(test['status'], 'GOOD')
    237         self.assertEquals(test['hostname'], 'myhost')
    238         self.assertEquals(test['kernel'], 'mykernel1')
    239 
    240 
    241     def test_get_detailed_test_views(self):
    242         test = rpc_interface.get_detailed_test_views()[0]
    243 
    244         self._check_for_get_test_views(test)
    245 
    246         self.assertEquals(test['attributes'], {'myattr': 'myval',
    247                                                'myattr2': 'myval2'})
    248         self.assertEquals(test['iterations'], [{'attr': {'iattr': 'ival',
    249                                                          'iattr2': 'ival2'},
    250                                                 'perf': {'iresult': 1,
    251                                                          'iresult2': 2}},
    252                                                {'attr': {},
    253                                                 'perf': {'iresult': 3,
    254                                                          'iresult2': 4}}])
    255         self.assertEquals(test['labels'], ['testlabel1', 'testlabel2'])
    256         self.assertEquals(test['job_keyvals'], {'keyval_key': 'keyval_value'})
    257 
    258 
    259     def test_test_attributes(self):
    260         rpc_interface.set_test_attribute('foo', 'bar', test_name='mytest1')
    261         test = rpc_interface.get_detailed_test_views()[0]
    262         self.assertEquals(test['attributes'], {'foo': 'bar',
    263                                                'myattr': 'myval',
    264                                                'myattr2': 'myval2'})
    265 
    266         rpc_interface.set_test_attribute('foo', 'goo', test_name='mytest1')
    267         test = rpc_interface.get_detailed_test_views()[0]
    268         self.assertEquals(test['attributes'], {'foo': 'goo',
    269                                                'myattr': 'myval',
    270                                                'myattr2': 'myval2'})
    271 
    272         rpc_interface.set_test_attribute('foo', None, test_name='mytest1')
    273         test = rpc_interface.get_detailed_test_views()[0]
    274         self.assertEquals(test['attributes'], {'myattr': 'myval',
    275                                                'myattr2': 'myval2'})
    276 
    277 
    278     def test_immutable_attributes(self):
    279         self.assertRaises(ValueError, rpc_interface.set_test_attribute,
    280                           'myattr', 'foo', test_name='mytest1')
    281 
    282 
    283     def test_get_test_views(self):
    284         tests = rpc_interface.get_test_views()
    285 
    286         self.assertEquals(len(tests), 3)
    287         test = rpc_interface.get_test_views(
    288             job_name='myjob1', test_name='mytest1')[0]
    289         self.assertEquals(tests[0], test)
    290 
    291         self._check_for_get_test_views(test)
    292 
    293         self.assertEquals(
    294             [], rpc_interface.get_test_views(hostname='fakehost'))
    295 
    296 
    297     def _check_test_names(self, tests, expected_names):
    298         self.assertEquals(set(test['test_name'] for test in tests),
    299                           set(expected_names))
    300 
    301 
    302     def test_get_test_views_filter_on_labels(self):
    303         tests = rpc_interface.get_test_views(include_labels=['testlabel1'])
    304         self._check_test_names(tests, ['mytest1'])
    305 
    306         tests = rpc_interface.get_test_views(exclude_labels=['testlabel1'])
    307         self._check_test_names(tests, ['mytest2', 'kernbench'])
    308 
    309 
    310     def test_get_test_views_filter_on_attributes(self):
    311         tests = rpc_interface.get_test_views(
    312                 include_attributes_where='attribute = "myattr" '
    313                                          'and value = "myval"')
    314         self._check_test_names(tests, ['mytest1'])
    315 
    316         tests = rpc_interface.get_test_views(
    317                 exclude_attributes_where='attribute="myattr2"')
    318         self._check_test_names(tests, ['mytest2', 'kernbench'])
    319 
    320 
    321     def test_get_num_test_views(self):
    322         self.assertEquals(rpc_interface.get_num_test_views(), 3)
    323         self.assertEquals(rpc_interface.get_num_test_views(
    324             job_name='myjob1', test_name='mytest1'), 1)
    325 
    326 
    327     def test_get_group_counts(self):
    328         self.assertEquals(rpc_interface.get_num_groups(['job_name']), 2)
    329 
    330         counts = rpc_interface.get_group_counts(['job_name'])
    331         groups = counts['groups']
    332         self.assertEquals(len(groups), 2)
    333         group1, group2 = groups
    334 
    335         self.assertEquals(group1['group_count'], 2)
    336         self.assertEquals(group1['job_name'], 'myjob1')
    337         self.assertEquals(group2['group_count'], 1)
    338         self.assertEquals(group2['job_name'], 'myjob2')
    339 
    340         extra = {'extra' : 'kernel_hash'}
    341         counts = rpc_interface.get_group_counts(['job_name'],
    342                                                 header_groups=[('job_name',)],
    343                                                 extra_select_fields=extra)
    344         groups = counts['groups']
    345         self.assertEquals(len(groups), 2)
    346         group1, group2 = groups
    347 
    348         self.assertEquals(group1['group_count'], 2)
    349         self.assertEquals(group1['header_indices'], [0])
    350         self.assertEquals(group1['extra'], 'mykernel1')
    351         self.assertEquals(group2['group_count'], 1)
    352         self.assertEquals(group2['header_indices'], [1])
    353         self.assertEquals(group2['extra'], 'mykernel2')
    354 
    355 
    356     def test_get_status_counts(self):
    357         counts = rpc_interface.get_status_counts(group_by=['job_name'])
    358         group1, group2 = counts['groups']
    359         self.assertEquals(group1['pass_count'], 1)
    360         self.assertEquals(group1['complete_count'], 2)
    361         self.assertEquals(group1['incomplete_count'], 0)
    362         self.assertEquals(group2['pass_count'], 1)
    363         self.assertEquals(group2['complete_count'], 1)
    364         self.assertEquals(group2['incomplete_count'], 0)
    365 
    366 
    367     def test_get_latest_tests(self):
    368         counts = rpc_interface.get_latest_tests(group_by=['job_name'])
    369         group1, group2 = counts['groups']
    370         self.assertEquals(group1['pass_count'], 0)
    371         self.assertEquals(group1['complete_count'], 1)
    372         self.assertEquals(group1['test_idx'], 2)
    373         self.assertEquals(group2['test_idx'], 3)
    374 
    375 
    376     def test_get_latest_tests_extra_info(self):
    377         counts = rpc_interface.get_latest_tests(group_by=['job_name'],
    378                                                 extra_info=['job_tag'])
    379         group1, group2 = counts['groups']
    380         self.assertEquals(group1['extra_info'], ['1-myjobtag1'])
    381         self.assertEquals(group2['extra_info'], ['2-myjobtag2'])
    382 
    383 
    384     def test_get_job_ids(self):
    385         self.assertEquals([1,2], rpc_interface.get_job_ids())
    386         self.assertEquals([1], rpc_interface.get_job_ids(test_name='mytest2'))
    387 
    388 
    389     def test_get_hosts_and_tests(self):
    390         host_info = rpc_interface.get_hosts_and_tests()
    391         self.assertEquals(len(host_info), 1)
    392         info = host_info['myhost']
    393 
    394         self.assertEquals(info['tests'], ['kernbench'])
    395         self.assertEquals(info['id'], 1)
    396 
    397 
    398     def _check_for_get_test_labels(self, label, label_num):
    399         self.assertEquals(label['id'], label_num)
    400         self.assertEquals(label['description'], '')
    401         self.assertEquals(label['name'], 'testlabel%d' % label_num)
    402 
    403 
    404     def test_test_labels(self):
    405         labels = rpc_interface.get_test_labels_for_tests(test_name='mytest1')
    406         self.assertEquals(len(labels), 2)
    407         label1 = labels[0]
    408         label2 = labels[1]
    409 
    410         self._check_for_get_test_labels(label1, 1)
    411         self._check_for_get_test_labels(label2, 2)
    412 
    413         rpc_interface.test_label_remove_tests(label1['id'], test_name='mytest1')
    414 
    415         labels = rpc_interface.get_test_labels_for_tests(test_name='mytest1')
    416         self.assertEquals(len(labels), 1)
    417         label = labels[0]
    418 
    419         self._check_for_get_test_labels(label, 2)
    420 
    421         rpc_interface.test_label_add_tests(label1['id'], test_name='mytest1')
    422 
    423         labels = rpc_interface.get_test_labels_for_tests(test_name='mytest1')
    424         self.assertEquals(len(labels), 2)
    425         label1 = labels[0]
    426         label2 = labels[1]
    427 
    428         self._check_for_get_test_labels(label1, 1)
    429         self._check_for_get_test_labels(label2, 2)
    430 
    431 
    432     def test_get_test_attribute_fields(self):
    433         tests = rpc_interface.get_test_views(
    434                 test_attribute_fields=['myattr', 'myattr2'])
    435         self.assertEquals(len(tests), 3)
    436 
    437         self.assertEquals(tests[0]['test_attribute_myattr'], 'myval')
    438         self.assertEquals(tests[0]['test_attribute_myattr2'], 'myval2')
    439 
    440         for index in (1, 2):
    441             self.assertEquals(tests[index]['test_attribute_myattr'], None)
    442             self.assertEquals(tests[index]['test_attribute_myattr2'], None)
    443 
    444 
    445     def test_filtering_on_test_attribute_fields(self):
    446         tests = rpc_interface.get_test_views(
    447                 extra_where='test_attribute_myattr.value = "myval"',
    448                 test_attribute_fields=['myattr'])
    449         self.assertEquals(len(tests), 1)
    450 
    451 
    452     def test_grouping_with_test_attribute_fields(self):
    453         num_groups = rpc_interface.get_num_groups(
    454                 ['test_attribute_myattr'], test_attribute_fields=['myattr'])
    455         self.assertEquals(num_groups, 2)
    456 
    457         counts = rpc_interface.get_group_counts(
    458                 ['test_attribute_myattr'], test_attribute_fields=['myattr'])
    459         groups = counts['groups']
    460         self.assertEquals(len(groups), num_groups)
    461         self.assertEquals(groups[0]['test_attribute_myattr'], None)
    462         self.assertEquals(groups[0]['group_count'], 2)
    463         self.assertEquals(groups[1]['test_attribute_myattr'], 'myval')
    464         self.assertEquals(groups[1]['group_count'], 1)
    465 
    466 
    467     def test_extra_info_test_attributes(self):
    468         counts = rpc_interface.get_latest_tests(
    469                 group_by=['test_idx'], extra_info=['test_attribute_myattr'],
    470                 test_attribute_fields=['myattr'])
    471         group1 = counts['groups'][0]
    472         self.assertEquals(group1['extra_info'], ['myval'])
    473 
    474 
    475     def test_get_test_label_fields(self):
    476         tests = rpc_interface.get_test_views(
    477                 test_label_fields=['testlabel1', 'testlabel2'])
    478         self.assertEquals(len(tests), 3)
    479 
    480         self.assertEquals(tests[0]['test_label_testlabel1'], 'testlabel1')
    481         self.assertEquals(tests[0]['test_label_testlabel2'], 'testlabel2')
    482 
    483         for index in (1, 2):
    484             self.assertEquals(tests[index]['test_label_testlabel1'], None)
    485             self.assertEquals(tests[index]['test_label_testlabel2'], None)
    486 
    487 
    488     def test_filtering_on_test_label_fields(self):
    489         tests = rpc_interface.get_test_views(
    490                 extra_where='test_label_testlabel1 = "testlabel1"',
    491                 test_label_fields=['testlabel1'])
    492         self.assertEquals(len(tests), 1)
    493 
    494 
    495     def test_grouping_on_test_label_fields(self):
    496         num_groups = rpc_interface.get_num_groups(
    497                 ['test_label_testlabel1'], test_label_fields=['testlabel1'])
    498         self.assertEquals(num_groups, 2)
    499 
    500         counts = rpc_interface.get_group_counts(
    501                 ['test_label_testlabel1'], test_label_fields=['testlabel1'])
    502         groups = counts['groups']
    503         self.assertEquals(len(groups), 2)
    504         self.assertEquals(groups[0]['test_label_testlabel1'], None)
    505         self.assertEquals(groups[0]['group_count'], 2)
    506         self.assertEquals(groups[1]['test_label_testlabel1'], 'testlabel1')
    507         self.assertEquals(groups[1]['group_count'], 1)
    508 
    509 
    510     def test_get_iteration_result_fields(self):
    511         num_iterations = rpc_interface.get_num_test_views(
    512                 iteration_result_fields=['iresult', 'iresult2'])
    513         self.assertEquals(num_iterations, 2)
    514 
    515         iterations = rpc_interface.get_test_views(
    516                 iteration_result_fields=['iresult', 'iresult2'])
    517         self.assertEquals(len(iterations), 2)
    518 
    519         for index in (0, 1):
    520             self.assertEquals(iterations[index]['test_idx'], 1)
    521 
    522         self.assertEquals(iterations[0]['iteration_index'], 1)
    523         self.assertEquals(iterations[0]['iteration_result_iresult'], 1)
    524         self.assertEquals(iterations[0]['iteration_result_iresult2'], 2)
    525 
    526         self.assertEquals(iterations[1]['iteration_index'], 2)
    527         self.assertEquals(iterations[1]['iteration_result_iresult'], 3)
    528         self.assertEquals(iterations[1]['iteration_result_iresult2'], 4)
    529 
    530 
    531     def test_filtering_on_iteration_result_fields(self):
    532         iterations = rpc_interface.get_test_views(
    533                 extra_where='iteration_result_iresult.value = 1',
    534                 iteration_result_fields=['iresult'])
    535         self.assertEquals(len(iterations), 1)
    536 
    537 
    538     def test_grouping_with_iteration_result_fields(self):
    539         num_groups = rpc_interface.get_num_groups(
    540                 ['iteration_result_iresult'],
    541                 iteration_result_fields=['iresult'])
    542         self.assertEquals(num_groups, 2)
    543 
    544         counts = rpc_interface.get_group_counts(
    545                 ['iteration_result_iresult'],
    546                 iteration_result_fields=['iresult'])
    547         groups = counts['groups']
    548         self.assertEquals(len(groups), 2)
    549         self.assertEquals(groups[0]['iteration_result_iresult'], 1)
    550         self.assertEquals(groups[0]['group_count'], 1)
    551         self.assertEquals(groups[1]['iteration_result_iresult'], 3)
    552         self.assertEquals(groups[1]['group_count'], 1)
    553 
    554 
    555     def _setup_machine_labels(self):
    556         models.TestAttribute.objects.create(test=self.first_test,
    557                                             attribute='host-labels',
    558                                             value='label1,label2')
    559 
    560 
    561     def test_get_machine_label_fields(self):
    562         self._setup_machine_labels()
    563 
    564         tests = rpc_interface.get_test_views(
    565                 machine_label_fields=['label1', 'otherlabel'])
    566         self.assertEquals(len(tests), 3)
    567 
    568         self.assertEquals(tests[0]['machine_label_label1'], 'label1')
    569         self.assertEquals(tests[0]['machine_label_otherlabel'], None)
    570 
    571         for index in (1, 2):
    572             self.assertEquals(tests[index]['machine_label_label1'], None)
    573             self.assertEquals(tests[index]['machine_label_otherlabel'], None)
    574 
    575 
    576     def test_grouping_with_machine_label_fields(self):
    577         self._setup_machine_labels()
    578 
    579         counts = rpc_interface.get_group_counts(['machine_label_label1'],
    580                                                 machine_label_fields=['label1'])
    581         groups = counts['groups']
    582         self.assertEquals(len(groups), 2)
    583         self.assertEquals(groups[0]['machine_label_label1'], None)
    584         self.assertEquals(groups[0]['group_count'], 2)
    585         self.assertEquals(groups[1]['machine_label_label1'], 'label1')
    586         self.assertEquals(groups[1]['group_count'], 1)
    587 
    588 
    589     def test_filtering_on_machine_label_fields(self):
    590         self._setup_machine_labels()
    591 
    592         tests = rpc_interface.get_test_views(
    593                 extra_where='machine_label_label1 = "label1"',
    594                 machine_label_fields=['label1'])
    595         self.assertEquals(len(tests), 1)
    596 
    597 
    598     def test_quoting_fields(self):
    599         # ensure fields with special characters are properly quoted throughout
    600         rpc_interface.add_test_label('hyphen-label')
    601         rpc_interface.get_group_counts(
    602                 ['test_attribute_hyphen-attr', 'test_label_hyphen-label',
    603                  'machine_label_hyphen-label',
    604                  'iteration_result_hyphen-result'],
    605                 test_attribute_fields=['hyphen-attr'],
    606                 test_label_fields=['hyphen-label'],
    607                 machine_label_fields=['hyphen-label'],
    608                 iteration_result_fields=['hyphen-result'])
    609 
    610 
    611 if __name__ == '__main__':
    612     unittest.main()
    613