Exemplo n.º 1
0
 def setup_mock_log_dict(self):
     # There is one user, named test
     homedirs = [HomeDir('test', '/Users/test')]
     self.expected_file_info = {
         'md5': '8675309',
         'sha1': 'babababa',
         'sha2': '11'
     }
     with contextlib.nested(
         patch.object(Logger, 'log_dict'),
         patch('osxcollector.osxcollector._get_homedirs', autospec=True, return_value=homedirs),
         patch('osxcollector.osxcollector._get_file_info', autospec=True, return_value=self.expected_file_info)
     ) as (self.mock_log_dict, self.mock_get_homedirs, self.mock_get_file_info):
         self.collector = Collector()
         yield
 def setup_mock_log_dict(self):
     # There is one user, named test
     homedirs = [HomeDir("test", "/Users/test")]
     self.expected_file_info = {"md5": "8675309", "sha1": "babababa", "sha2": "11"}
     with contextlib.nested(
         mock.patch.object(Logger, "log_dict"),
         mock.patch("osxcollector.osxcollector._get_homedirs", autospec=True, return_value=homedirs),
         mock.patch("osxcollector.osxcollector._get_file_info", autospec=True, return_value=self.expected_file_info),
     ) as (self.mock_log_dict, self.mock_get_homedirs, self.mock_get_file_info):
         self.collector = Collector()
         yield
