Home | History | Annotate | Download | only in dynamic_suite
      1 #!/usr/bin/env python2
      2 #
      3 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 
      7 # pylint: disable-msg=C0111
      8 
      9 """Unit tests for server/cros/dynamic_suite/job_status.py."""
     10 
     11 import mox
     12 import shutil
     13 import tempfile
     14 import time
     15 import unittest
     16 import os
     17 import common
     18 
     19 from autotest_lib.server import frontend
     20 from autotest_lib.server.cros.dynamic_suite import host_spec
     21 from autotest_lib.server.cros.dynamic_suite import job_status
     22 from autotest_lib.server.cros.dynamic_suite.fakes import FakeJob
     23 from autotest_lib.server.cros.dynamic_suite.fakes import FakeStatus
     24 
     25 
     26 DEFAULT_WAITTIMEOUT_MINS = 60 * 4
     27 
     28 
     29 class StatusTest(mox.MoxTestBase):
     30     """Unit tests for job_status.Status.
     31     """
     32 
     33 
     34     def setUp(self):
     35         super(StatusTest, self).setUp()
     36         self.afe = self.mox.CreateMock(frontend.AFE)
     37         self.tko = self.mox.CreateMock(frontend.TKO)
     38 
     39         self.tmpdir = tempfile.mkdtemp(suffix=type(self).__name__)
     40 
     41 
     42     def tearDown(self):
     43         super(StatusTest, self).tearDown()
     44         shutil.rmtree(self.tmpdir, ignore_errors=True)
     45 
     46 
     47     def expect_yield_job_entries(self, job):
     48         entries = [s.entry for s in job.statuses]
     49         self.afe.run('get_host_queue_entries',
     50                      job=job.id).AndReturn(entries)
     51         if True not in map(lambda e: 'aborted' in e and e['aborted'], entries):
     52             self.tko.get_job_test_statuses_from_db(job.id).AndReturn(
     53                     job.statuses)
     54 
     55 
     56     def testWaitForResults(self):
     57         """Should gather status and return records for job summaries."""
     58         jobs = [FakeJob(0, [FakeStatus('GOOD', 'T0', ''),
     59                             FakeStatus('GOOD', 'T1', '')]),
     60                 FakeJob(1, [FakeStatus('ERROR', 'T0', 'err', False),
     61                             FakeStatus('GOOD', 'T1', '')]),
     62                 FakeJob(2, [FakeStatus('TEST_NA', 'T0', 'no')]),
     63                 FakeJob(3, [FakeStatus('FAIL', 'T0', 'broken')]),
     64                 FakeJob(4, [FakeStatus('ERROR', 'SERVER_JOB', 'server error'),
     65                             FakeStatus('GOOD', 'T0', '')]),]
     66                 # TODO: Write a better test for the case where we yield
     67                 # results for aborts vs cannot yield results because of
     68                 # a premature abort. Currently almost all client aborts
     69                 # have been converted to failures, and when aborts do happen
     70                 # they result in server job failures for which we always
     71                 # want results.
     72                 # FakeJob(5, [FakeStatus('ERROR', 'T0', 'gah', True)]),
     73                 # The next job shouldn't be recorded in the results.
     74                 # FakeJob(6, [FakeStatus('GOOD', 'SERVER_JOB', '')])]
     75         for status in jobs[4].statuses:
     76             status.entry['job'] = {'name': 'broken_infra_job'}
     77 
     78         job_id_set = set([job.id for job in jobs])
     79         yield_values = [
     80                 [jobs[1]],
     81                 [jobs[0], jobs[2]],
     82                 jobs[3:6]
     83             ]
     84         self.mox.StubOutWithMock(time, 'sleep')
     85         for yield_this in yield_values:
     86             self.afe.get_jobs(id__in=list(job_id_set),
     87                               finished=True).AndReturn(yield_this)
     88             for job in yield_this:
     89                 self.expect_yield_job_entries(job)
     90                 job_id_set.remove(job.id)
     91             time.sleep(mox.IgnoreArg())
     92         self.mox.ReplayAll()
     93 
     94         results = [result for result in job_status.wait_for_results(self.afe,
     95                                                                     self.tko,
     96                                                                     jobs)]
     97         for job in jobs[:6]:  # the 'GOOD' SERVER_JOB shouldn't be there.
     98             for status in job.statuses:
     99                 self.assertTrue(True in map(status.equals_record, results))
    100 
    101 
    102     def testWaitForChildResults(self):
    103         """Should gather status and return records for job summaries."""
    104         parent_job_id = 54321
    105         jobs = [FakeJob(0, [FakeStatus('GOOD', 'T0', ''),
    106                             FakeStatus('GOOD', 'T1', '')],
    107                         parent_job_id=parent_job_id),
    108                 FakeJob(1, [FakeStatus('ERROR', 'T0', 'err', False),
    109                             FakeStatus('GOOD', 'T1', '')],
    110                         parent_job_id=parent_job_id),
    111                 FakeJob(2, [FakeStatus('TEST_NA', 'T0', 'no')],
    112                         parent_job_id=parent_job_id),
    113                 FakeJob(3, [FakeStatus('FAIL', 'T0', 'broken')],
    114                         parent_job_id=parent_job_id),
    115                 FakeJob(4, [FakeStatus('ERROR', 'SERVER_JOB', 'server error'),
    116                             FakeStatus('GOOD', 'T0', '')],
    117                         parent_job_id=parent_job_id),]
    118                 # TODO: Write a better test for the case where we yield
    119                 # results for aborts vs cannot yield results because of
    120                 # a premature abort. Currently almost all client aborts
    121                 # have been converted to failures and when aborts do happen
    122                 # they result in server job failures for which we always
    123                 # want results.
    124                 #FakeJob(5, [FakeStatus('ERROR', 'T0', 'gah', True)],
    125                 #        parent_job_id=parent_job_id),
    126                 # The next job shouldn't be recorded in the results.
    127                 #FakeJob(6, [FakeStatus('GOOD', 'SERVER_JOB', '')],
    128                 #        parent_job_id=12345)]
    129         for status in jobs[4].statuses:
    130             status.entry['job'] = {'name': 'broken_infra_job'}
    131 
    132         # Expect one call to get a list of all child jobs.
    133         self.afe.get_jobs(parent_job_id=parent_job_id).AndReturn(jobs[:6])
    134 
    135         job_id_set = set([job.id for job in jobs])
    136         yield_values = [
    137                 [jobs[1]],
    138                 [jobs[0], jobs[2]],
    139                 jobs[3:6]
    140             ]
    141         self.mox.StubOutWithMock(time, 'sleep')
    142         for yield_this in yield_values:
    143             self.afe.get_jobs(id__in=list(job_id_set),
    144                               finished=True).AndReturn(yield_this)
    145             for job in yield_this:
    146                 self.expect_yield_job_entries(job)
    147                 job_id_set.remove(job.id)
    148             time.sleep(mox.IgnoreArg())
    149         self.mox.ReplayAll()
    150 
    151         results = [result for result in job_status.wait_for_child_results(
    152                                                 self.afe,
    153                                                 self.tko,
    154                                                 parent_job_id)]
    155         for job in jobs[:6]:  # the 'GOOD' SERVER_JOB shouldn't be there.
    156             for status in job.statuses:
    157                 self.assertTrue(True in map(status.equals_record, results))
    158 
    159 
    160     def testYieldSubdir(self):
    161         """Make sure subdir are properly set for test and non-test status."""
    162         job_tag = '0-owner/172.33.44.55'
    163         job_name = 'broken_infra_job'
    164         job = FakeJob(0, [FakeStatus('ERROR', 'SERVER_JOB', 'server error',
    165                                      subdir='---', job_tag=job_tag),
    166                           FakeStatus('GOOD', 'T0', '',
    167                                      subdir='T0.subdir', job_tag=job_tag)],
    168                       parent_job_id=54321)
    169         for status in job.statuses:
    170             status.entry['job'] = {'name': job_name}
    171         self.expect_yield_job_entries(job)
    172         self.mox.ReplayAll()
    173         results = list(job_status._yield_job_results(self.afe, self.tko, job))
    174         for i in range(len(results)):
    175             result = results[i]
    176             if result.test_name.endswith('SERVER_JOB'):
    177                 expected_name = '%s_%s' % (job_name, job.statuses[i].test_name)
    178                 expected_subdir = job_tag
    179             else:
    180                 expected_name = job.statuses[i].test_name
    181                 expected_subdir = os.path.join(job_tag, job.statuses[i].subdir)
    182             self.assertEqual(results[i].test_name, expected_name)
    183             self.assertEqual(results[i].subdir, expected_subdir)
    184 
    185 
    186     def _prepareForReporting(self, results):
    187         def callable(x):
    188             pass
    189 
    190         record_entity = self.mox.CreateMock(callable)
    191         group = self.mox.CreateMock(host_spec.HostGroup)
    192 
    193         statuses = {}
    194         all_bad = True not in results.itervalues()
    195         for hostname, result in results.iteritems():
    196             status = self.mox.CreateMock(job_status.Status)
    197             status.record_all(record_entity).InAnyOrder('recording')
    198             status.is_good().InAnyOrder('recording').AndReturn(result)
    199             if not result:
    200                 status.test_name = 'test'
    201                 if not all_bad:
    202                     status.override_status('WARN').InAnyOrder('recording')
    203             else:
    204                 group.mark_host_success(hostname).InAnyOrder('recording')
    205             statuses[hostname] = status
    206 
    207         return (statuses, group, record_entity)
    208 
    209 
    210 if __name__ == '__main__':
    211     unittest.main()
    212