# There are already a number of tests that test general operations under
# power-loss (see the reentrant attribute). These tests are for explicitly
# testing specific corner cases.

# only a revision count
[cases.test_powerloss_only_rev]
code = '''
    lfs_t lfs;
    lfs_format(&lfs, cfg) => 0;

    lfs_mount(&lfs, cfg) => 0;
    lfs_mkdir(&lfs, "notebook") => 0;
    lfs_file_t file;
    lfs_file_open(&lfs, &file, "notebook/paper",
            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
    char buffer[256];
    strcpy(buffer, "hello");
    lfs_size_t size = strlen("hello");
    for (int i = 0; i < 5; i++) {
        lfs_file_write(&lfs, &file, buffer, size) => size;
        lfs_file_sync(&lfs, &file) => 0;
    }
    lfs_file_close(&lfs, &file) => 0;

    char rbuffer[256];
    lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
    for (int i = 0; i < 5; i++) {
        lfs_file_read(&lfs, &file, rbuffer, size) => size;
        assert(memcmp(rbuffer, buffer, size) == 0);
    }
    lfs_file_close(&lfs, &file) => 0;
    lfs_unmount(&lfs) => 0;

    // get pair/rev count
    lfs_mount(&lfs, cfg) => 0;
    lfs_dir_t dir;
    lfs_dir_open(&lfs, &dir, "notebook") => 0;
    lfs_block_t pair[2] = {dir.m.pair[0], dir.m.pair[1]};
    uint32_t rev = dir.m.rev;
    lfs_dir_close(&lfs, &dir) => 0;
    lfs_unmount(&lfs) => 0;

    // write just the revision count
    uint8_t bbuffer[BLOCK_SIZE];
    cfg->read(cfg, pair[1], 0, bbuffer, BLOCK_SIZE) => 0;

    memcpy(bbuffer, &(uint32_t){lfs_tole32(rev+1)}, sizeof(uint32_t));

    cfg->erase(cfg, pair[1]) => 0;
    cfg->prog(cfg, pair[1], 0, bbuffer, BLOCK_SIZE) => 0;

    lfs_mount(&lfs, cfg) => 0;

    // can read?
    lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
    for (int i = 0; i < 5; i++) {
        lfs_file_read(&lfs, &file, rbuffer, size) => size;
        assert(memcmp(rbuffer, buffer, size) == 0);
    }
    lfs_file_close(&lfs, &file) => 0;

    // can write?
    lfs_file_open(&lfs, &file, "notebook/paper",
            LFS_O_WRONLY | LFS_O_APPEND) => 0;
    strcpy(buffer, "goodbye");
    size = strlen("goodbye");
    for (int i = 0; i < 5; i++) {
        lfs_file_write(&lfs, &file, buffer, size) => size;
        lfs_file_sync(&lfs, &file) => 0;
    }
    lfs_file_close(&lfs, &file) => 0;

    lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
    strcpy(buffer, "hello");
    size = strlen("hello");
    for (int i = 0; i < 5; i++) {
        lfs_file_read(&lfs, &file, rbuffer, size) => size;
        assert(memcmp(rbuffer, buffer, size) == 0);
    }
    strcpy(buffer, "goodbye");
    size = strlen("goodbye");
    for (int i = 0; i < 5; i++) {
        lfs_file_read(&lfs, &file, rbuffer, size) => size;
        assert(memcmp(rbuffer, buffer, size) == 0);
    }
    lfs_file_close(&lfs, &file) => 0;

    lfs_unmount(&lfs) => 0;
'''

# partial prog, may not be byte in order!
[cases.test_powerloss_partial_prog]
if = '''
    PROG_SIZE < BLOCK_SIZE
        && (DISK_VERSION == 0 || DISK_VERSION >= 0x00020001)
'''
defines.BYTE_OFF = ["0", "PROG_SIZE-1", "PROG_SIZE/2"]
defines.BYTE_VALUE = [0x33, 0xcc]
in = "lfs.c"
code = '''
    lfs_t lfs;
    lfs_format(&lfs, cfg) => 0;

    lfs_mount(&lfs, cfg) => 0;
    lfs_mkdir(&lfs, "notebook") => 0;
    lfs_file_t file;
    lfs_file_open(&lfs, &file, "notebook/paper",
            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
    char buffer[256];
    strcpy(buffer, "hello");
    lfs_size_t size = strlen("hello");
    for (int i = 0; i < 5; i++) {
        lfs_file_write(&lfs, &file, buffer, size) => size;
        lfs_file_sync(&lfs, &file) => 0;
    }
    lfs_file_close(&lfs, &file) => 0;

    char rbuffer[256];
    lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
    for (int i = 0; i < 5; i++) {
        lfs_file_read(&lfs, &file, rbuffer, size) => size;
        assert(memcmp(rbuffer, buffer, size) == 0);
    }
    lfs_file_close(&lfs, &file) => 0;
    lfs_unmount(&lfs) => 0;

    // imitate a partial prog, value should not matter, if littlefs
    // doesn't notice the partial prog testbd will assert

    // get offset to next prog
    lfs_mount(&lfs, cfg) => 0;
    lfs_dir_t dir;
    lfs_dir_open(&lfs, &dir, "notebook") => 0;
    lfs_block_t block = dir.m.pair[0];
    lfs_off_t off = dir.m.off;
    lfs_dir_close(&lfs, &dir) => 0;
    lfs_unmount(&lfs) => 0;

    // tweak byte
    uint8_t bbuffer[BLOCK_SIZE];
    cfg->read(cfg, block, 0, bbuffer, BLOCK_SIZE) => 0;

    bbuffer[off + BYTE_OFF] = BYTE_VALUE;

    cfg->erase(cfg, block) => 0;
    cfg->prog(cfg, block, 0, bbuffer, BLOCK_SIZE) => 0;

    lfs_mount(&lfs, cfg) => 0;

    // can read?
    lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
    for (int i = 0; i < 5; i++) {
        lfs_file_read(&lfs, &file, rbuffer, size) => size;
        assert(memcmp(rbuffer, buffer, size) == 0);
    }
    lfs_file_close(&lfs, &file) => 0;

    // can write?
    lfs_file_open(&lfs, &file, "notebook/paper",
            LFS_O_WRONLY | LFS_O_APPEND) => 0;
    strcpy(buffer, "goodbye");
    size = strlen("goodbye");
    for (int i = 0; i < 5; i++) {
        lfs_file_write(&lfs, &file, buffer, size) => size;
        lfs_file_sync(&lfs, &file) => 0;
    }
    lfs_file_close(&lfs, &file) => 0;

    lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0;
    strcpy(buffer, "hello");
    size = strlen("hello");
    for (int i = 0; i < 5; i++) {
        lfs_file_read(&lfs, &file, rbuffer, size) => size;
        assert(memcmp(rbuffer, buffer, size) == 0);
    }
    strcpy(buffer, "goodbye");
    size = strlen("goodbye");
    for (int i = 0; i < 5; i++) {
        lfs_file_read(&lfs, &file, rbuffer, size) => size;
        assert(memcmp(rbuffer, buffer, size) == 0);
    }
    lfs_file_close(&lfs, &file) => 0;

    lfs_unmount(&lfs) => 0;
'''
