/* SPDX-License-Identifier: GPL-2.0-only */
struct test_case {
    const char *id;
    unsigned int ref_check, blocks;
    uint64_t seed;
    uint16_t app[2], guard[2];
    uint32_t ref[2];
    int expected_status;
    unsigned int expected_checks, pi_offset;
};
struct prefixed_pi {
    unsigned char prefix[8];
    struct t10_pi_tuple pi;
};
int main(void)
{
    const struct test_case cases[] = {
        {"ordinary-valid", 1, 1, 7, {0}, {0x1234}, {7}, 0, 1, 0},
        {"ordinary-bad-guard", 1, 1, 7, {0}, {0}, {7}, 1, 1, 0},
        {"ordinary-bad-reference", 1, 1, 7, {0}, {0x1234}, {8}, 1, 0, 0},
        {"escaped-bad-guard-and-reference", 1, 1, 7, {0xffff}, {0}, {8}, 0, 0, 0},
        {"near-escape-bad-guard", 1, 1, 7, {0xfffe}, {0}, {7}, 1, 1, 0},
        {"no-reference-check", 0, 1, 7, {0}, {0x1234}, {8}, 0, 1, 0},
        {"no-ref-app-escape-only", 0, 1, 7, {0xffff}, {0}, {0}, 1, 1, 0},
        {"no-ref-both-escapes", 0, 1, 7, {0xffff}, {0}, {0xffffffff}, 0, 0, 0},
        {"reference-wrap-alias", 1, 1, 0x100000007ULL, {0}, {0x1234}, {7}, 0, 1, 0},
        {"escaped-then-bad-guard", 1, 2, 7, {0xffff, 0}, {0, 0}, {99, 8}, 1, 1, 0},
        {"escaped-then-valid", 1, 2, 7, {0xffff, 0}, {0, 0x1234}, {99, 8}, 0, 1, 0},
        {"bad-guard-then-escaped", 1, 2, 7, {0, 0xffff}, {0, 0}, {7, 99}, 1, 1, 0},
        {"metadata-prefix-valid", 1, 1, 7, {0}, {0x1234}, {7}, 0, 2, 8},
        {"metadata-prefix-bad-guard", 1, 1, 7, {0}, {0}, {7}, 1, 2, 8},
        {"metadata-prefix-escaped", 1, 1, 7, {0xffff}, {0}, {99}, 0, 0, 8},
        {"metadata-prefix-two-blocks", 1, 2, 7, {0, 0}, {0x1234, 0x1234}, {7, 8}, 0, 4, 8}
    };
    _Static_assert(sizeof(struct t10_pi_tuple) == 8, "Unexpected tuple layout");
    _Static_assert(sizeof(struct prefixed_pi) == 16, "Unexpected metadata stride");
    unsigned char data[1024] = {0};
    for (unsigned int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
        const struct test_case *c = &cases[i];
        struct t10_pi_tuple tuples[2] = {0};
        struct prefixed_pi prefixed[2] = {0};
        void *metadata = c->pi_offset ? (void *)prefixed : (void *)tuples;
        unsigned int stride = 8 + c->pi_offset;
        for (unsigned int j = 0; j < c->blocks; j++) {
            struct t10_pi_tuple *pi = c->pi_offset ? &prefixed[j].pi : &tuples[j];
            pi->guard_tag = cpu_to_be16(c->guard[j]);
            pi->app_tag = cpu_to_be16(c->app[j]);
            pi->ref_tag = cpu_to_be32(c->ref[j]);
        }
        struct blk_integrity_iter iter = {
            .data_buf = data, .prot_buf = metadata, .seed = c->seed,
            .interval = 512, .data_size = c->blocks * 512, .disk_name = "fixture"
        };
        struct blk_integrity bi = {
            .flags = c->ref_check ? BLK_INTEGRITY_REF_TAG : 0,
            .tuple_size = stride, .pi_offset = c->pi_offset, .csum_type = 0
        };
        checksum_calls = 0;
        int result = t10_pi_verify(&iter, &bi);
        int matched = result == c->expected_status && checksum_calls == c->expected_checks;
        printf("{\"id\":\"%s\",\"referenceCheck\":%s,\"blocks\":%u,"
               "\"piOffset\":%u,\"metadataStride\":%u,\"seed\":%llu,\"firstApplicationTag\":%u,\"firstReferenceTag\":%u,"
               "\"status\":\"%s\",\"checksumCalls\":%u,\"matched_expected\":%s}\n",
               c->id, c->ref_check ? "true" : "false", c->blocks,
               c->pi_offset, stride, (unsigned long long)c->seed, c->app[0], c->ref[0],
               result == BLK_STS_OK ? "accepted" : "protection-error",
               checksum_calls, matched ? "true" : "false");
        if (!matched) return 1;
    }
    return 0;
}
