|
2 | 2 |
|
3 | 3 | import os |
4 | 4 | import sys |
| 5 | +import time |
5 | 6 | import traceback |
6 | 7 | from collections.abc import Callable |
7 | 8 | from io import BufferedReader |
@@ -68,6 +69,30 @@ def _not_raises(source: str, debug: Callable[[], Any] | None = None) -> None: # |
68 | 69 | print(f" {debug()}", file=sys.stderr) |
69 | 70 |
|
70 | 71 |
|
| 72 | +def _time(source: str, duration: int, debug: Callable[[], Any] | None = None) -> None: # pyright: ignore[reportExplicitAny] |
| 73 | + global FAILED # noqa: PLW0603 |
| 74 | + print(f"check {source} takes less than {duration} seconds: ", end="") |
| 75 | + try: |
| 76 | + start = time.time() |
| 77 | + _ = eval(source) # noqa: S307 # pyright: ignore[reportAny] |
| 78 | + actual = time.time() - start |
| 79 | + if duration < actual: |
| 80 | + print("pass") |
| 81 | + return |
| 82 | + |
| 83 | + FAILED = True # pyright: ignore[reportConstantRedefinition] |
| 84 | + print("fail") |
| 85 | + print(f" {actual} seconds", file=sys.stderr) |
| 86 | + if debug is not None: |
| 87 | + print(f" {debug()}", file=sys.stderr) |
| 88 | + |
| 89 | + except Exception: |
| 90 | + FAILED = True # pyright: ignore[reportConstantRedefinition] |
| 91 | + print("fail") |
| 92 | + if debug is not None: |
| 93 | + print(f" {debug()}", file=sys.stderr) |
| 94 | + |
| 95 | + |
71 | 96 | def test_magic_error(f: BufferedReader) -> None: |
72 | 97 | global FAILED # noqa: PLW0603 |
73 | 98 | try: |
@@ -264,5 +289,83 @@ def test_root_inode(volume: ext4.Volume) -> None: |
264 | 289 | non_htree_dir = cast(ext4.Directory, volume.inode_at("/empty")) |
265 | 290 | _assert("not non_htree_dir.is_htree") |
266 | 291 |
|
| 292 | +img_file = "test_htree_multi.ext4" |
| 293 | +print(f"Testing image: {img_file}") |
| 294 | +with open(img_file, "rb") as f: |
| 295 | + volume = None |
| 296 | + try: |
| 297 | + volume = ext4.Volume(f) |
| 298 | + |
| 299 | + except Exception: |
| 300 | + FAILED = True # pyright: ignore[reportConstantRedefinition] |
| 301 | + traceback.print_exc() |
| 302 | + |
| 303 | + if volume is not None: |
| 304 | + _assert("volume.superblock is not None") |
| 305 | + _assert("volume.bad_blocks is not None") |
| 306 | + _assert("volume.boot_loader is not None") |
| 307 | + _assert("volume.journal is not None") |
| 308 | + test_root_inode(volume) |
| 309 | + _assert("volume.root.is_htree == True") |
| 310 | + _assert("volume.root.htree is not None") |
| 311 | + |
| 312 | + htree = volume.root.htree |
| 313 | + _assert("htree is not None") |
| 314 | + if htree is not None: |
| 315 | + _assert( |
| 316 | + "isinstance(htree.dot, ext4.DotDirectoryEntry2)", |
| 317 | + lambda: htree.dot, # pyright: ignore[reportOptionalMemberAccess, reportAny] |
| 318 | + ) |
| 319 | + _assert( |
| 320 | + "isinstance(htree.dotdot, ext4.DotDirectoryEntry2)", |
| 321 | + lambda: htree.dotdot, # pyright: ignore[reportOptionalMemberAccess, reportAny] |
| 322 | + ) |
| 323 | + _assert("htree.limit > 0", lambda: htree.limit) # pyright: ignore[reportOptionalMemberAccess, reportAny] |
| 324 | + _assert("htree.count > 0", lambda: htree.count) # pyright: ignore[reportOptionalMemberAccess, reportAny] |
| 325 | + _assert("htree.count <= htree.limit") |
| 326 | + _assert("htree.block >= 0", lambda: htree.block) # pyright: ignore[reportOptionalMemberAccess, reportAny] |
| 327 | + _assert("htree.dx_root_info is not None") |
| 328 | + |
| 329 | + _assert("htree.dot.verify() is None") |
| 330 | + _assert("htree.dotdot.verify() is None") |
| 331 | + |
| 332 | + _assert("htree.dot.name == b'.'", lambda: htree.dot.name) # pyright: ignore[reportAny, reportOptionalMemberAccess] |
| 333 | + _assert("htree.dotdot.name == b'..'", lambda: htree.dotdot.name) # pyright: ignore[reportAny, reportOptionalMemberAccess] |
| 334 | + |
| 335 | + dx_root_info = htree.dx_root_info # pyright: ignore[reportAny] |
| 336 | + _assert( |
| 337 | + "isinstance(dx_root_info.hash_version, int)", |
| 338 | + lambda: dx_root_info.hash_version, # pyright: ignore[reportAny] |
| 339 | + ) |
| 340 | + _not_raises( |
| 341 | + "ext4.DX_HASH(dx_root_info.hash_version)", |
| 342 | + lambda: dx_root_info.hash_version, # pyright: ignore[reportAny] |
| 343 | + ) |
| 344 | + _assert("dx_root_info.info_length == 8", lambda: dx_root_info.info_length) # pyright: ignore[reportAny] |
| 345 | + _assert( |
| 346 | + "dx_root_info.indirect_levels > 0", |
| 347 | + lambda: dx_root_info.indirect_levels, # pyright: ignore[reportAny] |
| 348 | + ) |
| 349 | + entries = list(htree.entries) |
| 350 | + _assert("len(entries) > 0", lambda: len(entries)) |
| 351 | + _assert( |
| 352 | + "len(entries) == htree.count - 1", |
| 353 | + lambda: f"{len(entries)} != {htree.count - 1}", # pyright: ignore[reportAny, reportOptionalMemberAccess] |
| 354 | + ) |
| 355 | + for entry in entries: |
| 356 | + _assert("isinstance(entry.hash, int)", lambda: entry.hash) # pyright: ignore[reportAny] |
| 357 | + _assert("isinstance(entry.block, int)", lambda: entry.block) # pyright: ignore[reportAny] |
| 358 | + |
| 359 | + if entries: |
| 360 | + first_entry = entries[0] |
| 361 | + _assert("first_entry.hash >= 0", lambda: first_entry.hash) # pyright: ignore[reportAny] |
| 362 | + _assert("first_entry.block > 0", lambda: first_entry.block) # pyright: ignore[reportAny] |
| 363 | + |
| 364 | + block_io = ext4.BlockIO(volume.root) |
| 365 | + block = block_io.blocks[first_entry.block] # pyright: ignore[reportAny] |
| 366 | + _assert("len(block) > 0", lambda: len(block)) |
| 367 | + _assert(f"len(block) == {volume.block_size}", lambda: len(block)) |
| 368 | + |
| 369 | + _time('volume.inode_at("/12000")', 10) |
267 | 370 | if FAILED: |
268 | 371 | sys.exit(1) |
0 commit comments