Exemplo n.º 3
0
class CollectorTestCase(T.TestCase):

    @T.setup_teardown
    def setup_mock_log_dict(self):
        # There is one user, named test
        homedirs = [HomeDir('test', '/Users/test')]
        self.expected_file_info = {
            'md5': '8675309',
            'sha1': 'babababa',
            'sha2': '11'
        }
        with contextlib.nested(
            patch.object(Logger, 'log_dict'),
            patch('osxcollector.osxcollector._get_homedirs', autospec=True, return_value=homedirs),
            patch('osxcollector.osxcollector._get_file_info', autospec=True, return_value=self.expected_file_info)
        ) as (self.mock_log_dict, self.mock_get_homedirs, self.mock_get_file_info):
            self.collector = Collector()
            yield

    def test_log_items_in_plist(self):
        plist = {
            'system': {
                'name': ['os x']
            },
            'version': {
                'minor': '3'
            }
        }

        self.collector._log_items_in_plist(plist, 'system.name')
        self.mock_log_dict.assert_called_with('os x')

    def _really_expected_file_info(self, expected):
        really_expected = {}
        really_expected.update(expected)
        really_expected.update(self.expected_file_info)
        return really_expected

    def _test_log_launch_agents(self, dir_path, expected):
        expected = self._really_expected_file_info(expected)

        self.collector._log_launch_agents(dir_path)
        self.mock_log_dict.assert_called_with(expected)

    def test_log_launch_agents_just_program_no_arguments(self):
        # ProgramArguments value contains only program name
        expected = {
            'label': 'com.apple.csuseragent',
            'program': '/System/Library/CoreServices/CSUserAgent',
            'osxcollector_plist': 'tests/data/launch_agents/csuseragent/csuseragent.plist'
        }
        self._test_log_launch_agents('tests/data/launch_agents/csuseragent/', expected)

    def test_log_launch_agents_program(self):
        # no ProgramArguments, program name is under Program key in plist
        expected = {
            'label': 'com.apple.appleseed.seedusaged',
            'program': '/System/Library/CoreServices/Feedback Assistant.app/Contents/Library/LaunchServices/seedusaged',
            'osxcollector_plist': 'tests/data/launch_agents/seedusaged/seedusaged.plist'
        }
        self._test_log_launch_agents('tests/data/launch_agents/seedusaged/', expected)

    def test_log_launch_agents_program_and_arguments(self):
        # ProgramArguments value contains both program name arguments
        expected = {
            'label': 'com.apple.VoiceOver',
            'program': '/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOver',
            'arguments': ['launchd', '-s'],
            'osxcollector_plist': 'tests/data/launch_agents/voice_over/com.apple.VoiceOver.plist'
        }
        self._test_log_launch_agents('tests/data/launch_agents/voice_over/', expected)

    def test_log_packages_in_dir(self):
        expected = {
            'osxcollector_plist_path': 'tests/data/packages/Digital Hub Scripting.osax/Contents/Info.plist',
            'osxcollector_bundle_id': 'com.apple.osax.digihub'
        }
        expected = self._really_expected_file_info(expected)

        self.collector._log_packages_in_dir('tests/data/packages/')
        self.mock_log_dict.assert_called_with(expected)

    def test_log_startup_items(self):
        list_of_files_in_dir = ['StartupParameters.plist']
        plist = {
            'Provides': ['test_service']
        }
        with contextlib.nested(
            patch('os.path.isdir', autospec=True, return_value=True),
            patch('osxcollector.osxcollector.listdir', autospec=True, return_value=list_of_files_in_dir),
            patch.object(Collector, '_read_plist', autospec=True, return_value=plist)
        ):
            self.collector._log_startup_items('test_dir')
            self.mock_log_dict.assert_called_with(self.expected_file_info)

    def test_log_user_login_items(self):
        plist_path = '/Users/test/Library/Preferences/com.apple.loginitems.plist'
        login_item = {
            'Name': 'test-login-item'
        }
        plist = {
            'SessionItems': {
                'CustomListItems': [login_item]
            }
        }
        with patch.object(Collector, '_read_plist', autospec=True, return_value=plist) as mock_read_plist:
            self.collector._log_user_login_items()

            mock_read_plist.assert_called_with(self.collector, plist_path)
            self.mock_log_dict.assert_called_with(login_item)

    def test_collect_accounts_recent_items(self):
        plist_path = '/Users/test/Library/Preferences/com.apple.recentitems.plist'
        plist = {
            'RecentServers': {
                'CustomListItems': [
                    {'Name': 'presidio'},
                    {'Name': 'marina'},
                    {'Name': 'sunset'}
                ]
            },
            'RecentDocuments': {
                'CustomListItems': [
                    {'Name': 'russian_hill.jpg'},
                    {'Name': 'nob_hill.jpg'},
                    {'Name': 'rincon_hill.jpg'}
                ]
            },
            'RecentApplications': {
                'CustomListItems': [
                    {'Name': 'Golden Gate Park'},
                    {'Name': 'Glen Park'},
                    {'Name': 'Jordan Park'}
                ]
            },
            'Hosts': {
                'CustomListItems': [
                    {'Name': 'South of Market', 'URL': 'afp://sfo/soma'},
                    {'Name': 'Financial District', 'URL': 'afp://sfo/fidi'},
                    {'Name': 'North Beach', 'URL': 'afp://sfo/nobe'}
                ]
            }
        }
        recents = [
            {'server_name': 'presidio'},
            {'server_name': 'marina'},
            {'server_name': 'sunset'},
            {'document_name': 'russian_hill.jpg'},
            {'document_name': 'nob_hill.jpg'},
            {'document_name': 'rincon_hill.jpg'},
            {'application_name': 'Golden Gate Park'},
            {'application_name': 'Glen Park'},
            {'application_name': 'Jordan Park'},
            {'host_name': 'South of Market', 'host_url': 'afp://sfo/soma'},
            {'host_name': 'Financial District', 'host_url': 'afp://sfo/fidi'},
            {'host_name': 'North Beach', 'host_url': 'afp://sfo/nobe'}
        ]
        with patch.object(Collector, '_read_plist', autospec=True, return_value=plist) as mock_read_plist:
            self.collector._collect_accounts_recent_items()

            mock_read_plist.assert_called_with(self.collector, plist_path)
            for recent in recents:
                self.mock_log_dict.assert_any_call(recent)

    def assert_log(self, plist_path, expected_log):
        plist = self.collector._read_plist(plist_path)
        T.assert_equals({}, plist)
        self.mock_log_dict.assert_called_once_with(expected_log)

    def test_read_plist_file_not_found(self):
        plist_path = 'tests/data/plists/non_existing.plist'
        warning = 'plist file not found. plist_path[{0}]'.format(plist_path)
        expected_log = {
            'osxcollector_warn': warning
        }
        self.assert_log(plist_path, expected_log)

    def test_read_plist_empty(self):
        plist_path = 'tests/data/plists/empty.plist'
        warning = 'Empty plist. plist_path[{0}]'.format(plist_path)
        expected_log = {
            'osxcollector_warn': warning
        }
        self.assert_log(plist_path, expected_log)

    def test_read_plist_invalid_format(self):
        plist_path = 'tests/data/plists/invalid_format.plist'
        error = 'Unable to parse plist: [The data couldn\xe2\x80\x99t be read because it isn\xe2\x80\x99t in the correct format.].' \
            + ' plist_path[{0}]'.format(plist_path)
        expected_log = {
            'osxcollector_error': error
        }
        self.assert_log(plist_path, expected_log)

    def _mock_sqlite_connection(self):
        # context manager mock based on http://stackoverflow.com/a/3268310
        self.connect_mock = MagicMock()
        osxcollector.osxcollector.connect = self.connect_mock
        context_manager_mock = self.connect_mock.return_value
        self.conn_mock = context_manager_mock.__enter__.return_value

        self.cursor_mock = self.conn_mock.cursor.return_value
        tables = [
            ('boom', 'duh', 'fruits'),
            ('duff', 'poom', 'veggies'),
        ]
        rows_fruits = [
            ('apple', 'green'),
            ('banana', 'yellow'),
            ('cherry', 'red'),
        ]
        rows_veggies = [
            ('carrot', 'orange'),
            ('radish', 'red'),
        ]
        self.cursor_mock.fetchall.side_effect = [
            tables, rows_fruits, rows_veggies]
        self.cursor_mock.description = [['name'], ['color']]

    def test_log_sqlite_db(self):
        self._mock_sqlite_connection()

        with patch('os.path.isfile', return_value=True) as isfile_mock:
            self.collector._log_sqlite_db(
                '/Users/test/sqlite/db/panama_papers')

        isfile_mock.assert_called_once_with(
            '/Users/test/sqlite/db/panama_papers')
        self.connect_mock.assert_called_once_with(
            '/Users/test/sqlite/db/panama_papers')
        T.assert_truthy(self.conn_mock.cursor.called)

        expected_execute_calls = [
            call('SELECT * from sqlite_master WHERE type = "table"'),
            call('SELECT * from fruits'),
            call('SELECT * from veggies'),
        ]
        T.assert_equals(
            expected_execute_calls, self.cursor_mock.execute.call_args_list)

        expected_log_dict_calls = [
            call({
                'name': 'apple',
                'color': 'green',
            }),
            call({
                'name': 'banana',
                'color': 'yellow',
            }),
            call({
                'name': 'cherry',
                'color': 'red',
            }),
            call({
                'name': 'carrot',
                'color': 'orange',
            }),
            call({
                'name': 'radish',
                'color': 'red',
            }),
        ]
        T.assert_equals(
            expected_log_dict_calls, self.mock_log_dict.call_args_list)

    def test_log_sqlite_db_ignore(self):
        self._mock_sqlite_connection()

        with patch('os.path.isfile', return_value=True):
            self.collector._log_sqlite_db(
                '/Users/test/sqlite/db/cayman_airways',
                ignore={'fruits': ['color']})

        expected_log_dict_calls = [
            call({
                'name': 'apple',
            }),
            call({
                'name': 'banana',
            }),
            call({
                'name': 'cherry',
            }),
            call({
                'name': 'carrot',
                'color': 'orange',
            }),
            call({
                'name': 'radish',
                'color': 'red',
            }),
        ]
        T.assert_equals(
            expected_log_dict_calls, self.mock_log_dict.call_args_list)
