1# -*- coding: utf-8 -*- 2# This file is part of beets. 3# Copyright 2016, Stig Inge Lea Bjornsen. 4# 5# Permission is hereby granted, free of charge, to any person obtaining 6# a copy of this software and associated documentation files (the 7# "Software"), to deal in the Software without restriction, including 8# without limitation the rights to use, copy, modify, merge, publish, 9# distribute, sublicense, and/or sell copies of the Software, and to 10# permit persons to whom the Software is furnished to do so, subject to 11# the following conditions: 12# 13# The above copyright notice and this permission notice shall be 14# included in all copies or substantial portions of the Software. 15 16from __future__ import division, absolute_import, print_function 17 18"""Tests for the `importadded` plugin.""" 19 20import os 21import unittest 22 23from test.test_importer import ImportHelper, AutotagStub 24from beets import importer 25from beets import util 26from beetsplug.importadded import ImportAddedPlugin 27 28_listeners = ImportAddedPlugin.listeners 29 30 31def preserve_plugin_listeners(): 32 """Preserve the initial plugin listeners as they would otherwise be 33 deleted after the first setup / tear down cycle. 34 """ 35 if not ImportAddedPlugin.listeners: 36 ImportAddedPlugin.listeners = _listeners 37 38 39def modify_mtimes(paths, offset=-60000): 40 for i, path in enumerate(paths, start=1): 41 mstat = os.stat(path) 42 os.utime(path, (mstat.st_atime, mstat.st_mtime + offset * i)) 43 44 45class ImportAddedTest(unittest.TestCase, ImportHelper): 46 47 # The minimum mtime of the files to be imported 48 min_mtime = None 49 50 def setUp(self): 51 preserve_plugin_listeners() 52 self.setup_beets() 53 self.load_plugins('importadded') 54 self._create_import_dir(2) 55 # Different mtimes on the files to be imported in order to test the 56 # plugin 57 modify_mtimes((mfile.path for mfile in self.media_files)) 58 self.min_mtime = min(os.path.getmtime(mfile.path) 59 for mfile in self.media_files) 60 self.matcher = AutotagStub().install() 61 self.matcher.macthin = AutotagStub.GOOD 62 self._setup_import_session() 63 self.importer.add_choice(importer.action.APPLY) 64 65 def tearDown(self): 66 self.unload_plugins() 67 self.teardown_beets() 68 self.matcher.restore() 69 70 def find_media_file(self, item): 71 """Find the pre-import MediaFile for an Item""" 72 for m in self.media_files: 73 if m.title.replace('Tag', 'Applied') == item.title: 74 return m 75 raise AssertionError(u"No MediaFile found for Item " + 76 util.displayable_path(item.path)) 77 78 def assertEqualTimes(self, first, second, msg=None): # noqa 79 """For comparing file modification times at a sufficient precision""" 80 self.assertAlmostEqual(first, second, places=4, msg=msg) 81 82 def assertAlbumImport(self): # noqa 83 self.importer.run() 84 album = self.lib.albums().get() 85 self.assertEqual(album.added, self.min_mtime) 86 for item in album.items(): 87 self.assertEqual(item.added, self.min_mtime) 88 89 def test_import_album_with_added_dates(self): 90 self.assertAlbumImport() 91 92 def test_import_album_inplace_with_added_dates(self): 93 self.config['import']['copy'] = False 94 self.config['import']['move'] = False 95 self.config['import']['link'] = False 96 self.config['import']['hardlink'] = False 97 self.assertAlbumImport() 98 99 def test_import_album_with_preserved_mtimes(self): 100 self.config['importadded']['preserve_mtimes'] = True 101 self.importer.run() 102 album = self.lib.albums().get() 103 self.assertEqual(album.added, self.min_mtime) 104 for item in album.items(): 105 self.assertEqualTimes(item.added, self.min_mtime) 106 mediafile_mtime = os.path.getmtime(self.find_media_file(item).path) 107 self.assertEqualTimes(item.mtime, mediafile_mtime) 108 self.assertEqualTimes(os.path.getmtime(item.path), 109 mediafile_mtime) 110 111 def test_reimported_album_skipped(self): 112 # Import and record the original added dates 113 self.importer.run() 114 album = self.lib.albums().get() 115 album_added_before = album.added 116 items_added_before = dict((item.path, item.added) 117 for item in album.items()) 118 # Newer Item path mtimes as if Beets had modified them 119 modify_mtimes(items_added_before.keys(), offset=10000) 120 # Reimport 121 self._setup_import_session(import_dir=album.path) 122 self.importer.run() 123 # Verify the reimported items 124 album = self.lib.albums().get() 125 self.assertEqualTimes(album.added, album_added_before) 126 items_added_after = dict((item.path, item.added) 127 for item in album.items()) 128 for item_path, added_after in items_added_after.items(): 129 self.assertEqualTimes(items_added_before[item_path], added_after, 130 u"reimport modified Item.added for " + 131 util.displayable_path(item_path)) 132 133 def test_import_singletons_with_added_dates(self): 134 self.config['import']['singletons'] = True 135 self.importer.run() 136 for item in self.lib.items(): 137 mfile = self.find_media_file(item) 138 self.assertEqualTimes(item.added, os.path.getmtime(mfile.path)) 139 140 def test_import_singletons_with_preserved_mtimes(self): 141 self.config['import']['singletons'] = True 142 self.config['importadded']['preserve_mtimes'] = True 143 self.importer.run() 144 for item in self.lib.items(): 145 mediafile_mtime = os.path.getmtime(self.find_media_file(item).path) 146 self.assertEqualTimes(item.added, mediafile_mtime) 147 self.assertEqualTimes(item.mtime, mediafile_mtime) 148 self.assertEqualTimes(os.path.getmtime(item.path), 149 mediafile_mtime) 150 151 def test_reimported_singletons_skipped(self): 152 self.config['import']['singletons'] = True 153 # Import and record the original added dates 154 self.importer.run() 155 items_added_before = dict((item.path, item.added) 156 for item in self.lib.items()) 157 # Newer Item path mtimes as if Beets had modified them 158 modify_mtimes(items_added_before.keys(), offset=10000) 159 # Reimport 160 import_dir = os.path.dirname(list(items_added_before.keys())[0]) 161 self._setup_import_session(import_dir=import_dir, singletons=True) 162 self.importer.run() 163 # Verify the reimported items 164 items_added_after = dict((item.path, item.added) 165 for item in self.lib.items()) 166 for item_path, added_after in items_added_after.items(): 167 self.assertEqualTimes(items_added_before[item_path], added_after, 168 u"reimport modified Item.added for " + 169 util.displayable_path(item_path)) 170 171 172def suite(): 173 return unittest.TestLoader().loadTestsFromName(__name__) 174 175if __name__ == '__main__': 176 unittest.main(defaultTest='suite') 177