• 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# pylint: disable=protected-access
8
9"""Unit tests when creating the arguments for the patch manager."""
10
11from __future__ import print_function
12from collections import namedtuple
13import os
14import unittest
15import unittest.mock as mock
16
17from failure_modes import FailureModes
18import get_llvm_hash
19import llvm_patch_management
20import patch_manager
21import subprocess_helpers
22
23
24class LlvmPatchManagementTest(unittest.TestCase):
25  """Test class when constructing the arguments for the patch manager."""
26
27  # Simulate the behavior of `os.path.isdir()` when the chroot path does not
28  # exist or is not a directory.
29  @mock.patch.object(os.path, 'isdir', return_value=False)
30  def testInvalidChrootPathWhenGetPathToFilesDir(self, mock_isdir):
31    chroot_path = '/some/path/to/chroot'
32    package = 'sys-devel/llvm'
33
34    # Verify the exception is raised when an invalid absolute path to the chroot
35    # is passed in.
36    with self.assertRaises(ValueError) as err:
37      llvm_patch_management.GetPathToFilesDirectory(chroot_path, package)
38
39    self.assertEqual(
40        str(err.exception), 'Invalid chroot provided: %s' % chroot_path)
41
42    mock_isdir.assert_called_once()
43
44  # Simulate the behavior of 'os.path.isdir()' when a valid chroot path is
45  # passed in.
46  @mock.patch.object(os.path, 'isdir', return_value=True)
47  @mock.patch.object(subprocess_helpers, 'ChrootRunCommand')
48  @mock.patch.object(llvm_patch_management, '_GetRelativePathOfChrootPath')
49  def testSuccessfullyGetPathToFilesDir(
50      self, mock_get_relative_path_of_chroot_path, mock_chroot_cmd, mock_isdir):
51
52    package_chroot_path = '/mnt/host/source/path/to/llvm/llvm.ebuild'
53
54    # Simulate behavior of 'ChrootRunCommand()' when successfully
55    # retrieved the absolute chroot path to the package's ebuild.
56    mock_chroot_cmd.return_value = package_chroot_path
57
58    # Simulate behavior of '_GetRelativePathOfChrootPath()' when successfully
59    # removed '/mnt/host/source' of the absolute chroot path to the package's
60    # ebuild.
61    #
62    # Returns relative path after '/mnt/host/source/'.
63    mock_get_relative_path_of_chroot_path.return_value = 'path/to/llvm'
64
65    chroot_path = '/some/path/to/chroot'
66
67    package = 'sys-devel/llvm'
68
69    self.assertEqual(
70        llvm_patch_management.GetPathToFilesDirectory(chroot_path, package),
71        '/some/path/to/chroot/path/to/llvm/files/')
72
73    mock_isdir.assert_called_once()
74
75    mock_chroot_cmd.assert_called_once()
76
77    mock_get_relative_path_of_chroot_path.assert_called_once_with(
78        '/mnt/host/source/path/to/llvm')
79
80  def testInvalidPrefixForChrootPath(self):
81    package_chroot_path = '/path/to/llvm'
82
83    # Verify the exception is raised when the chroot path does not start with
84    # '/mnt/host/source/'.
85    with self.assertRaises(ValueError) as err:
86      llvm_patch_management._GetRelativePathOfChrootPath(package_chroot_path)
87
88    self.assertEqual(
89        str(err.exception),
90        'Invalid prefix for the chroot path: %s' % package_chroot_path)
91
92  def testValidPrefixForChrootPath(self):
93    package_chroot_path = '/mnt/host/source/path/to/llvm'
94
95    package_rel_path = 'path/to/llvm'
96
97    self.assertEqual(
98        llvm_patch_management._GetRelativePathOfChrootPath(package_chroot_path),
99        package_rel_path)
100
101  # Simulate behavior of 'os.path.isfile()' when the patch metadata file does
102  # not exist.
103  @mock.patch.object(os.path, 'isfile', return_value=False)
104  def testInvalidFileForPatchMetadataPath(self, mock_isfile):
105    abs_path_to_patch_file = '/abs/path/to/files/test.json'
106
107    # Verify the exception is raised when the absolute path to the patch
108    # metadata file does not exist.
109    with self.assertRaises(ValueError) as err:
110      llvm_patch_management._CheckPatchMetadataPath(abs_path_to_patch_file)
111
112    self.assertEqual(
113        str(err.exception),
114        'Invalid file provided: %s' % abs_path_to_patch_file)
115
116    mock_isfile.assert_called_once()
117
118  # Simulate behavior of 'os.path.isfile()' when the absolute path to the
119  # patch metadata file exists.
120  @mock.patch.object(os.path, 'isfile', return_value=True)
121  def testPatchMetadataFileDoesNotEndInJson(self, mock_isfile):
122    abs_path_to_patch_file = '/abs/path/to/files/PATCHES'
123
124    # Verify the exception is raised when the patch metadata file does not end
125    # in '.json'.
126    with self.assertRaises(ValueError) as err:
127      llvm_patch_management._CheckPatchMetadataPath(abs_path_to_patch_file)
128
129    self.assertEqual(
130        str(err.exception),
131        'File does not end in ".json": %s' % abs_path_to_patch_file)
132
133    mock_isfile.assert_called_once()
134
135  @mock.patch.object(os.path, 'isfile')
136  def testValidPatchMetadataFile(self, mock_isfile):
137    abs_path_to_patch_file = '/abs/path/to/files/PATCHES.json'
138
139    # Simulate behavior of 'os.path.isfile()' when the absolute path to the
140    # patch metadata file exists.
141    mock_isfile.return_value = True
142
143    llvm_patch_management._CheckPatchMetadataPath(abs_path_to_patch_file)
144
145    mock_isfile.assert_called_once()
146
147  # Simulate `GetGitHashFrom()` when successfully retrieved the git hash
148  # of the version passed in.
149  @mock.patch.object(
150      get_llvm_hash, 'GetGitHashFrom', return_value='a123testhash1')
151  # Simulate `CreateTempLLVMRepo()` when successfully created a work tree from
152  # the LLVM repo copy in `llvm_tools` directory.
153  @mock.patch.object(get_llvm_hash, 'CreateTempLLVMRepo')
154  # Simulate behavior of `_MoveSrcTreeHEADToGitHash()` when successfully moved
155  # the head pointer to the git hash of the revision.
156  @mock.patch.object(llvm_patch_management, '_MoveSrcTreeHEADToGitHash')
157  @mock.patch.object(llvm_patch_management, 'GetPathToFilesDirectory')
158  @mock.patch.object(llvm_patch_management, '_CheckPatchMetadataPath')
159  def testExceptionIsRaisedWhenUpdatingAPackagesMetadataFile(
160      self, mock_check_patch_metadata_path, mock_get_filesdir_path,
161      mock_move_head_pointer, mock_create_temp_llvm_repo, mock_get_git_hash):
162
163    abs_path_to_patch_file = \
164        '/some/path/to/chroot/some/path/to/filesdir/PATCHES'
165
166    # Simulate the behavior of '_CheckPatchMetadataPath()' when the patch
167    # metadata file in $FILESDIR does not exist or does not end in '.json'.
168    def InvalidPatchMetadataFile(patch_metadata_path):
169      self.assertEqual(patch_metadata_path, abs_path_to_patch_file)
170
171      raise ValueError(
172          'File does not end in ".json": %s' % abs_path_to_patch_file)
173
174    # Use the test function to simulate behavior of '_CheckPatchMetadataPath()'.
175    mock_check_patch_metadata_path.side_effect = InvalidPatchMetadataFile
176
177    abs_path_to_filesdir = '/some/path/to/chroot/some/path/to/filesdir'
178
179    # Simulate the behavior of 'GetPathToFilesDirectory()' when successfully
180    # constructed the absolute path to $FILESDIR of a package.
181    mock_get_filesdir_path.return_value = abs_path_to_filesdir
182
183    temp_work_tree = '/abs/path/to/tmpWorkTree'
184
185    # Simulate the behavior of returning the absolute path to a worktree via
186    # `git worktree add`.
187    mock_create_temp_llvm_repo.return_value.__enter__.return_value.name = \
188        temp_work_tree
189
190    chroot_path = '/some/path/to/chroot'
191    revision = 1000
192    patch_file_name = 'PATCHES'
193    package_name = 'test-package/package1'
194
195    # Verify the exception is raised when a package is constructing the
196    # arguments for the patch manager to update its patch metadata file and an
197    # exception is raised in the process.
198    with self.assertRaises(ValueError) as err:
199      llvm_patch_management.UpdatePackagesPatchMetadataFile(
200          chroot_path, revision, patch_file_name, [package_name],
201          FailureModes.FAIL)
202
203    self.assertEqual(
204        str(err.exception),
205        'File does not end in ".json": %s' % abs_path_to_patch_file)
206
207    mock_get_filesdir_path.assert_called_once_with(chroot_path, package_name)
208
209    mock_get_git_hash.assert_called_once()
210
211    mock_check_patch_metadata_path.assert_called_once()
212
213    mock_move_head_pointer.assert_called_once()
214
215    mock_create_temp_llvm_repo.assert_called_once()
216
217  # Simulate `CleanSrcTree()` when successfully removed changes from the
218  # worktree.
219  @mock.patch.object(patch_manager, 'CleanSrcTree')
220  # Simulate `GetGitHashFrom()` when successfully retrieved the git hash
221  # of the version passed in.
222  @mock.patch.object(
223      get_llvm_hash, 'GetGitHashFrom', return_value='a123testhash1')
224  # Simulate `CreateTempLLVMRepo()` when successfully created a work tree from
225  # the LLVM repo copy in `llvm_tools` directory.
226  @mock.patch.object(get_llvm_hash, 'CreateTempLLVMRepo')
227  # Simulate behavior of `_MoveSrcTreeHEADToGitHash()` when successfully moved
228  # the head pointer to the git hash of the revision.
229  @mock.patch.object(llvm_patch_management, '_MoveSrcTreeHEADToGitHash')
230  @mock.patch.object(llvm_patch_management, 'GetPathToFilesDirectory')
231  @mock.patch.object(llvm_patch_management, '_CheckPatchMetadataPath')
232  @mock.patch.object(patch_manager, 'HandlePatches')
233  def testSuccessfullyRetrievedPatchResults(
234      self, mock_handle_patches, mock_check_patch_metadata_path,
235      mock_get_filesdir_path, mock_move_head_pointer,
236      mock_create_temp_llvm_repo, mock_get_git_hash, mock_clean_src_tree):
237
238    abs_path_to_filesdir = '/some/path/to/chroot/some/path/to/filesdir'
239
240    abs_path_to_patch_file = \
241        '/some/path/to/chroot/some/path/to/filesdir/PATCHES.json'
242
243    # Simulate the behavior of 'GetPathToFilesDirectory()' when successfully
244    # constructed the absolute path to $FILESDIR of a package.
245    mock_get_filesdir_path.return_value = abs_path_to_filesdir
246
247    PatchInfo = namedtuple('PatchInfo', [
248        'applied_patches', 'failed_patches', 'non_applicable_patches',
249        'disabled_patches', 'removed_patches', 'modified_metadata'
250    ])
251
252    # Simulate the behavior of 'HandlePatches()' when successfully iterated
253    # through every patch in the patch metadata file and a dictionary is
254    # returned that contains information about the patches' status.
255    mock_handle_patches.return_value = PatchInfo(
256        applied_patches=['fixes_something.patch'],
257        failed_patches=['disables_output.patch'],
258        non_applicable_patches=[],
259        disabled_patches=[],
260        removed_patches=[],
261        modified_metadata=None)
262
263    temp_work_tree = '/abs/path/to/tmpWorkTree'
264
265    # Simulate the behavior of returning the absolute path to a worktree via
266    # `git worktree add`.
267    mock_create_temp_llvm_repo.return_value.__enter__.return_value.name = \
268        temp_work_tree
269
270    expected_patch_results = {
271        'applied_patches': ['fixes_something.patch'],
272        'failed_patches': ['disables_output.patch'],
273        'non_applicable_patches': [],
274        'disabled_patches': [],
275        'removed_patches': [],
276        'modified_metadata': None
277    }
278
279    chroot_path = '/some/path/to/chroot'
280    revision = 1000
281    patch_file_name = 'PATCHES.json'
282    package_name = 'test-package/package2'
283
284    patch_info = llvm_patch_management.UpdatePackagesPatchMetadataFile(
285        chroot_path, revision, patch_file_name, [package_name],
286        FailureModes.CONTINUE)
287
288    self.assertDictEqual(patch_info, {package_name: expected_patch_results})
289
290    mock_get_filesdir_path.assert_called_once_with(chroot_path, package_name)
291
292    mock_check_patch_metadata_path.assert_called_once_with(
293        abs_path_to_patch_file)
294
295    mock_handle_patches.assert_called_once()
296
297    mock_create_temp_llvm_repo.assert_called_once()
298
299    mock_get_git_hash.assert_called_once()
300
301    mock_move_head_pointer.assert_called_once()
302
303    mock_clean_src_tree.assert_called_once()
304
305
306if __name__ == '__main__':
307  unittest.main()
308