mirror of https://github.com/Yubico/python-fido2
143 lines
4.9 KiB
Python
143 lines
4.9 KiB
Python
# Copyright 2016 Google Inc. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""Tests for pyu2f.hid.macos."""
|
|
|
|
import ctypes
|
|
import sys
|
|
import mock
|
|
|
|
from fido_host.pyu2f import macos, errors
|
|
|
|
|
|
if sys.version_info[:2] < (2, 7):
|
|
import unittest2 as unittest # pylint: disable=g-import-not-at-top
|
|
else:
|
|
import unittest # pylint: disable=g-import-not-at-top
|
|
|
|
|
|
def init_mock_iokit(mock_iokit):
|
|
# Device open should always return 0 (success)
|
|
mock_iokit.IOHIDDeviceOpen = mock.Mock(return_value=0)
|
|
mock_iokit.IOHIDDeviceSetReport = mock.Mock(return_value=0)
|
|
mock_iokit.IOHIDDeviceCreate = mock.Mock(return_value='handle')
|
|
|
|
|
|
def init_mock_cf(mock_cf):
|
|
mock_cf.CFGetTypeID = mock.Mock(return_value=123)
|
|
mock_cf.CFNumberGetTypeID = mock.Mock(return_value=123)
|
|
mock_cf.CFStringGetTypeID = mock.Mock(return_value=123)
|
|
|
|
|
|
def init_mock_get_int_property(mock_get_int_property):
|
|
mock_get_int_property.return_value = 64
|
|
|
|
|
|
class MacOsTest(unittest.TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
macos.MacOsHidDevice.__del__ = lambda x: None
|
|
|
|
@mock.patch.object(macos.threading, 'Thread')
|
|
@mock.patch.multiple(macos, iokit=mock.DEFAULT, cf=mock.DEFAULT,
|
|
GetDeviceIntProperty=mock.DEFAULT)
|
|
def testInitHidDevice(self, thread, iokit, cf, GetDeviceIntProperty): # pylint: disable=invalid-name
|
|
init_mock_iokit(iokit)
|
|
init_mock_cf(cf)
|
|
init_mock_get_int_property(GetDeviceIntProperty)
|
|
|
|
device = macos.MacOsHidDevice('fakepath')
|
|
|
|
self.assertEqual(64, device.GetInReportDataLength())
|
|
self.assertEqual(64, device.GetOutReportDataLength())
|
|
|
|
@mock.patch.object(macos.threading, 'Thread')
|
|
@mock.patch.multiple(macos, iokit=mock.DEFAULT, cf=mock.DEFAULT,
|
|
GetDeviceIntProperty=mock.DEFAULT)
|
|
def testCallWriteSuccess(self, thread, iokit, cf, GetDeviceIntProperty): # pylint: disable=invalid-name
|
|
init_mock_iokit(iokit)
|
|
init_mock_cf(cf)
|
|
init_mock_get_int_property(GetDeviceIntProperty)
|
|
|
|
device = macos.MacOsHidDevice('fakepath')
|
|
|
|
# Write 64 bytes to device
|
|
data = bytearray(range(64))
|
|
|
|
# Write to device
|
|
device.Write(data)
|
|
|
|
# Validate that write calls into IOKit
|
|
set_report_call_args = iokit.IOHIDDeviceSetReport.call_args
|
|
self.assertIsNotNone(set_report_call_args)
|
|
|
|
set_report_call_pos_args = iokit.IOHIDDeviceSetReport.call_args[0]
|
|
self.assertEqual(len(set_report_call_pos_args), 5)
|
|
self.assertEqual(set_report_call_pos_args[0], 'handle')
|
|
self.assertEqual(set_report_call_pos_args[1], 1)
|
|
self.assertEqual(set_report_call_pos_args[2], 0)
|
|
self.assertEqual(set_report_call_pos_args[4], 64)
|
|
|
|
report_buffer = set_report_call_pos_args[3]
|
|
self.assertEqual(len(report_buffer), 64)
|
|
self.assertEqual(bytearray(report_buffer), data, 'Data sent to '
|
|
'IOHIDDeviceSetReport should match data sent to the '
|
|
'device')
|
|
|
|
@mock.patch.object(macos.threading, 'Thread')
|
|
@mock.patch.multiple(macos, iokit=mock.DEFAULT, cf=mock.DEFAULT,
|
|
GetDeviceIntProperty=mock.DEFAULT)
|
|
def testCallWriteFailure(self, thread, iokit, cf, GetDeviceIntProperty): # pylint: disable=invalid-name
|
|
init_mock_iokit(iokit)
|
|
init_mock_cf(cf)
|
|
init_mock_get_int_property(GetDeviceIntProperty)
|
|
|
|
# Make set report call return failure (non-zero)
|
|
iokit.IOHIDDeviceSetReport.return_value = -1
|
|
|
|
device = macos.MacOsHidDevice('fakepath')
|
|
|
|
# Write 64 bytes to device
|
|
data = bytearray(range(64))
|
|
|
|
# Write should throw an OsHidError exception
|
|
with self.assertRaises(errors.OsHidError):
|
|
device.Write(data)
|
|
|
|
@mock.patch.object(macos.threading, 'Thread')
|
|
@mock.patch.multiple(macos, iokit=mock.DEFAULT, cf=mock.DEFAULT,
|
|
GetDeviceIntProperty=mock.DEFAULT)
|
|
def testCallReadSuccess(self, thread, iokit, cf, GetDeviceIntProperty): # pylint: disable=invalid-name
|
|
init_mock_iokit(iokit)
|
|
init_mock_cf(cf)
|
|
init_mock_get_int_property(GetDeviceIntProperty)
|
|
|
|
device = macos.MacOsHidDevice('fakepath')
|
|
|
|
# Call callback for IN report
|
|
report = (ctypes.c_uint8 * 64)()
|
|
report[:] = range(64)[:]
|
|
q = device.read_queue
|
|
macos.HidReadCallback(q, None, None, None, 0, report, 64)
|
|
|
|
# Device read should return the callback data
|
|
read_result = device.Read()
|
|
self.assertEqual(read_result, list(range(64)), 'Read data should match data '
|
|
'passed into the callback')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|