# Test VfsFat class and its finaliser

try:
    import uerrno, uos

    uos.VfsFat
except (ImportError, AttributeError):
    print("SKIP")
    raise SystemExit


class RAMBlockDevice:
    def __init__(self, blocks, sec_size=512):
        self.sec_size = sec_size
        self.data = bytearray(blocks * self.sec_size)

    def readblocks(self, n, buf):
        for i in range(len(buf)):
            buf[i] = self.data[n * self.sec_size + i]

    def writeblocks(self, n, buf):
        for i in range(len(buf)):
            self.data[n * self.sec_size + i] = buf[i]

    def ioctl(self, op, arg):
        if op == 4:  # MP_BLOCKDEV_IOCTL_BLOCK_COUNT
            return len(self.data) // self.sec_size
        if op == 5:  # MP_BLOCKDEV_IOCTL_BLOCK_SIZE
            return self.sec_size


# Create block device, and skip test if not enough RAM
try:
    bdev = RAMBlockDevice(50)
except MemoryError:
    print("SKIP")
    raise SystemExit

# Format block device and create VFS object
uos.VfsFat.mkfs(bdev)
vfs = uos.VfsFat(bdev)

# Here we test that opening a file with the heap locked fails correctly.  This
# is a special case because file objects use a finaliser and allocating with a
# finaliser is a different path to normal allocation.  It would be better to
# test this in the core tests but there are no core objects that use finaliser.
import micropython

micropython.heap_lock()
try:
    vfs.open("x", "r")
except MemoryError:
    print("MemoryError")
micropython.heap_unlock()

# Here we test that the finaliser is actually called during a garbage collection.
import gc

N = 4
for i in range(N):
    n = "x%d" % i
    f = vfs.open(n, "w")
    f.write(n)
    f = None  # release f without closing
    [0, 1, 2, 3, 4, 5]  # use up Python stack so f is really gone
gc.collect()  # should finalise all N files by closing them
for i in range(N):
    with vfs.open("x%d" % i, "r") as f:
        print(f.read())