Exemplo n.º 4
0
class CollectorTestCase(T.TestCase):
    @T.setup_teardown
    def setup_mock_log_dict(self):
        # There is one user, named test
        homedirs = [HomeDir('test', '/Users/test')]
        self.expected_file_info = {
            'md5': '8675309',
            'sha1': 'babababa',
            'sha2': '11'
        }
        with contextlib.nested(
                mock.patch.object(Logger, 'log_dict'),
                mock.patch('osxcollector.osxcollector._get_homedirs',
                           autospec=True,
                           return_value=homedirs),
                mock.patch('osxcollector.osxcollector._get_file_info',
                           autospec=True,
                           return_value=self.expected_file_info)) as (
                               self.mock_log_dict, self.mock_get_homedirs,
                               self.mock_get_file_info):
            self.collector = Collector()
            yield

    def test_log_items_in_plist(self):
        plist = {'system': {'name': ['os x']}, 'version': {'minor': '3'}}

        self.collector._log_items_in_plist(plist, 'system.name')
        self.mock_log_dict.assert_called_with('os x')

    def _really_expected_file_info(self, expected):
        really_expected = {}
        really_expected.update(expected)
        really_expected.update(self.expected_file_info)
        return really_expected

    def _test_log_launch_agents(self, dir_path, expected):
        expected = self._really_expected_file_info(expected)

        self.collector._log_launch_agents(dir_path)
        self.mock_log_dict.assert_called_with(expected)

    def test_log_launch_agents_just_program_no_arguments(self):
        # ProgramArguments value contains only program name
        expected = {
            'label':
            'com.apple.csuseragent',
            'program':
            '/System/Library/CoreServices/CSUserAgent',
            'osxcollector_plist':
            'tests/data/launch_agents/csuseragent/csuseragent.plist'
        }
        self._test_log_launch_agents('tests/data/launch_agents/csuseragent/',
                                     expected)

    def test_log_launch_agents_program(self):
        # no ProgramArguments, program name is under Program key in plist
        expected = {
            'label':
            'com.apple.appleseed.seedusaged',
            'program':
            '/System/Library/CoreServices/Feedback Assistant.app/Contents/Library/LaunchServices/seedusaged',
            'osxcollector_plist':
            'tests/data/launch_agents/seedusaged/seedusaged.plist'
        }
        self._test_log_launch_agents('tests/data/launch_agents/seedusaged/',
                                     expected)

    def test_log_launch_agents_program_and_arguments(self):
        # ProgramArguments value contains both program name arguments
        expected = {
            'label':
            'com.apple.VoiceOver',
            'program':
            '/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOver',
            'arguments': ['launchd', '-s'],
            'osxcollector_plist':
            'tests/data/launch_agents/voice_over/com.apple.VoiceOver.plist'
        }
        self._test_log_launch_agents('tests/data/launch_agents/voice_over/',
                                     expected)

    def test_log_packages_in_dir(self):
        expected = {
            'osxcollector_plist_path':
            'tests/data/packages/Digital Hub Scripting.osax/Contents/Info.plist',
            'osxcollector_bundle_id': 'com.apple.osax.digihub'
        }
        expected = self._really_expected_file_info(expected)

        self.collector._log_packages_in_dir('tests/data/packages/')
        self.mock_log_dict.assert_called_with(expected)

    def test_log_startup_items(self):
        list_of_files_in_dir = ['StartupParameters.plist']
        plist = {'Provides': ['test_service']}
        with contextlib.nested(
                mock.patch('os.path.isdir', autospec=True, return_value=True),
                mock.patch('osxcollector.osxcollector.listdir',
                           autospec=True,
                           return_value=list_of_files_in_dir),
                mock.patch.object(Collector,
                                  '_read_plist',
                                  autospec=True,
                                  return_value=plist)):
            self.collector._log_startup_items('test_dir')
            self.mock_log_dict.assert_called_with(self.expected_file_info)

    def test_log_user_login_items(self):
        plist_path = '/Users/test/Library/Preferences/com.apple.loginitems.plist'
        login_item = {'Name': 'test-login-item'}
        plist = {'SessionItems': {'CustomListItems': [login_item]}}
        with mock.patch.object(Collector,
                               '_read_plist',
                               autospec=True,
                               return_value=plist) as mock_read_plist:
            self.collector._log_user_login_items()

            mock_read_plist.assert_called_with(self.collector, plist_path)
            self.mock_log_dict.assert_called_with(login_item)

    def test_collect_accounts_recent_items(self):
        plist_path = '/Users/test/Library/Preferences/com.apple.recentitems.plist'
        plist = {
            'RecentServers': {
                'CustomListItems': [{
                    'Name': 'presidio'
                }, {
                    'Name': 'marina'
                }, {
                    'Name': 'sunset'
                }]
            },
            'RecentDocuments': {
                'CustomListItems': [{
                    'Name': 'russian_hill.jpg'
                }, {
                    'Name': 'nob_hill.jpg'
                }, {
                    'Name': 'rincon_hill.jpg'
                }]
            },
            'RecentApplications': {
                'CustomListItems': [{
                    'Name': 'Golden Gate Park'
                }, {
                    'Name': 'Glen Park'
                }, {
                    'Name': 'Jordan Park'
                }]
            },
            'Hosts': {
                'CustomListItems': [{
                    'Name': 'South of Market',
                    'URL': 'afp://sfo/soma'
                }, {
                    'Name': 'Financial District',
                    'URL': 'afp://sfo/fidi'
                }, {
                    'Name': 'North Beach',
                    'URL': 'afp://sfo/nobe'
                }]
            }
        }
        recents = [{
            'server_name': 'presidio'
        }, {
            'server_name': 'marina'
        }, {
            'server_name': 'sunset'
        }, {
            'document_name': 'russian_hill.jpg'
        }, {
            'document_name': 'nob_hill.jpg'
        }, {
            'document_name': 'rincon_hill.jpg'
        }, {
            'application_name': 'Golden Gate Park'
        }, {
            'application_name': 'Glen Park'
        }, {
            'application_name': 'Jordan Park'
        }, {
            'host_name': 'South of Market',
            'host_url': 'afp://sfo/soma'
        }, {
            'host_name': 'Financial District',
            'host_url': 'afp://sfo/fidi'
        }, {
            'host_name': 'North Beach',
            'host_url': 'afp://sfo/nobe'
        }]
        with mock.patch.object(Collector,
                               '_read_plist',
                               autospec=True,
                               return_value=plist) as mock_read_plist:
            self.collector._collect_accounts_recent_items()

            mock_read_plist.assert_called_with(self.collector, plist_path)
            for recent in recents:
                self.mock_log_dict.assert_any_call(recent)

    def assert_log(self, plist_path, expected_log):
        plist = self.collector._read_plist(plist_path)
        T.assert_equals({}, plist)
        self.mock_log_dict.assert_called_once_with(expected_log)

    def test_read_plist_file_not_found(self):
        plist_path = 'tests/data/plists/non_existing.plist'
        warning = 'plist file not found. plist_path[{0}]'.format(plist_path)
        expected_log = {'osxcollector_warn': warning}
        self.assert_log(plist_path, expected_log)

    def test_read_plist_empty(self):
        plist_path = 'tests/data/plists/empty.plist'
        warning = 'Empty plist. plist_path[{0}]'.format(plist_path)
        expected_log = {'osxcollector_warn': warning}
        self.assert_log(plist_path, expected_log)

    def test_read_plist_invalid_format(self):
        plist_path = 'tests/data/plists/invalid_format.plist'
        error = 'Unable to parse plist: [The data couldn\xe2\x80\x99t be read because it isn\xe2\x80\x99t in the correct format.].' \
            + ' plist_path[{0}]'.format(plist_path)
        expected_log = {'osxcollector_error': error}
        self.assert_log(plist_path, expected_log)
