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