#!/usr/bin/python2 # pylint: disable=missing-docstring import unittest import common from autotest_lib.client.common_lib import error from autotest_lib.frontend import setup_django_environment from autotest_lib.frontend.afe import frontend_test_utils from autotest_lib.frontend.afe import models, model_logic class AclGroupTest(unittest.TestCase, frontend_test_utils.FrontendTestMixin): def setUp(self): self._frontend_common_setup() def tearDown(self): self._frontend_common_teardown() def _check_acls(self, host, acl_name_list): actual_acl_names = [acl_group.name for acl_group in host.aclgroup_set.all()] self.assertEquals(set(actual_acl_names), set(acl_name_list)) def test_on_host_membership_change(self): host1, host2 = self.hosts[1:3] everyone_acl = models.AclGroup.objects.get(name='Everyone') host1.aclgroup_set.clear() self._check_acls(host1, []) host2.aclgroup_set.add(everyone_acl) self._check_acls(host2, ['Everyone', 'my_acl']) models.AclGroup.on_host_membership_change() self._check_acls(host1, ['Everyone']) self._check_acls(host2, ['my_acl']) class HostTest(unittest.TestCase, frontend_test_utils.FrontendTestMixin): def setUp(self): self._frontend_common_setup() def tearDown(self): self._frontend_common_teardown() def _get_attributes(self, host): models.Host.objects.populate_relationships( [host], models.HostAttribute, 'attribute_list') return dict((attribute.attribute, attribute.value) for attribute in host.attribute_list) def test_delete_attribute(self): previous_config = models.RESPECT_STATIC_ATTRIBUTES models.RESPECT_STATIC_ATTRIBUTES = False host1 = models.Host.objects.create(hostname='test_host1') host1.set_attribute('test_attribute1', 'test_value1') attributes = self._get_attributes(host1) self.assertEquals(attributes['test_attribute1'], 'test_value1') host1.set_or_delete_attribute('test_attribute1', None) attributes = self._get_attributes(host1) self.assertNotIn('test_attribute1', attributes.keys()) models.RESPECT_STATIC_ATTRIBUTES = previous_config def test_delete_static_attribute(self): previous_config = models.RESPECT_STATIC_ATTRIBUTES models.RESPECT_STATIC_ATTRIBUTES = True host1 = models.Host.objects.create(hostname='test_host1') host1.set_attribute('test_attribute1', 'test_value1') self._set_static_attribute(host1, 'test_attribute1', 'test_value1') self.assertRaises( error.UnmodifiableAttributeException, host1.set_or_delete_attribute, 'test_attribute1', None) models.RESPECT_STATIC_ATTRIBUTES = previous_config def test_set_attribute(self): previous_config = models.RESPECT_STATIC_ATTRIBUTES models.RESPECT_STATIC_ATTRIBUTES = False host1 = models.Host.objects.create(hostname='test_host1') host1.set_attribute('test_attribute1', 'test_value1') host1.set_or_delete_attribute('test_attribute1', 'test_new_value1') attributes = self._get_attributes(host1) self.assertEquals(attributes['test_attribute1'], 'test_new_value1') models.RESPECT_STATIC_ATTRIBUTES = previous_config def test_set_static_attribute(self): previous_config = models.RESPECT_STATIC_ATTRIBUTES models.RESPECT_STATIC_ATTRIBUTES = True host1 = models.Host.objects.create(hostname='test_host1') host1.set_attribute('test_attribute1', 'test_value1') self._set_static_attribute(host1, 'test_attribute1', 'test_value1') self.assertRaises( error.UnmodifiableAttributeException, host1.set_or_delete_attribute, 'test_attribute1', 'test_value2') models.RESPECT_STATIC_ATTRIBUTES = previous_config def test_add_host_previous_one_time_host(self): # ensure that when adding a host which was previously used as a one-time # host, the status isn't reset, since this can interfere with the # scheduler. host = models.Host.create_one_time_host('othost') self.assertEquals(host.invalid, True) self.assertEquals(host.status, models.Host.Status.READY) host.status = models.Host.Status.RUNNING host.save() host2 = models.Host.add_object(hostname='othost') self.assertEquals(host2.id, host.id) self.assertEquals(host2.status, models.Host.Status.RUNNING) def test_check_board_labels_allowed(self): host = models.Host.create_one_time_host('othost') # First check with host with no board label. self.assertEqual(host.check_board_labels_allowed([host]), None) # Second check with host with board label label = models.Label.add_object(name='board:test') label.host_set.add(host) self.assertRaises(model_logic.ValidationError, host.check_board_labels_allowed, [host], ['board:new_board']) class SpecialTaskUnittest(unittest.TestCase, frontend_test_utils.FrontendTestMixin): def setUp(self): self._frontend_common_setup() def tearDown(self): self._frontend_common_teardown() def _create_task(self): return models.SpecialTask.objects.create( host=self.hosts[0], task=models.SpecialTask.Task.VERIFY, requested_by=models.User.current_user()) def test_execution_path(self): task = self._create_task() self.assertEquals(task.execution_path(), 'hosts/host1/1-verify') def test_status(self): task = self._create_task() self.assertEquals(task.status, 'Queued') task.update_object(is_active=True) self.assertEquals(task.status, 'Running') task.update_object(is_active=False, is_complete=True, success=True) self.assertEquals(task.status, 'Completed') task.update_object(success=False) self.assertEquals(task.status, 'Failed') def test_activate(self): task = self._create_task() task.activate() self.assertTrue(task.is_active) self.assertFalse(task.is_complete) def test_finish(self): task = self._create_task() task.activate() task.finish(True) self.assertFalse(task.is_active) self.assertTrue(task.is_complete) self.assertTrue(task.success) def test_requested_by_from_queue_entry(self): job = self._create_job(hosts=[0]) task = models.SpecialTask.objects.create( host=self.hosts[0], task=models.SpecialTask.Task.VERIFY, queue_entry=job.hostqueueentry_set.all()[0]) self.assertEquals(task.requested_by.login, 'autotest_system') class HostQueueEntryUnittest(unittest.TestCase, frontend_test_utils.FrontendTestMixin): def setUp(self): self._frontend_common_setup() def tearDown(self): self._frontend_common_teardown() def test_execution_path(self): entry = self._create_job(hosts=[1]).hostqueueentry_set.all()[0] entry.execution_subdir = 'subdir' entry.save() self.assertEquals(entry.execution_path(), '1-autotest_system/subdir') class ModelWithInvalidTest(unittest.TestCase, frontend_test_utils.FrontendTestMixin): def setUp(self): self._frontend_common_setup() def tearDown(self): self._frontend_common_teardown() def test_model_with_invalid_delete(self): self.assertFalse(self.hosts[0].invalid) self.hosts[0].delete() self.assertTrue(self.hosts[0].invalid) self.assertTrue(models.Host.objects.get(id=self.hosts[0].id)) def test_model_with_invalid_delete_queryset(self): for host in self.hosts: self.assertFalse(host.invalid) hosts = models.Host.objects.all() hosts.delete() self.assertEqual(hosts.count(), len(self.hosts)) for host in hosts: self.assertTrue(host.invalid) def test_cloned_queryset_delete(self): """ Make sure that a cloned queryset maintains the custom delete() """ to_delete = ('host1', 'host2') for host in self.hosts: self.assertFalse(host.invalid) hosts = models.Host.objects.all().filter(hostname__in=to_delete) hosts.delete() all_hosts = models.Host.objects.all() self.assertEqual(all_hosts.count(), len(self.hosts)) for host in all_hosts: if host.hostname in to_delete: self.assertTrue( host.invalid, '%s.invalid expected to be True' % host.hostname) else: self.assertFalse( host.invalid, '%s.invalid expected to be False' % host.hostname) def test_normal_delete(self): job = self._create_job(hosts=[1]) self.assertEqual(1, models.Job.objects.all().count()) job.delete() self.assertEqual(0, models.Job.objects.all().count()) def test_normal_delete_queryset(self): self._create_job(hosts=[1]) self._create_job(hosts=[2]) self.assertEqual(2, models.Job.objects.all().count()) models.Job.objects.all().delete() self.assertEqual(0, models.Job.objects.all().count()) class SerializationTest(unittest.TestCase, frontend_test_utils.FrontendTestMixin): def setUp(self): self._frontend_common_setup(fill_data=False) def tearDown(self): self._frontend_common_teardown() def _get_example_response(self): return {'hosts': [{'aclgroup_set': [{'description': '', 'id': 1, 'name': 'Everyone', 'users': [{ 'access_level': 100, 'id': 1, 'login': 'autotest_system', 'reboot_after': 0, 'reboot_before': 1, 'show_experimental': False}]}], 'dirty': True, 'hostattribute_set': [], 'hostname': '100.107.2.163', 'id': 2, 'invalid': False, 'labels': [{'id': 7, 'invalid': False, 'kernel_config': '', 'name': 'power:battery', 'only_if_needed': False, 'platform': False}, {'id': 9, 'invalid': False, 'kernel_config': '', 'name': 'hw_video_acc_h264', 'only_if_needed': False, 'platform': False}, {'id': 10, 'invalid': False, 'kernel_config': '', 'name': 'hw_video_acc_enc_h264', 'only_if_needed': False, 'platform': False}, {'id': 11, 'invalid': False, 'kernel_config': '', 'name': 'webcam', 'only_if_needed': False, 'platform': False}, {'id': 12, 'invalid': False, 'kernel_config': '', 'name': 'touchpad', 'only_if_needed': False, 'platform': False}, {'id': 13, 'invalid': False, 'kernel_config': '', 'name': 'spring', 'only_if_needed': False, 'platform': False}, {'id': 14, 'invalid': False, 'kernel_config': '', 'name': 'board:daisy', 'only_if_needed': False, 'platform': True}, {'id': 15, 'invalid': False, 'kernel_config': '', 'name': 'board_freq_mem:daisy_1.7GHz', 'only_if_needed': False, 'platform': False}, {'id': 16, 'invalid': False, 'kernel_config': '', 'name': 'bluetooth', 'only_if_needed': False, 'platform': False}, {'id': 17, 'invalid': False, 'kernel_config': '', 'name': 'gpu_family:mali', 'only_if_needed': False, 'platform': False}, {'id': 19, 'invalid': False, 'kernel_config': '', 'name': 'ec:cros', 'only_if_needed': False, 'platform': False}, {'id': 20, 'invalid': False, 'kernel_config': '', 'name': 'storage:mmc', 'only_if_needed': False, 'platform': False}, {'id': 21, 'invalid': False, 'kernel_config': '', 'name': 'hw_video_acc_vp8', 'only_if_needed': False, 'platform': False}, {'id': 22, 'invalid': False, 'kernel_config': '', 'name': 'video_glitch_detection', 'only_if_needed': False, 'platform': False}, {'id': 23, 'invalid': False, 'kernel_config': '', 'name': 'pool:suites', 'only_if_needed': False, 'platform': False}, {'id': 25, 'invalid': False, 'kernel_config': '', 'name': 'daisy-board-name', 'only_if_needed': False, 'platform': False}], 'leased': False, 'lock_reason': '', 'lock_time': None, 'locked': False, 'protection': 0, 'shard': {'hostname': '1', 'id': 1}, 'status': 'Ready', 'synch_id': None}], 'jobs': [{'control_file': 'some control file\n\n\n', 'control_type': 2, 'created_on': '2014-09-04T13:09:35', 'dependency_labels': [{'id': 14, 'invalid': False, 'kernel_config': '', 'name': 'board:daisy', 'only_if_needed': False, 'platform': True}, {'id': 23, 'invalid': False, 'kernel_config': '', 'name': 'pool:suites', 'only_if_needed': False, 'platform': False}, {'id': 25, 'invalid': False, 'kernel_config': '', 'name': 'daisy-board-name', 'only_if_needed': False, 'platform': False}], 'email_list': '', 'hostqueueentry_set': [{'aborted': False, 'active': False, 'complete': False, 'deleted': False, 'execution_subdir': '', 'finished_on': None, 'id': 5, 'meta_host': { 'id': 14, 'invalid': False, 'kernel_config': '', 'name': 'board:daisy', 'only_if_needed': False, 'platform': True}, 'host_id': None, 'started_on': None, 'status': 'Queued'}], 'id': 5, 'jobkeyval_set': [{'id': 10, 'job_id': 5, 'key': 'suite', 'value': 'dummy'}, {'id': 11, 'job_id': 5, 'key': 'build', 'value': 'daisy-release'}, {'id': 12, 'job_id': 5, 'key': 'experimental', 'value': 'False'}], 'max_runtime_hrs': 72, 'max_runtime_mins': 1440, 'name': 'daisy-experimental', 'owner': 'autotest', 'parse_failed_repair': True, 'priority': 40, 'reboot_after': 0, 'reboot_before': 1, 'run_reset': True, 'run_verify': False, 'shard': {'hostname': '1', 'id': 1}, 'synch_count': 1, 'test_retry': 0, 'timeout': 24, 'timeout_mins': 1440, 'require_ssp': None}, {'control_file': 'some control file\n\n\n', 'control_type': 2, 'created_on': '2014-09-04T13:09:35', 'dependency_labels': [{'id': 14, 'invalid': False, 'kernel_config': '', 'name': 'board:daisy', 'only_if_needed': False, 'platform': True}, {'id': 23, 'invalid': False, 'kernel_config': '', 'name': 'pool:suites', 'only_if_needed': False, 'platform': False}, {'id': 25, 'invalid': False, 'kernel_config': '', 'name': 'daisy-board-name', 'only_if_needed': False, 'platform': False}], 'email_list': '', 'hostqueueentry_set': [{'aborted': False, 'active': False, 'complete': False, 'deleted': False, 'execution_subdir': '', 'finished_on': None, 'id': 7, 'meta_host': { 'id': 14, 'invalid': False, 'kernel_config': '', 'name': 'board:daisy', 'only_if_needed': False, 'platform': True}, 'host_id': None, 'started_on': None, 'status': 'Queued'}], 'id': 7, 'jobkeyval_set': [{'id': 16, 'job_id': 7, 'key': 'suite', 'value': 'dummy'}, {'id': 17, 'job_id': 7, 'key': 'build', 'value': 'daisy-release'}, {'id': 18, 'job_id': 7, 'key': 'experimental', 'value': 'False'}], 'max_runtime_hrs': 72, 'max_runtime_mins': 1440, 'name': 'daisy-experimental', 'owner': 'autotest', 'parse_failed_repair': True, 'priority': 40, 'reboot_after': 0, 'reboot_before': 1, 'run_reset': True, 'run_verify': False, 'shard': {'hostname': '1', 'id': 1}, 'synch_count': 1, 'test_retry': 0, 'timeout': 24, 'timeout_mins': 1440, 'require_ssp': None}]} def test_response(self): heartbeat_response = self._get_example_response() hosts_serialized = heartbeat_response['hosts'] jobs_serialized = heartbeat_response['jobs'] # Persisting is automatically done inside deserialize hosts = [models.Host.deserialize(host) for host in hosts_serialized] jobs = [models.Job.deserialize(job) for job in jobs_serialized] generated_heartbeat_response = { 'hosts': [host.serialize() for host in hosts], 'jobs': [job.serialize() for job in jobs] } example_response = self._get_example_response() # For attribute-like objects, we don't care about its id. for r in [generated_heartbeat_response, example_response]: for job in r['jobs']: for keyval in job['jobkeyval_set']: keyval.pop('id') for host in r['hosts']: for attribute in host['hostattribute_set']: keyval.pop('id') self.assertEqual(generated_heartbeat_response, example_response) def test_update(self): job = self._create_job(hosts=[1]) serialized = job.serialize(include_dependencies=False) serialized['owner'] = 'some_other_owner' job.update_from_serialized(serialized) self.assertEqual(job.owner, 'some_other_owner') serialized = job.serialize() self.assertRaises( ValueError, job.update_from_serialized, serialized) def test_sync_aborted(self): job = self._create_job(hosts=[1]) serialized = job.serialize() serialized['hostqueueentry_set'][0]['aborted'] = True serialized['hostqueueentry_set'][0]['status'] = 'Running' models.Job.deserialize(serialized) job = models.Job.objects.get(pk=job.id) self.assertTrue(job.hostqueueentry_set.all()[0].aborted) self.assertEqual(job.hostqueueentry_set.all()[0].status, 'Queued') if __name__ == '__main__': unittest.main()