class CollectorTestCase(T.TestCase):

    @T.setup_teardown
    def setup_mock_log_dict(self):
        # There is one user, named test
        homedirs = [HomeDir('test', '/Users/test')]
        self.expected_file_info = {
            'md5': '8675309',
            'sha1': 'babababa',
            'sha2': '11'
        }
        with contextlib.nested(
            mock.patch.object(Logger, 'log_dict'),
            mock.patch('osxcollector.osxcollector._get_homedirs', autospec=True, return_value=homedirs),
            mock.patch('osxcollector.osxcollector._get_file_info', autospec=True, return_value=self.expected_file_info)
        ) as (self.mock_log_dict, self.mock_get_homedirs, self.mock_get_file_info):
            self.collector = Collector()
            yield

    def test_log_items_in_plist(self):
        plist = {
            'system': {
                'name': ['os x']
            },
            'version': {
                'minor': '3'
            }
        }

        self.collector._log_items_in_plist(plist, 'system.name')
        self.mock_log_dict.assert_called_with('os x')

    def _really_expected_file_info(self, expected):
        really_expected = {}
        really_expected.update(expected)
        really_expected.update(self.expected_file_info)
        return really_expected

    def _test_log_launch_agents(self, dir_path, expected):
        expected = self._really_expected_file_info(expected)

        self.collector._log_launch_agents(dir_path)
        self.mock_log_dict.assert_called_with(expected)

    def test_log_launch_agents_just_program_no_arguments(self):
        # ProgramArguments value contains only program name
        expected = {
            'label': 'com.apple.csuseragent',
            'program': '/System/Library/CoreServices/CSUserAgent',
            'osxcollector_plist': 'tests/data/launch_agents/csuseragent/csuseragent.plist'
        }
        self._test_log_launch_agents('tests/data/launch_agents/csuseragent/', expected)

    def test_log_launch_agents_program(self):
        # no ProgramArguments, program name is under Program key in plist
        expected = {
            'label': 'com.apple.appleseed.seedusaged',
            'program': '/System/Library/CoreServices/Feedback Assistant.app/Contents/Library/LaunchServices/seedusaged',
            'osxcollector_plist': 'tests/data/launch_agents/seedusaged/seedusaged.plist'
        }
        self._test_log_launch_agents('tests/data/launch_agents/seedusaged/', expected)

    def test_log_launch_agents_program_and_arguments(self):
        # ProgramArguments value contains both program name arguments
        expected = {
            'label': 'com.apple.VoiceOver',
            'program': '/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOver',
            'arguments': ['launchd', '-s'],
            'osxcollector_plist': 'tests/data/launch_agents/voice_over/com.apple.VoiceOver.plist'
        }
        self._test_log_launch_agents('tests/data/launch_agents/voice_over/', expected)

    def test_log_packages_in_dir(self):
        expected = {
            'osxcollector_plist_path': 'tests/data/packages/Digital Hub Scripting.osax/Contents/Info.plist',
            'osxcollector_bundle_id': 'com.apple.osax.digihub'
        }
        expected = self._really_expected_file_info(expected)

        self.collector._log_packages_in_dir('tests/data/packages/')
        self.mock_log_dict.assert_called_with(expected)

    def test_log_startup_items(self):
        list_of_files_in_dir = ['StartupParameters.plist']
        plist = {
            'Provides': ['test_service']
        }
        with contextlib.nested(
            mock.patch('os.path.isdir', autospec=True, return_value=True),
            mock.patch('osxcollector.osxcollector.listdir', autospec=True, return_value=list_of_files_in_dir),
            mock.patch.object(Collector, '_read_plist', autospec=True, return_value=plist)
        ):
            self.collector._log_startup_items('test_dir')
            self.mock_log_dict.assert_called_with(self.expected_file_info)

    def test_log_user_login_items(self):
        plist_path = '/Users/test/Library/Preferences/com.apple.loginitems.plist'
        login_item = {
            'Name': 'test-login-item'
        }
        plist = {
            'SessionItems': {
                'CustomListItems': [login_item]
            }
        }
        with mock.patch.object(Collector, '_read_plist', autospec=True, return_value=plist) as mock_read_plist:
            self.collector._log_user_login_items()

            mock_read_plist.assert_called_with(self.collector, plist_path)
            self.mock_log_dict.assert_called_with(login_item)

    def test_collect_accounts_recent_items(self):
        plist_path = '/Users/test/Library/Preferences/com.apple.recentitems.plist'
        plist = {
            'RecentServers': {
                'CustomListItems': [
                    {'Name': 'presidio'},
                    {'Name': 'marina'},
                    {'Name': 'sunset'}
                ]
            },
            'RecentDocuments': {
                'CustomListItems': [
                    {'Name': 'russian_hill.jpg'},
                    {'Name': 'nob_hill.jpg'},
                    {'Name': 'rincon_hill.jpg'}
                ]
            },
            'RecentApplications': {
                'CustomListItems': [
                    {'Name': 'Golden Gate Park'},
                    {'Name': 'Glen Park'},
                    {'Name': 'Jordan Park'}
                ]
            },
            'Hosts': {
                'CustomListItems': [
                    {'Name': 'South of Market', 'URL': 'afp://sfo/soma'},
                    {'Name': 'Financial District', 'URL': 'afp://sfo/fidi'},
                    {'Name': 'North Beach', 'URL': 'afp://sfo/nobe'}
                ]
            }
        }
        recents = [
            {'server_name': 'presidio'},
            {'server_name': 'marina'},
            {'server_name': 'sunset'},
            {'document_name': 'russian_hill.jpg'},
            {'document_name': 'nob_hill.jpg'},
            {'document_name': 'rincon_hill.jpg'},
            {'application_name': 'Golden Gate Park'},
            {'application_name': 'Glen Park'},
            {'application_name': 'Jordan Park'},
            {'host_name': 'South of Market', 'host_url': 'afp://sfo/soma'},
            {'host_name': 'Financial District', 'host_url': 'afp://sfo/fidi'},
            {'host_name': 'North Beach', 'host_url': 'afp://sfo/nobe'}
        ]
        with mock.patch.object(Collector, '_read_plist', autospec=True, return_value=plist) as mock_read_plist:
            self.collector._collect_accounts_recent_items()

            mock_read_plist.assert_called_with(self.collector, plist_path)
            for recent in recents:
                self.mock_log_dict.assert_any_call(recent)

    def assert_log(self, plist_path, expected_log):
        plist = self.collector._read_plist(plist_path)
        T.assert_equals({}, plist)
        self.mock_log_dict.assert_called_once_with(expected_log)

    def test_read_plist_file_not_found(self):
        plist_path = 'tests/data/plists/non_existing.plist'
        warning = 'plist file not found. plist_path[{0}]'.format(plist_path)
        expected_log = {
            'osxcollector_warn': warning
        }
        self.assert_log(plist_path, expected_log)

    def test_read_plist_empty(self):
        plist_path = 'tests/data/plists/empty.plist'
        warning = 'Empty plist. plist_path[{0}]'.format(plist_path)
        expected_log = {
            'osxcollector_warn': warning
        }
        self.assert_log(plist_path, expected_log)

    def test_read_plist_invalid_format(self):
        plist_path = 'tests/data/plists/invalid_format.plist'
        error = 'Unable to parse plist: [The data couldn\xe2\x80\x99t be read because it isn\xe2\x80\x99t in the correct format.].' \
            + ' plist_path[{0}]'.format(plist_path)
        expected_log = {
            'osxcollector_error': error
        }
        self.assert_log(plist_path, expected_log)
