def test_total_size_calculated_correctly(self, tmp_folder): """Check if directory scanning calculates the item sizes correctly. Testing for: @property def total_size(self). :param tmp_folder: Test params and dummy test folder factory fixture pair. :type tmp_folder: (dict, dirtools.tests.factory.DummyFolderFactory) """ params, factory = tmp_folder scan = Folder(factory.path, params['sort_by'], params['level']) items_len = len(list(scan.items())) # For an empty folder if factory.total_items == 0: assert factory._level == 0 assert factory.total_size == 0 assert scan.total_size == 0 assert items_len == 0 # Compare with the testing factory testing desired_size param assert scan.total_size == sum( [i['size'] for i in scan.items(humanise=False)]) assert scan.total_size == factory.total_size assert items_len == factory.total_items # Because the factory class does not create at exact size assert scan.total_size == factory.total_size assert scan.total_size >= 0.9 * human2bytes(params['total_size']) assert scan.total_size <= 1.1 * human2bytes(params['total_size'])
def test_human2bytes_raises_expected_errors(): # Check TypeError(s) for invalid in (None, 123, 123.0, [1, 2, 3]): with pytest.raises(TypeError): utils.human2bytes(invalid) # Check ValueErrors(s) for invalid in ('123 bytes', '123 foo', '123 m'): with pytest.raises(ValueError): utils.human2bytes(invalid)
def cleanup_items(self, max_total_size: str, humanise: bool = True, precision: int = 2) -> Iterator[dict]: """Completely remove every item starting from the first in given sorting order until it reaches to ``max_total_size`` parameter. Returns empty generator if the given ``max_total_size`` parameter is equal or greater than entire total size. Otherwise removes and yields every deleted item. Blocks until the scanning operation has been completed on first access. .. warning:: Result of this method is to DELETE the physical files / folders on your disk until the given size matches the actual size and there is NO UNDO for this operation. :param max_total_size: Human representation of total desired size. See: :func:``dirtools.utils.human2bytes``. :type max_total_size: str :param humanise: Humanise flag (required, no default value). :type humanise: bool :param precision: The floating precision of the human-readable size format (defaults to 2). :type precision: int :return: iterator """ self._await() # Start deleting in the sorted order old_len = self._items_len old_size = self._total_size max_total_size = utils.human2bytes(max_total_size) while self._total_size > max_total_size: item = self._items.popleft() self._total_size -= item["size"] self._items_len -= 1 item_path = os.path.abspath(os.path.join(self._root, item["name"])) # REMOVE THE ITEM PERMANENTLY try: shutil.rmtree(item_path) except NotADirectoryError: os.remove(item_path) # yield removed item yield self._humanise_item(item, precision) if humanise else item # Reduced to desired size logger.debug( "{del_len:d} items with total of {del_size} data has been deleted." .format( del_len=old_len - self._items_len, del_size=utils.bytes2human(old_size - self._total_size), ))
def test_invoke_trimming_down_instead_of_listing(self, monkeypatch, tmp_folder, clone_factory): """ :param monkeypatch: pytest monkey patch fixture :type monkeypatch: _pytest.monkeypatch.MonkeyPatch :param tmp_folder: Test params and dummy test folder factory fixture pair. :type tmp_folder: (dict, dirtools.tests.factory.DummyFolderFactory) :param clone_factory: Factory to create `Folder` clone instance. :type clone_factory: dirtools.tests.conftest.clone_factory._factory :return: """ params, _factory = tmp_folder if params['total_items'] == 0: return trim_down = int(human2bytes(params['total_size']) / 2) trim_down_human = bytes2human(trim_down) # Use another factory folder that is not shared with other tests with DummyFolderFactory(params['total_items'], params['total_size'], level=params['level']) as factory: # Create a scanner mock from the factory scan = clone_factory(factory.path, params['sort_by'], level=params['level']) FolderScanMock = Mock(return_value=scan) monkeypatch.setattr(self.dirt, 'Folder', FolderScanMock) # Give only numeric trim-down value that shouldn't be accepted result = self.runner.invoke(self.dirt.invoke_dirtools3, [ factory.path, '-s', str(params['sort_by']).lower(), '--trim-down', str(trim_down) ]) assert result.exception is None assert '--trim-down value cannot be only numeric' in result.output result = self.runner.invoke(self.dirt.invoke_dirtools3, [ factory.path, '-s', str(params['sort_by']).lower(), '--trim-down', trim_down_human ]) assert result.exception is None scan.items.assert_not_called() scan.cleanup_items.assert_called_once_with( trim_down_human, humanise=not self._DEFAULTS['nohuman'], precision=self._DEFAULTS['precision'])
def __init__(self, total_items, total_size, **kwargs): # Pick a random folder name # under OS dependent temporary space if no "_root" keyword is given self._root = '{root}/dirtools3-{rand}'.format( root=kwargs.get('_root', tempfile.gettempdir()), rand=self._gen_rand_str(3)) # Save configs self.total_items = int(total_items) self._desired_size = utils.human2bytes(total_size) self._level = int(kwargs.get('level', self._level)) assert self._level <= (self.total_items // 4), \ 'Level cannot be more than a 4rd of total_items: {0:d}'.format(self.total_items) self._max_depth = int(kwargs.get('max_depth', self._max_depth)) assert self._max_depth >= 0, 'Max depth cannot be negative.' self.cleanup = bool(kwargs.get('cleanup', self.cleanup))
def test_bytes2human_calculates_correct_bytes_to_human(): for byte_val, human in VALID_BYTES2HUMAN: # Also assert well-formatted human value to bytes assert byte_val == utils.human2bytes(human) assert utils.bytes2human(byte_val, precision=11) == human
def test_human2bytes_calculates_correct_values_to_bytes(): for human, byte_val in VALID_HUMAN2BYTES: assert utils.human2bytes(human) == byte_val