• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2019 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"""Tests for modifying a tryjob."""
8
9from __future__ import print_function
10
11import json
12import unittest
13import unittest.mock as mock
14
15import get_llvm_hash
16import modify_a_tryjob
17import test_helpers
18import update_packages_and_run_tests
19import update_tryjob_status
20
21
22class ModifyATryjobTest(unittest.TestCase):
23  """Unittests for modifying a tryjob."""
24
25  def testNoTryjobsInStatusFile(self):
26    bisect_test_contents = {'start': 369410, 'end': 369420, 'jobs': []}
27
28    # Create a temporary .JSON file to simulate a .JSON file that has bisection
29    # contents.
30    with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
31      with open(temp_json_file, 'w') as f:
32        test_helpers.WritePrettyJsonFile(bisect_test_contents, f)
33
34      revision_to_modify = 369411
35
36      args_output = test_helpers.ArgsOutputTest()
37      args_output.builders = None
38      args_output.options = None
39
40      # Verify the exception is raised there are no tryjobs in the status file
41      # and the mode is not to 'add' a tryjob.
42      with self.assertRaises(SystemExit) as err:
43        modify_a_tryjob.PerformTryjobModification(
44            revision_to_modify, modify_a_tryjob.ModifyTryjob.REMOVE,
45            temp_json_file, args_output.extra_change_lists, args_output.options,
46            args_output.builders, args_output.chroot_path, args_output.verbose)
47
48      self.assertEqual(str(err.exception), 'No tryjobs in %s' % temp_json_file)
49
50  # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob
51  # was not found.
52  @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=None)
53  def testNoTryjobIndexFound(self, mock_find_tryjob_index):
54    bisect_test_contents = {
55        'start': 369410,
56        'end': 369420,
57        'jobs': [{
58            'rev': 369411,
59            'status': 'pending',
60            'buildbucket_id': 1200
61        }]
62    }
63
64    # Create a temporary .JSON file to simulate a .JSON file that has bisection
65    # contents.
66    with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
67      with open(temp_json_file, 'w') as f:
68        test_helpers.WritePrettyJsonFile(bisect_test_contents, f)
69
70      revision_to_modify = 369412
71
72      args_output = test_helpers.ArgsOutputTest()
73      args_output.builders = None
74      args_output.options = None
75
76      # Verify the exception is raised when the index of the tryjob was not
77      # found in the status file and the mode is not to 'add' a tryjob.
78      with self.assertRaises(ValueError) as err:
79        modify_a_tryjob.PerformTryjobModification(
80            revision_to_modify, modify_a_tryjob.ModifyTryjob.REMOVE,
81            temp_json_file, args_output.extra_change_lists, args_output.options,
82            args_output.builders, args_output.chroot_path, args_output.verbose)
83
84      self.assertEqual(
85          str(err.exception), 'Unable to find tryjob for %d in %s' %
86          (revision_to_modify, temp_json_file))
87
88    mock_find_tryjob_index.assert_called_once()
89
90  # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob
91  # was found.
92  @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0)
93  def testSuccessfullyRemovedTryjobInStatusFile(self, mock_find_tryjob_index):
94    bisect_test_contents = {
95        'start': 369410,
96        'end': 369420,
97        'jobs': [{
98            'rev': 369414,
99            'status': 'pending',
100            'buildbucket_id': 1200
101        }]
102    }
103
104    # Create a temporary .JSON file to simulate a .JSON file that has bisection
105    # contents.
106    with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
107      with open(temp_json_file, 'w') as f:
108        test_helpers.WritePrettyJsonFile(bisect_test_contents, f)
109
110      revision_to_modify = 369414
111
112      args_output = test_helpers.ArgsOutputTest()
113      args_output.builders = None
114      args_output.options = None
115
116      modify_a_tryjob.PerformTryjobModification(
117          revision_to_modify, modify_a_tryjob.ModifyTryjob.REMOVE,
118          temp_json_file, args_output.extra_change_lists, args_output.options,
119          args_output.builders, args_output.chroot_path, args_output.verbose)
120
121      # Verify that the tryjob was removed from the status file.
122      with open(temp_json_file) as status_file:
123        bisect_contents = json.load(status_file)
124
125        expected_file_contents = {'start': 369410, 'end': 369420, 'jobs': []}
126
127        self.assertDictEqual(bisect_contents, expected_file_contents)
128
129    mock_find_tryjob_index.assert_called_once()
130
131  # Simulate the behavior of `RunTryJobs()` when successfully submitted a
132  # tryjob.
133  @mock.patch.object(update_packages_and_run_tests, 'RunTryJobs')
134  # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob
135  # was found.
136  @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0)
137  def testSuccessfullyRelaunchedTryjob(self, mock_find_tryjob_index,
138                                       mock_run_tryjob):
139
140    bisect_test_contents = {
141        'start':
142            369410,
143        'end':
144            369420,
145        'jobs': [{
146            'rev': 369411,
147            'status': 'bad',
148            'link': 'https://some_tryjob_link.com',
149            'buildbucket_id': 1200,
150            'cl': 123,
151            'extra_cls': None,
152            'options': None,
153            'builder': ['some-builder-tryjob']
154        }]
155    }
156
157    tryjob_result = [{
158        'link': 'https://some_new_tryjob_link.com',
159        'buildbucket_id': 20
160    }]
161
162    mock_run_tryjob.return_value = tryjob_result
163
164    # Create a temporary .JSON file to simulate a .JSON file that has bisection
165    # contents.
166    with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
167      with open(temp_json_file, 'w') as f:
168        test_helpers.WritePrettyJsonFile(bisect_test_contents, f)
169
170      revision_to_modify = 369411
171
172      args_output = test_helpers.ArgsOutputTest()
173      args_output.builders = None
174      args_output.options = None
175
176      modify_a_tryjob.PerformTryjobModification(
177          revision_to_modify, modify_a_tryjob.ModifyTryjob.RELAUNCH,
178          temp_json_file, args_output.extra_change_lists, args_output.options,
179          args_output.builders, args_output.chroot_path, args_output.verbose)
180
181      # Verify that the tryjob's information was updated after submtting the
182      # tryjob.
183      with open(temp_json_file) as status_file:
184        bisect_contents = json.load(status_file)
185
186        expected_file_contents = {
187            'start':
188                369410,
189            'end':
190                369420,
191            'jobs': [{
192                'rev': 369411,
193                'status': 'pending',
194                'link': 'https://some_new_tryjob_link.com',
195                'buildbucket_id': 20,
196                'cl': 123,
197                'extra_cls': None,
198                'options': None,
199                'builder': ['some-builder-tryjob']
200            }]
201        }
202
203        self.assertDictEqual(bisect_contents, expected_file_contents)
204
205    mock_find_tryjob_index.assert_called_once()
206
207    mock_run_tryjob.assert_called_once()
208
209  # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob
210  # was found.
211  @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0)
212  def testAddingTryjobThatAlreadyExists(self, mock_find_tryjob_index):
213    bisect_test_contents = {
214        'start': 369410,
215        'end': 369420,
216        'jobs': [{
217            'rev': 369411,
218            'status': 'bad',
219            'builder': ['some-builder']
220        }]
221    }
222
223    # Create a temporary .JSON file to simulate a .JSON file that has bisection
224    # contents.
225    with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
226      with open(temp_json_file, 'w') as f:
227        test_helpers.WritePrettyJsonFile(bisect_test_contents, f)
228
229      revision_to_add = 369411
230
231      # Index of the tryjob in 'jobs' list.
232      tryjob_index = 0
233
234      args_output = test_helpers.ArgsOutputTest()
235      args_output.options = None
236
237      # Verify the exception is raised when the tryjob that is going to added
238      # already exists in the status file (found its index).
239      with self.assertRaises(ValueError) as err:
240        modify_a_tryjob.PerformTryjobModification(
241            revision_to_add, modify_a_tryjob.ModifyTryjob.ADD, temp_json_file,
242            args_output.extra_change_lists, args_output.options,
243            args_output.builders, args_output.chroot_path, args_output.verbose)
244
245      self.assertEqual(
246          str(err.exception), 'Tryjob already exists (index is %d) in %s.' %
247          (tryjob_index, temp_json_file))
248
249    mock_find_tryjob_index.assert_called_once()
250
251  # Simulate the behavior of `FindTryjobIndex()` when the tryjob was not found.
252  @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=None)
253  def testSuccessfullyDidNotAddTryjobOutsideOfBisectionBounds(
254      self, mock_find_tryjob_index):
255
256    bisect_test_contents = {
257        'start': 369410,
258        'end': 369420,
259        'jobs': [{
260            'rev': 369411,
261            'status': 'bad'
262        }]
263    }
264
265    # Create a temporary .JSON file to simulate a .JSON file that has bisection
266    # contents.
267    with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
268      with open(temp_json_file, 'w') as f:
269        test_helpers.WritePrettyJsonFile(bisect_test_contents, f)
270
271      # Add a revision that is outside of 'start' and 'end'.
272      revision_to_add = 369450
273
274      args_output = test_helpers.ArgsOutputTest()
275      args_output.options = None
276
277      # Verify the exception is raised when adding a tryjob that does not exist
278      # and is not within 'start' and 'end'.
279      with self.assertRaises(ValueError) as err:
280        modify_a_tryjob.PerformTryjobModification(
281            revision_to_add, modify_a_tryjob.ModifyTryjob.ADD, temp_json_file,
282            args_output.extra_change_lists, args_output.options,
283            args_output.builders, args_output.chroot_path, args_output.verbose)
284
285      self.assertEqual(
286          str(err.exception), 'Failed to add tryjob to %s' % temp_json_file)
287
288    mock_find_tryjob_index.assert_called_once()
289
290  # Simulate the behavior of `AddTryjob()` when successfully submitted the
291  # tryjob and constructed the tryjob information (a dictionary).
292  @mock.patch.object(modify_a_tryjob, 'AddTryjob')
293  # Simulate the behavior of `GetLLVMHashAndVersionFromSVNOption()` when
294  # successfully retrieved the git hash of the revision to launch a tryjob for.
295  @mock.patch.object(
296      get_llvm_hash,
297      'GetLLVMHashAndVersionFromSVNOption',
298      return_value=('a123testhash1', 369418))
299  # Simulate the behavior of `FindTryjobIndex()` when the tryjob was not found.
300  @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=None)
301  def testSuccessfullyAddedTryjob(self, mock_find_tryjob_index,
302                                  mock_get_llvm_hash, mock_add_tryjob):
303
304    bisect_test_contents = {
305        'start': 369410,
306        'end': 369420,
307        'jobs': [{
308            'rev': 369411,
309            'status': 'bad'
310        }]
311    }
312
313    # Create a temporary .JSON file to simulate a .JSON file that has bisection
314    # contents.
315    with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
316      with open(temp_json_file, 'w') as f:
317        test_helpers.WritePrettyJsonFile(bisect_test_contents, f)
318
319      # Add a revision that is outside of 'start' and 'end'.
320      revision_to_add = 369418
321
322      args_output = test_helpers.ArgsOutputTest()
323      args_output.options = None
324
325      new_tryjob_info = {
326          'rev': revision_to_add,
327          'status': 'pending',
328          'options': args_output.options,
329          'extra_cls': args_output.extra_change_lists,
330          'builder': args_output.builders
331      }
332
333      mock_add_tryjob.return_value = new_tryjob_info
334
335      modify_a_tryjob.PerformTryjobModification(
336          revision_to_add, modify_a_tryjob.ModifyTryjob.ADD, temp_json_file,
337          args_output.extra_change_lists, args_output.options,
338          args_output.builders, args_output.chroot_path, args_output.verbose)
339
340      # Verify that the tryjob was added to the status file.
341      with open(temp_json_file) as status_file:
342        bisect_contents = json.load(status_file)
343
344        expected_file_contents = {
345            'start': 369410,
346            'end': 369420,
347            'jobs': [{
348                'rev': 369411,
349                'status': 'bad'
350            }, new_tryjob_info]
351        }
352
353        self.assertDictEqual(bisect_contents, expected_file_contents)
354
355    mock_find_tryjob_index.assert_called_once()
356
357    mock_get_llvm_hash.assert_called_once_with(revision_to_add)
358
359    mock_add_tryjob.assert_called_once()
360
361  # Simulate the behavior of `FindTryjobIndex()` when the tryjob was found.
362  @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0)
363  def testModifyATryjobOptionDoesNotExist(self, mock_find_tryjob_index):
364    bisect_test_contents = {
365        'start': 369410,
366        'end': 369420,
367        'jobs': [{
368            'rev': 369414,
369            'status': 'bad'
370        }]
371    }
372
373    # Create a temporary .JSON file to simulate a .JSON file that has bisection
374    # contents.
375    with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
376      with open(temp_json_file, 'w') as f:
377        test_helpers.WritePrettyJsonFile(bisect_test_contents, f)
378
379      # Add a revision that is outside of 'start' and 'end'.
380      revision_to_modify = 369414
381
382      args_output = test_helpers.ArgsOutputTest()
383      args_output.builders = None
384      args_output.options = None
385
386      # Verify the exception is raised when the modify a tryjob option does not
387      # exist.
388      with self.assertRaises(ValueError) as err:
389        modify_a_tryjob.PerformTryjobModification(
390            revision_to_modify, 'remove_link', temp_json_file,
391            args_output.extra_change_lists, args_output.options,
392            args_output.builders, args_output.chroot_path, args_output.verbose)
393
394      self.assertEqual(
395          str(err.exception),
396          'Invalid "modify_tryjob" option provided: remove_link')
397
398      mock_find_tryjob_index.assert_called_once()
399
400
401if __name__ == '__main__':
402  unittest.main()
403