class CollectorTestCase(T.TestCase):
    @T.setup_teardown
    def setup_mock_log_dict(self):
        # There is one user, named test
        homedirs = [HomeDir("test", "/Users/test")]
        self.expected_file_info = {"md5": "8675309", "sha1": "babababa", "sha2": "11"}
        with contextlib.nested(
            mock.patch.object(Logger, "log_dict"),
            mock.patch("osxcollector.osxcollector._get_homedirs", autospec=True, return_value=homedirs),
            mock.patch("osxcollector.osxcollector._get_file_info", autospec=True, return_value=self.expected_file_info),
        ) as (self.mock_log_dict, self.mock_get_homedirs, self.mock_get_file_info):
            self.collector = Collector()
            yield

    def test_log_items_in_plist(self):
        plist = {"system": {"name": ["os x"]}, "version": {"minor": "3"}}

        self.collector._log_items_in_plist(plist, "system.name")
        self.mock_log_dict.assert_called_with("os x")

    def _really_expected_file_info(self, expected):
        really_expected = {}
        really_expected.update(expected)
        really_expected.update(self.expected_file_info)
        return really_expected

    def _test_log_launch_agents(self, dir_path, expected):
        expected = self._really_expected_file_info(expected)

        self.collector._log_launch_agents(dir_path)
        self.mock_log_dict.assert_called_with(expected)

    def test_log_launch_agents_just_program_no_arguments(self):
        # ProgramArguments value contains only program name
        expected = {
            "label": "com.apple.csuseragent",
            "program": "/System/Library/CoreServices/CSUserAgent",
            "osxcollector_plist": "tests/data/launch_agents/csuseragent/csuseragent.plist",
        }
        self._test_log_launch_agents("tests/data/launch_agents/csuseragent/", expected)

    def test_log_launch_agents_program(self):
        # no ProgramArguments, program name is under Program key in plist
        expected = {
            "label": "com.apple.appleseed.seedusaged",
            "program": "/System/Library/CoreServices/Feedback Assistant.app/Contents/Library/LaunchServices/seedusaged",
            "osxcollector_plist": "tests/data/launch_agents/seedusaged/seedusaged.plist",
        }
        self._test_log_launch_agents("tests/data/launch_agents/seedusaged/", expected)

    def test_log_launch_agents_program_and_arguments(self):
        # ProgramArguments value contains both program name arguments
        expected = {
            "label": "com.apple.VoiceOver",
            "program": "/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOver",
            "arguments": ["launchd", "-s"],
            "osxcollector_plist": "tests/data/launch_agents/voice_over/com.apple.VoiceOver.plist",
        }
        self._test_log_launch_agents("tests/data/launch_agents/voice_over/", expected)

    def test_log_packages_in_dir(self):
        expected = {
            "osxcollector_plist_path": "tests/data/packages/Digital Hub Scripting.osax/Contents/Info.plist",
            "osxcollector_bundle_id": "com.apple.osax.digihub",
        }
        expected = self._really_expected_file_info(expected)

        self.collector._log_packages_in_dir("tests/data/packages/")
        self.mock_log_dict.assert_called_with(expected)

    def test_log_startup_items(self):
        list_of_files_in_dir = ["StartupParameters.plist"]
        plist = {"Provides": ["test_service"]}
        with contextlib.nested(
            mock.patch("os.path.isdir", autospec=True, return_value=True),
            mock.patch("osxcollector.osxcollector.listdir", autospec=True, return_value=list_of_files_in_dir),
            mock.patch.object(Collector, "_read_plist", autospec=True, return_value=plist),
        ):
            self.collector._log_startup_items("test_dir")
            self.mock_log_dict.assert_called_with(self.expected_file_info)

    def test_log_user_login_items(self):
        plist_path = "/Users/test/Library/Preferences/com.apple.loginitems.plist"
        login_item = {"Name": "test-login-item"}
        plist = {"SessionItems": {"CustomListItems": [login_item]}}
        with mock.patch.object(Collector, "_read_plist", autospec=True, return_value=plist) as mock_read_plist:
            self.collector._log_user_login_items()

            mock_read_plist.assert_called_with(self.collector, plist_path)
            self.mock_log_dict.assert_called_with(login_item)

    def test_collect_accounts_recent_items(self):
        plist_path = "/Users/test/Library/Preferences/com.apple.recentitems.plist"
        plist = {
            "RecentServers": {"CustomListItems": [{"Name": "presidio"}, {"Name": "marina"}, {"Name": "sunset"}]},
            "RecentDocuments": {
                "CustomListItems": [{"Name": "russian_hill.jpg"}, {"Name": "nob_hill.jpg"}, {"Name": "rincon_hill.jpg"}]
            },
            "RecentApplications": {
                "CustomListItems": [{"Name": "Golden Gate Park"}, {"Name": "Glen Park"}, {"Name": "Jordan Park"}]
            },
            "Hosts": {
                "CustomListItems": [
                    {"Name": "South of Market", "URL": "afp://sfo/soma"},
                    {"Name": "Financial District", "URL": "afp://sfo/fidi"},
                    {"Name": "North Beach", "URL": "afp://sfo/nobe"},
                ]
            },
        }
        recents = [
            {"server_name": "presidio"},
            {"server_name": "marina"},
            {"server_name": "sunset"},
            {"document_name": "russian_hill.jpg"},
            {"document_name": "nob_hill.jpg"},
            {"document_name": "rincon_hill.jpg"},
            {"application_name": "Golden Gate Park"},
            {"application_name": "Glen Park"},
            {"application_name": "Jordan Park"},
            {"host_name": "South of Market", "host_url": "afp://sfo/soma"},
            {"host_name": "Financial District", "host_url": "afp://sfo/fidi"},
            {"host_name": "North Beach", "host_url": "afp://sfo/nobe"},
        ]
        with mock.patch.object(Collector, "_read_plist", autospec=True, return_value=plist) as mock_read_plist:
            self.collector._collect_accounts_recent_items()

            mock_read_plist.assert_called_with(self.collector, plist_path)
            for recent in recents:
                self.mock_log_dict.assert_any_call(recent)