#include "test.h" #include "store.h" #include "assets/triples.h" #define STORE_ID_MDB "file:///tmp/testdb"; #define STORE_ID_HTABLE LSUP_NS "dummy"; static LSUP_Store store_s; static LSUP_Store *store = &store_s; /** @brief Test triple store. * * This runs context-aware stores in triple mode. They should behave exactly * in the same way as non-context stores. */ static int test_triple_store() { if (store->sif->setup_fn) EXPECT_PASS (store->sif->setup_fn (store->id, true)); log_info ("Testing triple store for %s", store->id); store->data = store->sif->new_fn (store->id, 0); ASSERT (store->data != NULL, "Error creating store data back end!"); LSUP_Triple **trp = create_triples(); LSUP_BufferTriple ser_trp[NUM_TRP]; for (int i = 0; i < NUM_TRP; i++) { LSUP_BufferTriple *tmp = LSUP_triple_serialize (trp[i]); ser_trp[i] = *tmp; free (tmp); } // Test adding. void *it = store->sif->add_init_fn (store->data, NULL, NULL); size_t ins = 0; for (size_t i = 0; i < NUM_TRP; i++) { LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i); ASSERT (rc >= 0, "Error inserting triples!"); if (rc == LSUP_OK) ins++; } store->sif->add_done_fn (it); EXPECT_INT_EQ (ins, 8); EXPECT_INT_EQ (store->sif->size_fn (store->data), ins); // Test lookups. LSUP_Buffer *lut[12][3] = { {NULL, NULL, NULL}, {ser_trp[0].s, NULL, NULL}, {ser_trp[2].s, NULL, NULL}, {NULL, ser_trp[0].p, NULL}, {NULL, ser_trp[0].s, NULL}, {NULL, NULL, ser_trp[6].o}, {ser_trp[4].s, ser_trp[4].p, NULL}, {NULL, ser_trp[7].p, ser_trp[7].o}, {ser_trp[5].s, NULL, ser_trp[5].o}, {ser_trp[5].s, NULL, ser_trp[5].o}, {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o}, {ser_trp[4].s, ser_trp[4].p, ser_trp[6].o}, }; LSUP_Buffer *luc[12] = { NULL, NULL, ser_trp[2].s, NULL, NULL, NULL, NULL, NULL, NULL, ser_trp[2].s, NULL, NULL, }; size_t results[12] = { 8, 5, // Lookup on nonexisting context is ignored by non-context store. store->sif->features & LSUP_STORE_CTX ? 0 : 1, 1, 0, 1, 2, 1, 2, store->sif->features & LSUP_STORE_CTX ? 0 : 2, 1, 0, }; for (int i = 0; i < 12; i++) { size_t ct; log_info ("Testing triple lookup #%d.", i); void *it = store->sif->lookup_fn ( store->data, lut[i][0], lut[i][1], lut[i][2], luc[i], NULL, &ct); ASSERT (it != NULL, "Error creating iterator!"); EXPECT_INT_EQ (ct, results[i]); store->sif->lu_free_fn (it); } for (int i = 0; i < NUM_TRP; i++) { LSUP_buffer_free (ser_trp[i].s); LSUP_buffer_free (ser_trp[i].p); LSUP_buffer_free (ser_trp[i].o); } store->sif->free_fn (store->data); free_triples (trp); return 0; } /** @brief Test quad store. * * Insert the same triple set twice with different contexts. * * This is skipped for non-context stores. */ static int test_quad_store() { if (!(store->sif->features & LSUP_STORE_CTX)) return 0; if (store->sif->setup_fn) EXPECT_PASS (store->sif->setup_fn (store->id, true)); log_info ("Testing quad store for %s", store->id); store->data = store->sif->new_fn (store->id, 0); ASSERT (store->data != NULL, "Error creating store data back end!"); LSUP_Triple **trp = create_triples(); LSUP_BufferTriple ser_trp[NUM_TRP]; for (int i = 0; i < NUM_TRP; i++) { LSUP_BufferTriple *tmp = LSUP_triple_serialize (trp[i]); ser_trp[i] = *tmp; free (tmp); } void *it; size_t ins; // Only triples 0÷5 in default context. it = store->sif->add_init_fn (store->data, NULL, NULL); ins = 0; for (size_t i = 0; i < 6; i++) { log_info ("Inserting triple #%d in default context.", i); LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i); EXPECT_PASS (rc); if (rc == LSUP_OK) ins++; } store->sif->add_done_fn (it); EXPECT_INT_EQ (ins, 6); LSUP_Buffer *sc1 = LSUP_default_ctx_buf; LSUP_Term *ctx2 = LSUP_iriref_new("urn:c:2", NULL); LSUP_Buffer *sc2 = LSUP_term_serialize (ctx2); // Only triples 4÷9 in context 2 (effectively 4 non-duplicates). it = store->sif->add_init_fn (store->data, sc2, NULL); ASSERT (it != NULL, "Error creating iterator!"); ins = 0; for (size_t i = 4; i < 10; i++) { log_info ("Inserting triple #%d in context 2.", i); LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i); ASSERT (rc == LSUP_OK || rc == LSUP_NOACTION, "Error adding triples!"); if (rc == LSUP_OK) ins++; } store->sif->add_done_fn (it); EXPECT_INT_EQ (ins, 4); // 6 triples in ctx1 + 6 in ctx2 - 2 duplicates EXPECT_INT_EQ (store->sif->size_fn (store->data), 10); // This context has no triples. LSUP_Term *ctx3 = LSUP_iriref_new("urn:c:3", NULL); LSUP_Buffer *sc3 = LSUP_term_serialize (ctx3); // Test lookups. LSUP_Buffer *lut[41][3] = { // Any context {NULL, NULL, NULL}, // #0 {ser_trp[0].s, NULL, NULL}, // #1 {NULL, ser_trp[0].p, NULL}, // #2 {NULL, ser_trp[0].s, NULL}, // #3 {NULL, NULL, ser_trp[6].o}, // #4 {ser_trp[4].s, ser_trp[4].p, NULL}, // #5 {NULL, ser_trp[7].p, ser_trp[7].o}, // #6 {ser_trp[5].s, NULL, ser_trp[5].o}, // #7 {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o}, // #8 {ser_trp[2].s, ser_trp[4].p, ser_trp[5].o}, // #9 // Context 1 (trp[0÷5]) {NULL, NULL, NULL}, // #10 {ser_trp[0].s, NULL, NULL}, // #11 {ser_trp[2].s, NULL, NULL}, // #12 {NULL, ser_trp[0].p, NULL}, // #13 {NULL, ser_trp[6].p, NULL}, // #14 {NULL, NULL, ser_trp[6].o}, // #15 {ser_trp[4].s, ser_trp[4].p, NULL}, // #16 {NULL, ser_trp[7].p, ser_trp[7].o}, // #17 {ser_trp[5].s, NULL, ser_trp[5].o}, // #18 {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o}, // #19 {ser_trp[6].s, ser_trp[6].p, ser_trp[6].o}, // #20 // Context 2 (trp[4÷9]) {NULL, NULL, NULL}, // #21 {ser_trp[0].s, NULL, NULL}, // #22 {NULL, ser_trp[0].p, NULL}, // #23 {NULL, ser_trp[0].s, NULL}, // #24 {NULL, NULL, ser_trp[6].o}, // #25 {ser_trp[4].s, ser_trp[4].p, NULL}, // #26 {NULL, ser_trp[7].p, ser_trp[7].o}, // #27 {ser_trp[5].s, NULL, ser_trp[5].o}, // #28 {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o}, // #29 {ser_trp[6].s, ser_trp[6].p, ser_trp[6].o}, // #30 // Non-existing context {NULL, NULL, NULL}, // #31 {ser_trp[0].s, NULL, NULL}, // #32 {NULL, ser_trp[0].p, NULL}, // #33 {NULL, ser_trp[0].s, NULL}, // #34 {NULL, NULL, ser_trp[6].o}, // #35 {ser_trp[4].s, ser_trp[4].p, NULL}, // #36 {NULL, ser_trp[7].p, ser_trp[7].o}, // #37 {ser_trp[5].s, NULL, ser_trp[5].o}, // #38 {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o}, // #39 {ser_trp[6].s, ser_trp[6].p, ser_trp[6].o}, // #40 }; LSUP_Buffer *luc[41] = { // Any context NULL, // #0 NULL, NULL, NULL, NULL, // #1-#4 NULL, NULL, NULL, // #5-#7 NULL, NULL, // #8-#9 // Context 1 (trp[0÷5]) sc1, // #10 sc1, sc1, sc1, sc1, sc1, // #11-#15 sc1, sc1, sc1, // #16-#18 sc1, sc1, // #19-#20 // Context 2 (trp[4÷9]) sc2, // #21 sc2, sc2, sc2, sc2, // #22-#25 sc2, sc2, sc2, // #26-#28 sc2, sc2, // #29-#30 // Non-existing context sc3, // #31 sc3, sc3, sc3, sc3, // #32-#35 sc3, sc3, sc3, // #36-#38 sc3, sc3, // #39-#40 }; size_t results[41] = { // NULL ctx 8, // #0 5, 1, 0, 1, // #1-#4 2, 1, 2, // #5-#7 1, 0, // #8-#9 // ctx1 6, // #10 4, 1, 1, 0, 0, // #11-#15 1, 0, 1, // #16-#18 1, 0, // #19-#20 // ctx2 4, // #21 3, 0, 0, 1, // #22-#25 2, 1, 2, // #26-#28 1, 1, // #29-#30 // ctx3 0, // #31-#32 0, 0, 0, 0, // #33-#36 0, 0, 0, // #37-#39 0, 0, // #40 }; int ctx_ct[10] = { // BEGIN ctx1 (triples 0÷5) 1, 1, 1, 1, // BEGIN ctx2 (triples 4÷9) 2, 2, // END ctx 1 1, 1, 1, 1, // END ctx 2 }; for (int i = 0; i < 41; i++) { size_t ct; log_info ("Checking triple #%d.", i); void *it = store->sif->lookup_fn ( store->data, lut[i][0], lut[i][1], lut[i][2], luc[i], NULL, &ct); ASSERT (it != NULL, "Lookup error!"); EXPECT_INT_EQ (ct, results[i]); store->sif->lu_free_fn (it); } // Check triple contexts. for (int i = 0; i < 10; i++) { void *it = store->sif->lookup_fn ( store->data, ser_trp[i].s, ser_trp[i].p, ser_trp[i].o, NULL, NULL, NULL); log_info ("Checking contexts for triple %d.", i); LSUP_Buffer *ctx_a; EXPECT_PASS (store->sif->lu_next_fn (it, NULL, &ctx_a)); store->sif->lu_free_fn (it); ASSERT (ctx_a != NULL, "No contexts found!"); size_t j = 0; while (ctx_a[j].addr) j++; free (ctx_a); EXPECT_INT_EQ (j, ctx_ct[i]); } for (int i = 0; i < NUM_TRP; i++) { LSUP_buffer_free (ser_trp[i].s); LSUP_buffer_free (ser_trp[i].p); LSUP_buffer_free (ser_trp[i].o); } LSUP_term_free (ctx2); LSUP_term_free (ctx3); LSUP_buffer_free (sc2); LSUP_buffer_free (sc3); free_triples (trp); store->sif->free_fn (store->data); return 0; } static int test_txn_commit (void) { if (!(store->sif->features & LSUP_STORE_TXN)) return 0; ASSERT ( store->sif->txn_begin_fn != NULL, "Transaction begin function is NULL!"); ASSERT ( store->sif->txn_commit_fn != NULL, "Transaction commit function is NULL!"); ASSERT ( store->sif->txn_abort_fn != NULL, "Transaction abort function is NULL!"); ASSERT ( store->sif->add_abort_fn != NULL, "Add abort function is NULL!"); ASSERT ( store->sif->iter_txn_fn != NULL, "Iterator transaction function is NULL!"); if (store->sif->setup_fn) EXPECT_PASS (store->sif->setup_fn (store->id, true)); log_info ("Testing transaction control for %s", store->id); store->data = store->sif->new_fn (store->id, 0); ASSERT (store->data != NULL, "Error creating store data back end!"); LSUP_Triple **trp = create_triples(); LSUP_BufferTriple ser_trp[NUM_TRP]; for (int i = 0; i < NUM_TRP; i++) { LSUP_BufferTriple *tmp = LSUP_triple_serialize (trp[i]); ser_trp[i] = *tmp; free (tmp); } void *it; // Start adding then commit. it = store->sif->add_init_fn (store->data, NULL, NULL); for (size_t i = 0; i < NUM_TRP; i++) { LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i); ASSERT (rc >= 0, "Error inserting triples!"); } store->sif->add_abort_fn (it); EXPECT_INT_EQ (store->sif->size_fn (store->data), 0); // Add within a transaction, commit, then commit parent transaction. void *txn; EXPECT_PASS (store->sif->txn_begin_fn (store->data, 0, &txn)); it = store->sif->add_init_fn (store->data, NULL, txn); for (size_t i = 0; i < NUM_TRP; i++) { LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i); ASSERT (rc >= 0, "Error inserting triples!"); } store->sif->add_done_fn (it); // Triples are added in child txn but parent is still open. // Size function always calculates outside of all transactions. EXPECT_INT_EQ (store->sif->size_fn (store->data), 0); size_t ct; it = store->sif->lookup_fn ( store->data, NULL, NULL, NULL, NULL, txn, &ct); store->sif->lu_free_fn (it); // Should show triples added within the parent txn. EXPECT_INT_EQ (ct, 8); // commit child txn operations. EXPECT_PASS (store->sif->txn_commit_fn (txn)); it = store->sif->lookup_fn ( store->data, NULL, NULL, NULL, NULL, NULL, &ct); store->sif->lu_free_fn (it); EXPECT_INT_EQ (ct, 8); EXPECT_INT_EQ (store->sif->size_fn (store->data), 8); for (int i = 0; i < NUM_TRP; i++) { LSUP_buffer_free (ser_trp[i].s); LSUP_buffer_free (ser_trp[i].p); LSUP_buffer_free (ser_trp[i].o); } store->sif->free_fn (store->data); free_triples (trp); return 0; } static int test_txn_abort (void) { if (!(store->sif->features & LSUP_STORE_TXN)) return 0; if (store->sif->setup_fn) EXPECT_PASS (store->sif->setup_fn (store->id, true)); log_info ("Testing transaction control for %s", store->id); store->data = store->sif->new_fn (store->id, 0); ASSERT (store->data != NULL, "Error creating store data back end!"); LSUP_Triple **trp = create_triples(); LSUP_BufferTriple ser_trp[NUM_TRP]; for (int i = 0; i < NUM_TRP; i++) { LSUP_BufferTriple *tmp = LSUP_triple_serialize (trp[i]); ser_trp[i] = *tmp; free (tmp); } void *it; // Start adding then abort. it = store->sif->add_init_fn (store->data, NULL, NULL); for (size_t i = 0; i < NUM_TRP; i++) { LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i); ASSERT (rc >= 0, "Error inserting triples!"); } store->sif->add_abort_fn (it); EXPECT_INT_EQ (store->sif->size_fn (store->data), 0); // Add within a transaction, commit, then abort parent transaction. void *txn; EXPECT_PASS (store->sif->txn_begin_fn (store->data, 0, &txn)); it = store->sif->add_init_fn (store->data, NULL, txn); for (size_t i = 0; i < NUM_TRP; i++) { LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i); ASSERT (rc >= 0, "Error inserting triples!"); } store->sif->add_done_fn (it); // Triples are added in child txn but parent is still open. // Size function always calculates outside of all transactions. EXPECT_INT_EQ (store->sif->size_fn (store->data), 0); size_t ct; it = store->sif->lookup_fn ( store->data, NULL, NULL, NULL, NULL, txn, &ct); store->sif->lu_free_fn (it); // Should show triples added within the parent txn. EXPECT_INT_EQ (ct, 8); // Discard child txn operations. store->sif->txn_abort_fn (txn); it = store->sif->lookup_fn ( store->data, NULL, NULL, NULL, NULL, NULL, &ct); store->sif->lu_free_fn (it); EXPECT_INT_EQ (ct, 0); for (int i = 0; i < NUM_TRP; i++) { LSUP_buffer_free (ser_trp[i].s); LSUP_buffer_free (ser_trp[i].p); LSUP_buffer_free (ser_trp[i].o); } store->sif->free_fn (store->data); free_triples (trp); return 0; } int store_tests() { #define ENTRY(a, b) \ store->type = LSUP_STORE_##a; \ store->id = STORE_ID_##a; \ store->sif = &b; \ RUN (test_triple_store); \ RUN (test_quad_store); \ RUN (test_txn_commit); \ RUN (test_txn_abort); BACKEND_TBL #undef ENTRY return 0; }