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_result_gathering(self, job):
     48         self.afe.get_jobs(id=job.id, finished=True).AndReturn(job)
     49         self.expect_yield_job_entries(job)
     50 
     51 
     52     def expect_yield_job_entries(self, job):
     53         entries = [s.entry for s in job.statuses]
     54         self.afe.run('get_host_queue_entries',
     55                      job=job.id).AndReturn(entries)
     56         if True not in map(lambda e: 'aborted' in e and e['aborted'], entries):
     57             self.tko.get_job_test_statuses_from_db(job.id).AndReturn(
     58                     job.statuses)
     59 
     60 
     61     def testWaitForResults(self):
     62         """Should gather status and return records for job summaries."""
     63         jobs = [FakeJob(0, [FakeStatus('GOOD', 'T0', ''),
     64                             FakeStatus('GOOD', 'T1', '')]),
     65                 FakeJob(1, [FakeStatus('ERROR', 'T0', 'err', False),
     66                             FakeStatus('GOOD', 'T1', '')]),
     67                 FakeJob(2, [FakeStatus('TEST_NA', 'T0', 'no')]),
     68                 FakeJob(3, [FakeStatus('FAIL', 'T0', 'broken')]),
     69                 FakeJob(4, [FakeStatus('ERROR', 'SERVER_JOB', 'server error'),
     70                             FakeStatus('GOOD', 'T0', '')]),]
     71 
     72                 # TODO: Write a better test for the case where we yield
     73                 # results for aborts vs cannot yield results because of
     74                 # a premature abort. Currently almost all client aborts
     75                 # have been converted to failures, and when aborts do happen
     76                 # they result in server job failures for which we always
     77                 # want results.
     78                 # FakeJob(5, [FakeStatus('ERROR', 'T0', 'gah', True)]),
     79                 # The next job shouldn't be recorded in the results.
     80                 # FakeJob(6, [FakeStatus('GOOD', 'SERVER_JOB', '')])]
     81 
     82         for status in jobs[4].statuses:
     83             status.entry['job'] = {'name': 'broken_infra_job'}
     84 
     85         # To simulate a job that isn't ready the first time we check.
     86         self.afe.get_jobs(id=jobs[0].id, finished=True).AndReturn([])
     87         # Expect all the rest of the jobs to be good to go the first time.
     88         for job in jobs[1:]:
     89             self.expect_result_gathering(job)
     90         # Then, expect job[0] to be ready.
     91         self.expect_result_gathering(jobs[0])
     92         # Expect us to poll twice.
     93         self.mox.StubOutWithMock(time, 'sleep')
     94         time.sleep(mox.IgnoreArg())
     95         time.sleep(mox.IgnoreArg())
     96         self.mox.ReplayAll()
     97 
     98         results = [result for result in job_status.wait_for_results(self.afe,
     99                                                                     self.tko,
    100                                                                     jobs)]
    101         for job in jobs[:6]:  # the 'GOOD' SERVER_JOB shouldn't be there.
    102             for status in job.statuses:
    103                 self.assertTrue(True in map(status.equals_record, results))
    104 
    105 
    106     def testWaitForChildResults(self):
    107         """Should gather status and return records for job summaries."""
    108         parent_job_id = 54321
    109         jobs = [FakeJob(0, [FakeStatus('GOOD', 'T0', ''),
    110                             FakeStatus('GOOD', 'T1', '')],
    111                         parent_job_id=parent_job_id),
    112                 FakeJob(1, [FakeStatus('ERROR', 'T0', 'err', False),
    113                             FakeStatus('GOOD', 'T1', '')],
    114                         parent_job_id=parent_job_id),
    115                 FakeJob(2, [FakeStatus('TEST_NA', 'T0', 'no')],
    116                         parent_job_id=parent_job_id),
    117                 FakeJob(3, [FakeStatus('FAIL', 'T0', 'broken')],
    118                         parent_job_id=parent_job_id),
    119                 FakeJob(4, [FakeStatus('ERROR', 'SERVER_JOB', 'server error'),
    120                             FakeStatus('GOOD', 'T0', '')],
    121                         parent_job_id=parent_job_id),]
    122 
    123                 # TODO: Write a better test for the case where we yield
    124                 # results for aborts vs cannot yield results because of
    125                 # a premature abort. Currently almost all client aborts
    126                 # have been converted to failures and when aborts do happen
    127                 # they result in server job failures for which we always
    128                 # want results.
    129                 #FakeJob(5, [FakeStatus('ERROR', 'T0', 'gah', True)],
    130                 #        parent_job_id=parent_job_id),
    131                 # The next job shouldn't be recorded in the results.
    132                 #FakeJob(6, [FakeStatus('GOOD', 'SERVER_JOB', '')],
    133                 #        parent_job_id=12345)]
    134         for status in jobs[4].statuses:
    135             status.entry['job'] = {'name': 'broken_infra_job'}
    136 
    137         # Expect one call to get a list of all child jobs.
    138         self.afe.get_jobs(parent_job_id=parent_job_id).AndReturn(jobs[:6])
    139 
    140         job_id_set = set([job.id for job in jobs])
    141         yield_values = [
    142                 [jobs[1]],
    143                 [jobs[0], jobs[2]],
    144                 jobs[3:6]
    145             ]
    146         self.mox.StubOutWithMock(time, 'sleep')
    147         for yield_this in yield_values:
    148             self.afe.get_jobs(id__in=list(job_id_set),
    149                               finished=True).AndReturn(yield_this)
    150             for job in yield_this:
    151                 self.expect_yield_job_entries(job)
    152                 job_id_set.remove(job.id)
    153             time.sleep(mox.IgnoreArg())
    154         self.mox.ReplayAll()
    155 
    156         results = [result for result in job_status.wait_for_child_results(
    157                                                 self.afe,
    158                                                 self.tko,
    159                                                 parent_job_id)]
    160         for job in jobs[:6]:  # the 'GOOD' SERVER_JOB shouldn't be there.
    161             for status in job.statuses:
    162                 self.assertTrue(True in map(status.equals_record, results))
    163 
    164 
    165     def testYieldSubdir(self):
    166         """Make sure subdir are properly set for test and non-test status."""
    167         job_tag = '0-owner/172.33.44.55'
    168         job_name = 'broken_infra_job'
    169         job = FakeJob(0, [FakeStatus('ERROR', 'SERVER_JOB', 'server error',
    170                                      subdir='---', job_tag=job_tag),
    171                           FakeStatus('GOOD', 'T0', '',
    172                                      subdir='T0.subdir', job_tag=job_tag)],
    173                       parent_job_id=54321)
    174         for status in job.statuses:
    175             status.entry['job'] = {'name': job_name}
    176         self.expect_yield_job_entries(job)
    177         self.mox.ReplayAll()
    178         results = list(job_status._yield_job_results(self.afe, self.tko, job))
    179         for i in range(len(results)):
    180             result = results[i]
    181             if result.test_name.endswith('SERVER_JOB'):
    182                 expected_name = '%s_%s' % (job_name, job.statuses[i].test_name)
    183                 expected_subdir = job_tag
    184             else:
    185                 expected_name = job.statuses[i].test_name
    186                 expected_subdir = os.path.join(job_tag, job.statuses[i].subdir)
    187             self.assertEqual(results[i].test_name, expected_name)
    188             self.assertEqual(results[i].subdir, expected_subdir)
    189 
    190 
    191     def _prepareForReporting(self, results):
    192         def callable(x):
    193             pass
    194 
    195         record_entity = self.mox.CreateMock(callable)
    196         group = self.mox.CreateMock(host_spec.HostGroup)
    197 
    198         statuses = {}
    199         all_bad = True not in results.itervalues()
    200         for hostname, result in results.iteritems():
    201             status = self.mox.CreateMock(job_status.Status)
    202             status.record_all(record_entity).InAnyOrder('recording')
    203             status.is_good().InAnyOrder('recording').AndReturn(result)
    204             if not result:
    205                 status.test_name = 'test'
    206                 if not all_bad:
    207                     status.override_status('WARN').InAnyOrder('recording')
    208             else:
    209                 group.mark_host_success(hostname).InAnyOrder('recording')
    210             statuses[hostname] = status
    211 
    212         return (statuses, group, record_entity)
    213 
    214 
    215 if __name__ == '__main__':
    216     unittest.main()
    217