xref: /aosp_15_r20/external/autotest/frontend/afe/models_test.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li#!/usr/bin/python3
2*9c5db199SXin Li# pylint: disable=missing-docstring
3*9c5db199SXin Li
4*9c5db199SXin Liimport unittest
5*9c5db199SXin Li
6*9c5db199SXin Liimport common
7*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
8*9c5db199SXin Lifrom autotest_lib.frontend import setup_django_environment
9*9c5db199SXin Lifrom autotest_lib.frontend.afe import frontend_test_utils
10*9c5db199SXin Lifrom autotest_lib.frontend.afe import models, model_logic
11*9c5db199SXin Li
12*9c5db199SXin Li
13*9c5db199SXin Liclass AclGroupTest(unittest.TestCase,
14*9c5db199SXin Li                   frontend_test_utils.FrontendTestMixin):
15*9c5db199SXin Li    def setUp(self):
16*9c5db199SXin Li        self._frontend_common_setup()
17*9c5db199SXin Li
18*9c5db199SXin Li
19*9c5db199SXin Li    def tearDown(self):
20*9c5db199SXin Li        self._frontend_common_teardown()
21*9c5db199SXin Li
22*9c5db199SXin Li
23*9c5db199SXin Li    def _check_acls(self, host, acl_name_list):
24*9c5db199SXin Li        actual_acl_names = [acl_group.name for acl_group
25*9c5db199SXin Li                            in host.aclgroup_set.all()]
26*9c5db199SXin Li        self.assertEquals(set(actual_acl_names), set(acl_name_list))
27*9c5db199SXin Li
28*9c5db199SXin Li
29*9c5db199SXin Li    def test_on_host_membership_change(self):
30*9c5db199SXin Li        host1, host2 = self.hosts[1:3]
31*9c5db199SXin Li        everyone_acl = models.AclGroup.objects.get(name='Everyone')
32*9c5db199SXin Li
33*9c5db199SXin Li        host1.aclgroup_set.clear()
34*9c5db199SXin Li        self._check_acls(host1, [])
35*9c5db199SXin Li        host2.aclgroup_set.add(everyone_acl)
36*9c5db199SXin Li        self._check_acls(host2, ['Everyone', 'my_acl'])
37*9c5db199SXin Li
38*9c5db199SXin Li        models.AclGroup.on_host_membership_change()
39*9c5db199SXin Li
40*9c5db199SXin Li        self._check_acls(host1, ['Everyone'])
41*9c5db199SXin Li        self._check_acls(host2, ['my_acl'])
42*9c5db199SXin Li
43*9c5db199SXin Li
44*9c5db199SXin Liclass HostTest(unittest.TestCase,
45*9c5db199SXin Li               frontend_test_utils.FrontendTestMixin):
46*9c5db199SXin Li    def setUp(self):
47*9c5db199SXin Li        self._frontend_common_setup()
48*9c5db199SXin Li
49*9c5db199SXin Li
50*9c5db199SXin Li    def tearDown(self):
51*9c5db199SXin Li        self._frontend_common_teardown()
52*9c5db199SXin Li
53*9c5db199SXin Li
54*9c5db199SXin Li    def _get_attributes(self, host):
55*9c5db199SXin Li        models.Host.objects.populate_relationships(
56*9c5db199SXin Li                [host], models.HostAttribute, 'attribute_list')
57*9c5db199SXin Li        return dict((attribute.attribute, attribute.value)
58*9c5db199SXin Li                    for attribute in host.attribute_list)
59*9c5db199SXin Li
60*9c5db199SXin Li    def test_delete_attribute(self):
61*9c5db199SXin Li        previous_config = models.RESPECT_STATIC_ATTRIBUTES
62*9c5db199SXin Li        models.RESPECT_STATIC_ATTRIBUTES = False
63*9c5db199SXin Li        host1 = models.Host.objects.create(hostname='test_host1')
64*9c5db199SXin Li        host1.set_attribute('test_attribute1', 'test_value1')
65*9c5db199SXin Li
66*9c5db199SXin Li        attributes = self._get_attributes(host1)
67*9c5db199SXin Li        self.assertEquals(attributes['test_attribute1'], 'test_value1')
68*9c5db199SXin Li
69*9c5db199SXin Li        host1.set_or_delete_attribute('test_attribute1', None)
70*9c5db199SXin Li        attributes = self._get_attributes(host1)
71*9c5db199SXin Li        self.assertNotIn('test_attribute1', attributes.keys())
72*9c5db199SXin Li
73*9c5db199SXin Li        models.RESPECT_STATIC_ATTRIBUTES = previous_config
74*9c5db199SXin Li
75*9c5db199SXin Li
76*9c5db199SXin Li    def test_delete_static_attribute(self):
77*9c5db199SXin Li        previous_config = models.RESPECT_STATIC_ATTRIBUTES
78*9c5db199SXin Li        models.RESPECT_STATIC_ATTRIBUTES = True
79*9c5db199SXin Li        host1 = models.Host.objects.create(hostname='test_host1')
80*9c5db199SXin Li        host1.set_attribute('test_attribute1', 'test_value1')
81*9c5db199SXin Li        self._set_static_attribute(host1, 'test_attribute1', 'test_value1')
82*9c5db199SXin Li
83*9c5db199SXin Li        self.assertRaises(
84*9c5db199SXin Li                error.UnmodifiableAttributeException,
85*9c5db199SXin Li                host1.set_or_delete_attribute,
86*9c5db199SXin Li                'test_attribute1', None)
87*9c5db199SXin Li
88*9c5db199SXin Li        models.RESPECT_STATIC_ATTRIBUTES = previous_config
89*9c5db199SXin Li
90*9c5db199SXin Li
91*9c5db199SXin Li    def test_set_attribute(self):
92*9c5db199SXin Li        previous_config = models.RESPECT_STATIC_ATTRIBUTES
93*9c5db199SXin Li        models.RESPECT_STATIC_ATTRIBUTES = False
94*9c5db199SXin Li        host1 = models.Host.objects.create(hostname='test_host1')
95*9c5db199SXin Li        host1.set_attribute('test_attribute1', 'test_value1')
96*9c5db199SXin Li
97*9c5db199SXin Li        host1.set_or_delete_attribute('test_attribute1', 'test_new_value1')
98*9c5db199SXin Li
99*9c5db199SXin Li        attributes = self._get_attributes(host1)
100*9c5db199SXin Li        self.assertEquals(attributes['test_attribute1'], 'test_new_value1')
101*9c5db199SXin Li
102*9c5db199SXin Li        models.RESPECT_STATIC_ATTRIBUTES = previous_config
103*9c5db199SXin Li
104*9c5db199SXin Li
105*9c5db199SXin Li    def test_set_static_attribute(self):
106*9c5db199SXin Li        previous_config = models.RESPECT_STATIC_ATTRIBUTES
107*9c5db199SXin Li        models.RESPECT_STATIC_ATTRIBUTES = True
108*9c5db199SXin Li        host1 = models.Host.objects.create(hostname='test_host1')
109*9c5db199SXin Li        host1.set_attribute('test_attribute1', 'test_value1')
110*9c5db199SXin Li        self._set_static_attribute(host1, 'test_attribute1', 'test_value1')
111*9c5db199SXin Li
112*9c5db199SXin Li        self.assertRaises(
113*9c5db199SXin Li                error.UnmodifiableAttributeException,
114*9c5db199SXin Li                host1.set_or_delete_attribute,
115*9c5db199SXin Li                'test_attribute1', 'test_value2')
116*9c5db199SXin Li
117*9c5db199SXin Li        models.RESPECT_STATIC_ATTRIBUTES = previous_config
118*9c5db199SXin Li
119*9c5db199SXin Li
120*9c5db199SXin Li    def test_add_host_previous_one_time_host(self):
121*9c5db199SXin Li        # ensure that when adding a host which was previously used as a one-time
122*9c5db199SXin Li        # host, the status isn't reset, since this can interfere with the
123*9c5db199SXin Li        # scheduler.
124*9c5db199SXin Li        host = models.Host.create_one_time_host('othost')
125*9c5db199SXin Li        self.assertEquals(host.invalid, True)
126*9c5db199SXin Li        self.assertEquals(host.status, models.Host.Status.READY)
127*9c5db199SXin Li
128*9c5db199SXin Li        host.status = models.Host.Status.RUNNING
129*9c5db199SXin Li        host.save()
130*9c5db199SXin Li
131*9c5db199SXin Li        host2 = models.Host.add_object(hostname='othost')
132*9c5db199SXin Li        self.assertEquals(host2.id, host.id)
133*9c5db199SXin Li        self.assertEquals(host2.status, models.Host.Status.RUNNING)
134*9c5db199SXin Li
135*9c5db199SXin Li
136*9c5db199SXin Li    def test_check_board_labels_allowed(self):
137*9c5db199SXin Li        host = models.Host.create_one_time_host('othost')
138*9c5db199SXin Li        # First check with host with no board label.
139*9c5db199SXin Li        self.assertEqual(host.check_board_labels_allowed([host]), None)
140*9c5db199SXin Li
141*9c5db199SXin Li        # Second check with host with board label
142*9c5db199SXin Li        label = models.Label.add_object(name='board:test')
143*9c5db199SXin Li        label.host_set.add(host)
144*9c5db199SXin Li        self.assertRaises(model_logic.ValidationError,
145*9c5db199SXin Li                          host.check_board_labels_allowed, [host],
146*9c5db199SXin Li                          ['board:new_board'])
147*9c5db199SXin Li
148*9c5db199SXin Li
149*9c5db199SXin Liclass SpecialTaskUnittest(unittest.TestCase,
150*9c5db199SXin Li                          frontend_test_utils.FrontendTestMixin):
151*9c5db199SXin Li    def setUp(self):
152*9c5db199SXin Li        self._frontend_common_setup()
153*9c5db199SXin Li
154*9c5db199SXin Li
155*9c5db199SXin Li    def tearDown(self):
156*9c5db199SXin Li        self._frontend_common_teardown()
157*9c5db199SXin Li
158*9c5db199SXin Li
159*9c5db199SXin Li    def _create_task(self):
160*9c5db199SXin Li        return models.SpecialTask.objects.create(
161*9c5db199SXin Li                host=self.hosts[0], task=models.SpecialTask.Task.VERIFY,
162*9c5db199SXin Li                requested_by=models.User.current_user())
163*9c5db199SXin Li
164*9c5db199SXin Li
165*9c5db199SXin Li    def test_execution_path(self):
166*9c5db199SXin Li        task = self._create_task()
167*9c5db199SXin Li        self.assertEquals(task.execution_path(), 'hosts/host1/1-verify')
168*9c5db199SXin Li
169*9c5db199SXin Li
170*9c5db199SXin Li    def test_status(self):
171*9c5db199SXin Li        task = self._create_task()
172*9c5db199SXin Li        self.assertEquals(task.status, 'Queued')
173*9c5db199SXin Li
174*9c5db199SXin Li        task.update_object(is_active=True)
175*9c5db199SXin Li        self.assertEquals(task.status, 'Running')
176*9c5db199SXin Li
177*9c5db199SXin Li        task.update_object(is_active=False, is_complete=True, success=True)
178*9c5db199SXin Li        self.assertEquals(task.status, 'Completed')
179*9c5db199SXin Li
180*9c5db199SXin Li        task.update_object(success=False)
181*9c5db199SXin Li        self.assertEquals(task.status, 'Failed')
182*9c5db199SXin Li
183*9c5db199SXin Li
184*9c5db199SXin Li    def test_activate(self):
185*9c5db199SXin Li        task = self._create_task()
186*9c5db199SXin Li        task.activate()
187*9c5db199SXin Li        self.assertTrue(task.is_active)
188*9c5db199SXin Li        self.assertFalse(task.is_complete)
189*9c5db199SXin Li
190*9c5db199SXin Li
191*9c5db199SXin Li    def test_finish(self):
192*9c5db199SXin Li        task = self._create_task()
193*9c5db199SXin Li        task.activate()
194*9c5db199SXin Li        task.finish(True)
195*9c5db199SXin Li        self.assertFalse(task.is_active)
196*9c5db199SXin Li        self.assertTrue(task.is_complete)
197*9c5db199SXin Li        self.assertTrue(task.success)
198*9c5db199SXin Li
199*9c5db199SXin Li
200*9c5db199SXin Li    def test_requested_by_from_queue_entry(self):
201*9c5db199SXin Li        job = self._create_job(hosts=[0])
202*9c5db199SXin Li        task = models.SpecialTask.objects.create(
203*9c5db199SXin Li                host=self.hosts[0], task=models.SpecialTask.Task.VERIFY,
204*9c5db199SXin Li                queue_entry=job.hostqueueentry_set.all()[0])
205*9c5db199SXin Li        self.assertEquals(task.requested_by.login, 'autotest_system')
206*9c5db199SXin Li
207*9c5db199SXin Li
208*9c5db199SXin Liclass HostQueueEntryUnittest(unittest.TestCase,
209*9c5db199SXin Li                             frontend_test_utils.FrontendTestMixin):
210*9c5db199SXin Li    def setUp(self):
211*9c5db199SXin Li        self._frontend_common_setup()
212*9c5db199SXin Li
213*9c5db199SXin Li
214*9c5db199SXin Li    def tearDown(self):
215*9c5db199SXin Li        self._frontend_common_teardown()
216*9c5db199SXin Li
217*9c5db199SXin Li
218*9c5db199SXin Li    def test_execution_path(self):
219*9c5db199SXin Li        entry = self._create_job(hosts=[1]).hostqueueentry_set.all()[0]
220*9c5db199SXin Li        entry.execution_subdir = 'subdir'
221*9c5db199SXin Li        entry.save()
222*9c5db199SXin Li
223*9c5db199SXin Li        self.assertEquals(entry.execution_path(), '1-autotest_system/subdir')
224*9c5db199SXin Li
225*9c5db199SXin Li
226*9c5db199SXin Liclass ModelWithInvalidTest(unittest.TestCase,
227*9c5db199SXin Li                           frontend_test_utils.FrontendTestMixin):
228*9c5db199SXin Li    def setUp(self):
229*9c5db199SXin Li        self._frontend_common_setup()
230*9c5db199SXin Li
231*9c5db199SXin Li
232*9c5db199SXin Li    def tearDown(self):
233*9c5db199SXin Li        self._frontend_common_teardown()
234*9c5db199SXin Li
235*9c5db199SXin Li
236*9c5db199SXin Li    def test_model_with_invalid_delete(self):
237*9c5db199SXin Li        self.assertFalse(self.hosts[0].invalid)
238*9c5db199SXin Li        self.hosts[0].delete()
239*9c5db199SXin Li        self.assertTrue(self.hosts[0].invalid)
240*9c5db199SXin Li        self.assertTrue(models.Host.objects.get(id=self.hosts[0].id))
241*9c5db199SXin Li
242*9c5db199SXin Li
243*9c5db199SXin Li    def test_model_with_invalid_delete_queryset(self):
244*9c5db199SXin Li        for host in self.hosts:
245*9c5db199SXin Li            self.assertFalse(host.invalid)
246*9c5db199SXin Li
247*9c5db199SXin Li        hosts = models.Host.objects.all()
248*9c5db199SXin Li        hosts.delete()
249*9c5db199SXin Li        self.assertEqual(hosts.count(), len(self.hosts))
250*9c5db199SXin Li
251*9c5db199SXin Li        for host in hosts:
252*9c5db199SXin Li            self.assertTrue(host.invalid)
253*9c5db199SXin Li
254*9c5db199SXin Li
255*9c5db199SXin Li    def test_cloned_queryset_delete(self):
256*9c5db199SXin Li        """
257*9c5db199SXin Li        Make sure that a cloned queryset maintains the custom delete()
258*9c5db199SXin Li        """
259*9c5db199SXin Li        to_delete = ('host1', 'host2')
260*9c5db199SXin Li
261*9c5db199SXin Li        for host in self.hosts:
262*9c5db199SXin Li            self.assertFalse(host.invalid)
263*9c5db199SXin Li
264*9c5db199SXin Li        hosts = models.Host.objects.all().filter(hostname__in=to_delete)
265*9c5db199SXin Li        hosts.delete()
266*9c5db199SXin Li        all_hosts = models.Host.objects.all()
267*9c5db199SXin Li        self.assertEqual(all_hosts.count(), len(self.hosts))
268*9c5db199SXin Li
269*9c5db199SXin Li        for host in all_hosts:
270*9c5db199SXin Li            if host.hostname in to_delete:
271*9c5db199SXin Li                self.assertTrue(
272*9c5db199SXin Li                        host.invalid,
273*9c5db199SXin Li                        '%s.invalid expected to be True' % host.hostname)
274*9c5db199SXin Li            else:
275*9c5db199SXin Li                self.assertFalse(
276*9c5db199SXin Li                        host.invalid,
277*9c5db199SXin Li                        '%s.invalid expected to be False' % host.hostname)
278*9c5db199SXin Li
279*9c5db199SXin Li
280*9c5db199SXin Li    def test_normal_delete(self):
281*9c5db199SXin Li        job = self._create_job(hosts=[1])
282*9c5db199SXin Li        self.assertEqual(1, models.Job.objects.all().count())
283*9c5db199SXin Li
284*9c5db199SXin Li        job.delete()
285*9c5db199SXin Li        self.assertEqual(0, models.Job.objects.all().count())
286*9c5db199SXin Li
287*9c5db199SXin Li
288*9c5db199SXin Li    def test_normal_delete_queryset(self):
289*9c5db199SXin Li        self._create_job(hosts=[1])
290*9c5db199SXin Li        self._create_job(hosts=[2])
291*9c5db199SXin Li
292*9c5db199SXin Li        self.assertEqual(2, models.Job.objects.all().count())
293*9c5db199SXin Li
294*9c5db199SXin Li        models.Job.objects.all().delete()
295*9c5db199SXin Li        self.assertEqual(0, models.Job.objects.all().count())
296*9c5db199SXin Li
297*9c5db199SXin Li
298*9c5db199SXin Liclass SerializationTest(unittest.TestCase,
299*9c5db199SXin Li                        frontend_test_utils.FrontendTestMixin):
300*9c5db199SXin Li    def setUp(self):
301*9c5db199SXin Li        self._frontend_common_setup(fill_data=False)
302*9c5db199SXin Li
303*9c5db199SXin Li
304*9c5db199SXin Li    def tearDown(self):
305*9c5db199SXin Li        self._frontend_common_teardown()
306*9c5db199SXin Li
307*9c5db199SXin Li
308*9c5db199SXin Li    def _get_example_response(self):
309*9c5db199SXin Li        return {'hosts': [{'aclgroup_set': [{'description': '',
310*9c5db199SXin Li                                             'id': 1,
311*9c5db199SXin Li                                             'name': 'Everyone',
312*9c5db199SXin Li                                             'users': [{
313*9c5db199SXin Li                                                 'access_level': 100,
314*9c5db199SXin Li                                                 'id': 1,
315*9c5db199SXin Li                                                 'login': 'autotest_system',
316*9c5db199SXin Li                                                 'reboot_after': 0,
317*9c5db199SXin Li                                                 'reboot_before': 1,
318*9c5db199SXin Li                                                 'show_experimental': False}]}],
319*9c5db199SXin Li                           'dirty': True,
320*9c5db199SXin Li                           'hostattribute_set': [],
321*9c5db199SXin Li                           'hostname': '100.107.2.163',
322*9c5db199SXin Li                           'id': 2,
323*9c5db199SXin Li                           'invalid': False,
324*9c5db199SXin Li                           'labels': [{'id': 7,
325*9c5db199SXin Li                                       'invalid': False,
326*9c5db199SXin Li                                       'kernel_config': '',
327*9c5db199SXin Li                                       'name': 'power:battery',
328*9c5db199SXin Li                                       'only_if_needed': False,
329*9c5db199SXin Li                                       'platform': False},
330*9c5db199SXin Li                                      {'id': 9,
331*9c5db199SXin Li                                       'invalid': False,
332*9c5db199SXin Li                                       'kernel_config': '',
333*9c5db199SXin Li                                       'name': 'hw_video_acc_h264',
334*9c5db199SXin Li                                       'only_if_needed': False,
335*9c5db199SXin Li                                       'platform': False},
336*9c5db199SXin Li                                      {'id': 10,
337*9c5db199SXin Li                                       'invalid': False,
338*9c5db199SXin Li                                       'kernel_config': '',
339*9c5db199SXin Li                                       'name': 'hw_video_acc_enc_h264',
340*9c5db199SXin Li                                       'only_if_needed': False,
341*9c5db199SXin Li                                       'platform': False},
342*9c5db199SXin Li                                      {'id': 11,
343*9c5db199SXin Li                                       'invalid': False,
344*9c5db199SXin Li                                       'kernel_config': '',
345*9c5db199SXin Li                                       'name': 'webcam',
346*9c5db199SXin Li                                       'only_if_needed': False,
347*9c5db199SXin Li                                       'platform': False},
348*9c5db199SXin Li                                      {'id': 12,
349*9c5db199SXin Li                                       'invalid': False,
350*9c5db199SXin Li                                       'kernel_config': '',
351*9c5db199SXin Li                                       'name': 'touchpad',
352*9c5db199SXin Li                                       'only_if_needed': False,
353*9c5db199SXin Li                                       'platform': False},
354*9c5db199SXin Li                                      {'id': 13,
355*9c5db199SXin Li                                       'invalid': False,
356*9c5db199SXin Li                                       'kernel_config': '',
357*9c5db199SXin Li                                       'name': 'spring',
358*9c5db199SXin Li                                       'only_if_needed': False,
359*9c5db199SXin Li                                       'platform': False},
360*9c5db199SXin Li                                      {'id': 14,
361*9c5db199SXin Li                                       'invalid': False,
362*9c5db199SXin Li                                       'kernel_config': '',
363*9c5db199SXin Li                                       'name': 'board:daisy',
364*9c5db199SXin Li                                       'only_if_needed': False,
365*9c5db199SXin Li                                       'platform': True},
366*9c5db199SXin Li                                      {'id': 15,
367*9c5db199SXin Li                                       'invalid': False,
368*9c5db199SXin Li                                       'kernel_config': '',
369*9c5db199SXin Li                                       'name': 'board_freq_mem:daisy_1.7GHz',
370*9c5db199SXin Li                                       'only_if_needed': False,
371*9c5db199SXin Li                                       'platform': False},
372*9c5db199SXin Li                                      {'id': 16,
373*9c5db199SXin Li                                       'invalid': False,
374*9c5db199SXin Li                                       'kernel_config': '',
375*9c5db199SXin Li                                       'name': 'bluetooth',
376*9c5db199SXin Li                                       'only_if_needed': False,
377*9c5db199SXin Li                                       'platform': False},
378*9c5db199SXin Li                                      {'id': 17,
379*9c5db199SXin Li                                       'invalid': False,
380*9c5db199SXin Li                                       'kernel_config': '',
381*9c5db199SXin Li                                       'name': 'gpu_family:mali',
382*9c5db199SXin Li                                       'only_if_needed': False,
383*9c5db199SXin Li                                       'platform': False},
384*9c5db199SXin Li                                      {'id': 19,
385*9c5db199SXin Li                                       'invalid': False,
386*9c5db199SXin Li                                       'kernel_config': '',
387*9c5db199SXin Li                                       'name': 'ec:cros',
388*9c5db199SXin Li                                       'only_if_needed': False,
389*9c5db199SXin Li                                       'platform': False},
390*9c5db199SXin Li                                      {'id': 20,
391*9c5db199SXin Li                                       'invalid': False,
392*9c5db199SXin Li                                       'kernel_config': '',
393*9c5db199SXin Li                                       'name': 'storage:mmc',
394*9c5db199SXin Li                                       'only_if_needed': False,
395*9c5db199SXin Li                                       'platform': False},
396*9c5db199SXin Li                                      {'id': 21,
397*9c5db199SXin Li                                       'invalid': False,
398*9c5db199SXin Li                                       'kernel_config': '',
399*9c5db199SXin Li                                       'name': 'hw_video_acc_vp8',
400*9c5db199SXin Li                                       'only_if_needed': False,
401*9c5db199SXin Li                                       'platform': False},
402*9c5db199SXin Li                                      {'id': 22,
403*9c5db199SXin Li                                       'invalid': False,
404*9c5db199SXin Li                                       'kernel_config': '',
405*9c5db199SXin Li                                       'name': 'video_glitch_detection',
406*9c5db199SXin Li                                       'only_if_needed': False,
407*9c5db199SXin Li                                       'platform': False},
408*9c5db199SXin Li                                      {'id': 23,
409*9c5db199SXin Li                                       'invalid': False,
410*9c5db199SXin Li                                       'kernel_config': '',
411*9c5db199SXin Li                                       'name': 'pool:suites',
412*9c5db199SXin Li                                       'only_if_needed': False,
413*9c5db199SXin Li                                       'platform': False},
414*9c5db199SXin Li                                      {'id': 25,
415*9c5db199SXin Li                                       'invalid': False,
416*9c5db199SXin Li                                       'kernel_config': '',
417*9c5db199SXin Li                                       'name': 'daisy-board-name',
418*9c5db199SXin Li                                       'only_if_needed': False,
419*9c5db199SXin Li                                       'platform': False}],
420*9c5db199SXin Li                           'leased': False,
421*9c5db199SXin Li                           'lock_reason': '',
422*9c5db199SXin Li                           'lock_time': None,
423*9c5db199SXin Li                           'locked': False,
424*9c5db199SXin Li                           'protection': 0,
425*9c5db199SXin Li                           'shard': {'hostname': '1', 'id': 1},
426*9c5db199SXin Li                           'status': 'Ready',
427*9c5db199SXin Li                           'synch_id': None}],
428*9c5db199SXin Li                'jobs': [{'control_file': 'some control file\n\n\n',
429*9c5db199SXin Li                          'control_type': 2,
430*9c5db199SXin Li                          'created_on': '2014-09-04T13:09:35',
431*9c5db199SXin Li                          'dependency_labels': [{'id': 14,
432*9c5db199SXin Li                                                 'invalid': False,
433*9c5db199SXin Li                                                 'kernel_config': '',
434*9c5db199SXin Li                                                 'name': 'board:daisy',
435*9c5db199SXin Li                                                 'only_if_needed': False,
436*9c5db199SXin Li                                                 'platform': True},
437*9c5db199SXin Li                                                {'id': 23,
438*9c5db199SXin Li                                                 'invalid': False,
439*9c5db199SXin Li                                                 'kernel_config': '',
440*9c5db199SXin Li                                                 'name': 'pool:suites',
441*9c5db199SXin Li                                                 'only_if_needed': False,
442*9c5db199SXin Li                                                 'platform': False},
443*9c5db199SXin Li                                                {'id': 25,
444*9c5db199SXin Li                                                 'invalid': False,
445*9c5db199SXin Li                                                 'kernel_config': '',
446*9c5db199SXin Li                                                 'name': 'daisy-board-name',
447*9c5db199SXin Li                                                 'only_if_needed': False,
448*9c5db199SXin Li                                                 'platform': False}],
449*9c5db199SXin Li                          'email_list': '',
450*9c5db199SXin Li                          'hostqueueentry_set': [{'aborted': False,
451*9c5db199SXin Li                                                  'active': False,
452*9c5db199SXin Li                                                  'complete': False,
453*9c5db199SXin Li                                                  'deleted': False,
454*9c5db199SXin Li                                                  'execution_subdir': '',
455*9c5db199SXin Li                                                  'finished_on': None,
456*9c5db199SXin Li                                                  'id': 5,
457*9c5db199SXin Li                                                  'meta_host': {
458*9c5db199SXin Li                                                      'id': 14,
459*9c5db199SXin Li                                                      'invalid': False,
460*9c5db199SXin Li                                                      'kernel_config': '',
461*9c5db199SXin Li                                                      'name': 'board:daisy',
462*9c5db199SXin Li                                                      'only_if_needed': False,
463*9c5db199SXin Li                                                      'platform': True},
464*9c5db199SXin Li                                                  'host_id': None,
465*9c5db199SXin Li                                                  'started_on': None,
466*9c5db199SXin Li                                                  'status': 'Queued'}],
467*9c5db199SXin Li                          'id': 5,
468*9c5db199SXin Li                          'jobkeyval_set': [{'id': 10,
469*9c5db199SXin Li                                             'job_id': 5,
470*9c5db199SXin Li                                             'key': 'suite',
471*9c5db199SXin Li                                             'value': 'stub'},
472*9c5db199SXin Li                                            {'id': 11,
473*9c5db199SXin Li                                             'job_id': 5,
474*9c5db199SXin Li                                             'key': 'build',
475*9c5db199SXin Li                                             'value': 'daisy-release'},
476*9c5db199SXin Li                                            {'id': 12,
477*9c5db199SXin Li                                             'job_id': 5,
478*9c5db199SXin Li                                             'key': 'experimental',
479*9c5db199SXin Li                                             'value': 'False'}],
480*9c5db199SXin Li                          'max_runtime_hrs': 72,
481*9c5db199SXin Li                          'max_runtime_mins': 1440,
482*9c5db199SXin Li                          'name': 'daisy-experimental',
483*9c5db199SXin Li                          'owner': 'autotest',
484*9c5db199SXin Li                          'parse_failed_repair': True,
485*9c5db199SXin Li                          'priority': 40,
486*9c5db199SXin Li                          'reboot_after': 0,
487*9c5db199SXin Li                          'reboot_before': 1,
488*9c5db199SXin Li                          'run_reset': True,
489*9c5db199SXin Li                          'run_verify': False,
490*9c5db199SXin Li                          'shard': {'hostname': '1', 'id': 1},
491*9c5db199SXin Li                          'synch_count': 1,
492*9c5db199SXin Li                          'test_retry': 0,
493*9c5db199SXin Li                          'timeout': 24,
494*9c5db199SXin Li                          'timeout_mins': 1440,
495*9c5db199SXin Li                          'require_ssp': None},
496*9c5db199SXin Li                         {'control_file': 'some control file\n\n\n',
497*9c5db199SXin Li                          'control_type': 2,
498*9c5db199SXin Li                          'created_on': '2014-09-04T13:09:35',
499*9c5db199SXin Li                          'dependency_labels': [{'id': 14,
500*9c5db199SXin Li                                                 'invalid': False,
501*9c5db199SXin Li                                                 'kernel_config': '',
502*9c5db199SXin Li                                                 'name': 'board:daisy',
503*9c5db199SXin Li                                                 'only_if_needed': False,
504*9c5db199SXin Li                                                 'platform': True},
505*9c5db199SXin Li                                                {'id': 23,
506*9c5db199SXin Li                                                 'invalid': False,
507*9c5db199SXin Li                                                 'kernel_config': '',
508*9c5db199SXin Li                                                 'name': 'pool:suites',
509*9c5db199SXin Li                                                 'only_if_needed': False,
510*9c5db199SXin Li                                                 'platform': False},
511*9c5db199SXin Li                                                {'id': 25,
512*9c5db199SXin Li                                                 'invalid': False,
513*9c5db199SXin Li                                                 'kernel_config': '',
514*9c5db199SXin Li                                                 'name': 'daisy-board-name',
515*9c5db199SXin Li                                                 'only_if_needed': False,
516*9c5db199SXin Li                                                 'platform': False}],
517*9c5db199SXin Li                          'email_list': '',
518*9c5db199SXin Li                          'hostqueueentry_set': [{'aborted': False,
519*9c5db199SXin Li                                                  'active': False,
520*9c5db199SXin Li                                                  'complete': False,
521*9c5db199SXin Li                                                  'deleted': False,
522*9c5db199SXin Li                                                  'execution_subdir': '',
523*9c5db199SXin Li                                                  'finished_on': None,
524*9c5db199SXin Li                                                  'id': 7,
525*9c5db199SXin Li                                                  'meta_host': {
526*9c5db199SXin Li                                                      'id': 14,
527*9c5db199SXin Li                                                      'invalid': False,
528*9c5db199SXin Li                                                      'kernel_config': '',
529*9c5db199SXin Li                                                      'name': 'board:daisy',
530*9c5db199SXin Li                                                      'only_if_needed': False,
531*9c5db199SXin Li                                                      'platform': True},
532*9c5db199SXin Li                                                  'host_id': None,
533*9c5db199SXin Li                                                  'started_on': None,
534*9c5db199SXin Li                                                  'status': 'Queued'}],
535*9c5db199SXin Li                          'id': 7,
536*9c5db199SXin Li                          'jobkeyval_set': [{'id': 16,
537*9c5db199SXin Li                                             'job_id': 7,
538*9c5db199SXin Li                                             'key': 'suite',
539*9c5db199SXin Li                                             'value': 'stub'},
540*9c5db199SXin Li                                            {'id': 17,
541*9c5db199SXin Li                                             'job_id': 7,
542*9c5db199SXin Li                                             'key': 'build',
543*9c5db199SXin Li                                             'value': 'daisy-release'},
544*9c5db199SXin Li                                            {'id': 18,
545*9c5db199SXin Li                                             'job_id': 7,
546*9c5db199SXin Li                                             'key': 'experimental',
547*9c5db199SXin Li                                             'value': 'False'}],
548*9c5db199SXin Li                          'max_runtime_hrs': 72,
549*9c5db199SXin Li                          'max_runtime_mins': 1440,
550*9c5db199SXin Li                          'name': 'daisy-experimental',
551*9c5db199SXin Li                          'owner': 'autotest',
552*9c5db199SXin Li                          'parse_failed_repair': True,
553*9c5db199SXin Li                          'priority': 40,
554*9c5db199SXin Li                          'reboot_after': 0,
555*9c5db199SXin Li                          'reboot_before': 1,
556*9c5db199SXin Li                          'run_reset': True,
557*9c5db199SXin Li                          'run_verify': False,
558*9c5db199SXin Li                          'shard': {'hostname': '1', 'id': 1},
559*9c5db199SXin Li                          'synch_count': 1,
560*9c5db199SXin Li                          'test_retry': 0,
561*9c5db199SXin Li                          'timeout': 24,
562*9c5db199SXin Li                          'timeout_mins': 1440,
563*9c5db199SXin Li                          'require_ssp': None}]}
564*9c5db199SXin Li
565*9c5db199SXin Li
566*9c5db199SXin Li    def test_response(self):
567*9c5db199SXin Li        heartbeat_response = self._get_example_response()
568*9c5db199SXin Li        hosts_serialized = heartbeat_response['hosts']
569*9c5db199SXin Li        jobs_serialized = heartbeat_response['jobs']
570*9c5db199SXin Li
571*9c5db199SXin Li        # Persisting is automatically done inside deserialize
572*9c5db199SXin Li        hosts = [models.Host.deserialize(host) for host in hosts_serialized]
573*9c5db199SXin Li        jobs = [models.Job.deserialize(job) for job in jobs_serialized]
574*9c5db199SXin Li
575*9c5db199SXin Li        generated_heartbeat_response = {
576*9c5db199SXin Li            'hosts': [host.serialize() for host in hosts],
577*9c5db199SXin Li            'jobs': [job.serialize() for job in jobs]
578*9c5db199SXin Li        }
579*9c5db199SXin Li        example_response = self._get_example_response()
580*9c5db199SXin Li        # For attribute-like objects, we don't care about its id.
581*9c5db199SXin Li        for r in [generated_heartbeat_response, example_response]:
582*9c5db199SXin Li            for job in r['jobs']:
583*9c5db199SXin Li                for keyval in job['jobkeyval_set']:
584*9c5db199SXin Li                    keyval.pop('id')
585*9c5db199SXin Li            for host in r['hosts']:
586*9c5db199SXin Li                for attribute in host['hostattribute_set']:
587*9c5db199SXin Li                    keyval.pop('id')
588*9c5db199SXin Li        self.assertEqual(generated_heartbeat_response, example_response)
589*9c5db199SXin Li
590*9c5db199SXin Li
591*9c5db199SXin Li    def test_update(self):
592*9c5db199SXin Li        job = self._create_job(hosts=[1])
593*9c5db199SXin Li        serialized = job.serialize(include_dependencies=False)
594*9c5db199SXin Li        serialized['owner'] = 'some_other_owner'
595*9c5db199SXin Li
596*9c5db199SXin Li        job.update_from_serialized(serialized)
597*9c5db199SXin Li        self.assertEqual(job.owner, 'some_other_owner')
598*9c5db199SXin Li
599*9c5db199SXin Li        serialized = job.serialize()
600*9c5db199SXin Li        self.assertRaises(
601*9c5db199SXin Li            ValueError,
602*9c5db199SXin Li            job.update_from_serialized, serialized)
603*9c5db199SXin Li
604*9c5db199SXin Li
605*9c5db199SXin Li    def test_sync_aborted(self):
606*9c5db199SXin Li        job = self._create_job(hosts=[1])
607*9c5db199SXin Li        serialized = job.serialize()
608*9c5db199SXin Li
609*9c5db199SXin Li        serialized['hostqueueentry_set'][0]['aborted'] = True
610*9c5db199SXin Li        serialized['hostqueueentry_set'][0]['status'] = 'Running'
611*9c5db199SXin Li
612*9c5db199SXin Li        models.Job.deserialize(serialized)
613*9c5db199SXin Li
614*9c5db199SXin Li        job = models.Job.objects.get(pk=job.id)
615*9c5db199SXin Li        self.assertTrue(job.hostqueueentry_set.all()[0].aborted)
616*9c5db199SXin Li        self.assertEqual(job.hostqueueentry_set.all()[0].status, 'Queued')
617*9c5db199SXin Li
618*9c5db199SXin Li
619*9c5db199SXin Liif __name__ == '__main__':
620*9c5db199SXin Li    unittest.main()
621