#!/usr/bin/env vpython3
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unittests for mac_util_test.py."""
import json
import subprocess
import unittest
from unittest.mock import patch, MagicMock
import distutils.version
import mac_util
import test_runner_test
class TestIsMacOS13OrHigher(test_runner_test.TestCase):
@patch('mac_util.version')
def test_is_macos_13_or_higher_with_version_12(self, mock_version):
mock_version.return_value = '12.0.0'
self.assertFalse(mac_util.is_macos_13_or_higher())
@patch('mac_util.version')
def test_is_macos_13_or_higher_with_version_13(self, mock_version):
mock_version.return_value = '13.0.0'
self.assertTrue(mac_util.is_macos_13_or_higher())
@patch('mac_util.version')
def test_is_macos_13_or_higher_with_version_14(self, mock_version):
mock_version.return_value = '14.0.0'
self.assertTrue(mac_util.is_macos_13_or_higher())
class TestRunCodesignCheck(test_runner_test.TestCase):
@patch('subprocess.check_call')
def test_run_codesign_check_succeeds(self, mock_check_call):
mock_check_call.return_value = MagicMock()
success, message = mac_util.run_codesign_check("testdir/Xcode.app")
self.assertEqual(success, True)
self.assertEqual(message, None)
@patch('subprocess.check_call')
def test_run_codesign_check_fails(self, mock_check_call):
error = subprocess.CalledProcessError(1, "codesign check return error")
mock_check_call.side_effect = error
success, return_error = mac_util.run_codesign_check("testdir/Xcode.app")
self.assertEqual(success, False)
self.assertEqual(return_error, error)
class TestKillUsbMuxd(test_runner_test.TestCase):
@patch('subprocess.check_call')
def test_run_kill_usbmuxd_succeeds(self, mock_check_call):
mock_check_call.return_value = MagicMock()
mac_util.kill_usbmuxd()
mock_check_call.assert_called_with(
['sudo', '/usr/bin/killall', '-v', 'usbmuxd'])
class TestStopUsbMuxd(test_runner_test.TestCase):
@patch('subprocess.check_call')
def test_run_stop_usbmuxd_succeeds(self, mock_check_call):
mock_check_call.return_value = MagicMock()
mac_util.stop_usbmuxd()
mock_check_call.assert_called_with(
['sudo', '/bin/launchctl', 'stop', 'com.apple.usbmuxd'])
class TestPlistAsDict(test_runner_test.TestCase):
@patch('subprocess.check_output')
def test_complex_dict(self, mock_check_output: MagicMock):
mock_return = '{"key1":"val1","key2":{"key3":[1,2,3],"key4":false}}'.encode(
'utf-8')
mock_check_output.return_value = mock_return
plist, error = mac_util.plist_as_dict('PATH')
expected_plist = {
'key1': 'val1',
'key2': {
'key3': [1, 2, 3],
'key4': False
}
}
self.assertIsNone(error)
self.assertDictEqual(plist, expected_plist)
@patch('subprocess.check_output')
def test_subprocess_call(self, mock_check_output: MagicMock):
mock_check_output.return_value = '{}'.encode('utf-8')
mac_util.plist_as_dict('PATH')
mock_check_output.assert_called_once_with(
['plutil', '-convert', 'json', '-o', '-', 'PATH'])
@patch('subprocess.check_output')
def test_subprocess_raises_error(self, mock_check_output: MagicMock):
mock_check_output.side_effect = subprocess.CalledProcessError(
cmd='cmd', returncode=1)
plist, error = mac_util.plist_as_dict('PATH')
self.assertIsNone(plist)
self.assertIsInstance(error, subprocess.CalledProcessError)
@patch('subprocess.check_output')
def test_json_decode_raises_error(self, mock_check_output: MagicMock):
mock_json_loads = MagicMock()
mock_json_loads.side_effect = json.JSONDecodeError('message', 'doc', 0)
self.mock(json, 'loads', mock_json_loads)
plist, error = mac_util.plist_as_dict('PATH')
self.assertIsNone(plist)
self.assertIsInstance(error, json.JSONDecodeError)
if __name__ == '__main__':
unittest.main()