Skip to content

Commit

Permalink
NAS-128911 / 24.10 / Add basic tests for statx wrapper (#13702)
Browse files Browse the repository at this point in the history
* Add basic tests for statx wrapper

* Validate that output matches what we see from os.stat

* Validate that opening with dirfd works as expected

* Validate that opening with AT_EMPTY_PATH works as expected
  • Loading branch information
anodos325 authored May 10, 2024
1 parent ca74bbb commit c1ef176
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/middlewared/middlewared/plugins/filesystem_/stat_x.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# This utility provides a basic wrapper for statx(2).
#
# We need statx(2) for gathering birth time, mount id, and
# file attributes for the middleware filesystem plugin
#
# NOTE: tests for these utils are in src/middlewared/middlewared/pytest/unit/utils/test_statx.py

import os
import ctypes
from enum import IntFlag
Expand Down
105 changes: 105 additions & 0 deletions src/middlewared/middlewared/pytest/unit/utils/test_statx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import os
import pytest
import stat

from middlewared.plugins.filesystem_ import stat_x as sx


BASIC_STAT_ATTRS = [
'MODE',
'UID',
'GID',
'ATIME',
'MTIME',
'CTIME',
'DEV',
'INO',
'SIZE',
'BLOCKS',
'BLKSIZE',
'NLINK',
]

def timespec_convert(timespec):
return timespec.tv_sec + timespec.tv_nsec / 1000000000


def do_stat(filename, isdir):
if not isdir:
with open(filename, "w"):
pass

return (os.stat(filename), sx.statx(filename))


def validate_stat(stat_prop, st1, st2):
match stat_prop:
case 'MODE':
assert st1.st_mode == st2.stx_mode
case 'UID':
assert st1.st_uid == st2.stx_uid
case 'GID':
assert st1.st_gid == st2.stx_gid
case 'ATIME':
assert st1.st_atime == timespec_convert(st2.stx_atime)
case 'MTIME':
assert st1.st_mtime == timespec_convert(st2.stx_mtime)
case 'CTIME':
assert st1.st_ctime == timespec_convert(st2.stx_ctime)
case 'INO':
assert st1.st_ino == st2.stx_ino
case 'DEV':
assert st1.st_dev == os.makedev(st2.stx_dev_major, st2.stx_dev_minor)
case 'BLOCKS':
assert st1.st_blocks == st2.stx_blocks
case 'BLKSIZE':
assert st1.st_blksize == st2.stx_blksize
case 'NLINK':
assert st1.st_nlink == st2.stx_nlink
case 'SIZE':
assert st1.st_size == st2.stx_size
case _:
raise ValueError(f'{stat_prop}: unknown stat property')


@pytest.mark.parametrize('stat_prop', BASIC_STAT_ATTRS)
def test__check_statx_vs_stat_file(tmpdir, stat_prop):
st1, st2 = do_stat(os.path.join(tmpdir, 'testfile'), False)
validate_stat(stat_prop, st1, st2)


@pytest.mark.parametrize('stat_prop', BASIC_STAT_ATTRS)
def test__check_statx_vs_stat_dir(tmpdir, stat_prop):
st1, st2 = do_stat(str(tmpdir), True)
validate_stat(stat_prop, st1, st2)


def test__check_dirfd(tmpdir):
testfile = os.path.join(tmpdir, 'testfile')
with open(testfile, 'w'):
pass

stx1 = sx.statx(testfile)
try:
dirfd = os.open(tmpdir, os.O_PATH)
stx2 = sx.statx('testfile', {'dir_fd': dirfd})
finally:
os.close(dirfd)

assert stx1.stx_ino == stx2.stx_ino


def test__check_statx_empty_path(tmpdir):
# test fstat equivalent via statx interface
testfile = os.path.join(tmpdir, 'testfile')
with open(testfile, 'w'):
pass

stx1 = sx.statx(testfile)
try:
fd = os.open(testfile, os.O_PATH)
stx2 = sx.statx('', {'dir_fd': fd, 'flags': sx.ATFlags.EMPTY_PATH})
finally:
os.close(fd)

assert stx1.stx_ino == stx2.stx_ino

0 comments on commit c1ef176

Please sign in to comment.