#include "test.h" #include "graph.h" #include "assets/triples.h" #define N_LUT 13 static int _graph_new (LSUP_StoreType type) { const LSUP_StoreInt *sif = LSUP_store_int (type); if (sif->setup_fn) sif->setup_fn (NULL, true); LSUP_Graph *gr; LSUP_Store *store; if (type == LSUP_STORE_HTABLE) { gr = LSUP_graph_new (NULL, NULL, NULL); } else { store = LSUP_store_new (type, NULL, 0); gr = LSUP_graph_new (store, NULL, NULL); } ASSERT (gr != NULL, "Error creating graph!"); EXPECT_PASS (LSUP_graph_set_uri (gr, LSUP_iriref_new ("urn:gr:1", NULL))); EXPECT_STR_EQ (LSUP_graph_uri (gr)->data, "urn:gr:1"); // Check that setup function is idempotent with clear == false. if (sif->setup_fn) EXPECT_INT_EQ ( sif->setup_fn (NULL, false), LSUP_NOACTION); ASSERT ( strcmp (LSUP_graph_uri (gr)->data, "urn:gr:1") == 0, "Graph URI mismatch!"); EXPECT_INT_EQ (LSUP_graph_size (gr), 0); LSUP_graph_free (gr); if (type != LSUP_STORE_HTABLE) LSUP_store_free (store); return 0; } static int _graph_add (LSUP_StoreType type) { const LSUP_StoreInt *sif = LSUP_store_int (type); if (sif->setup_fn) sif->setup_fn (NULL, true); LSUP_Triple *trp = create_triples(); LSUP_Graph *gr; LSUP_Store *store; if (type == LSUP_STORE_HTABLE) { gr = LSUP_graph_new (NULL, NULL, NULL); } else { store = LSUP_store_new (type, NULL, 0); gr = LSUP_graph_new (store, NULL, NULL); } ASSERT (gr != NULL, "Error creating graph!"); size_t ct; LSUP_graph_add (gr, trp, &ct); EXPECT_INT_EQ (ct, 8); EXPECT_INT_EQ (LSUP_graph_size (gr), 8); for (int i = 0; i < sizeof (trp); i++) { log_info ("checking triple #%d.", i); ASSERT (LSUP_graph_contains (gr, trp + i), "Triple not in graph!"); } LSUP_Triple *missing_trp = LSUP_triple_new (trp[1].s, trp[6].p, trp[4].o); ASSERT (! LSUP_graph_contains (gr, missing_trp), "Triple in graph!"); free (missing_trp); free_triples (trp); // gr copied data. LSUP_graph_free (gr); if (type != LSUP_STORE_HTABLE) LSUP_store_free (store); return 0; } static int _graph_lookup (LSUP_StoreType type) { const LSUP_StoreInt *sif = LSUP_store_int (type); LSUP_Triple *trp = create_triples(); // Lookup triples. LSUP_Term *lu_trp[N_LUT][3] = { {NULL, NULL, NULL}, // 8 matches {trp[0].s, NULL, NULL}, // 5 matches {NULL, trp[2].p, NULL}, // 3 matches {NULL, NULL, trp[5].o}, // 2 matches {trp[0].s, trp[0].p, NULL}, // 1 match {NULL, trp[0].p, trp[0].o}, // 1 match {trp[0].s, trp[2].p, trp[5].o}, // 1 match {trp[0].p, NULL, NULL}, // 0 matches {NULL, trp[2].s, NULL}, // 0 matches {NULL, NULL, trp[5].p}, // 0 matches {trp[2].s, trp[6].p, NULL}, // 0 matches {NULL, trp[1].p, trp[5].o}, // 0 matches {trp[2].s, trp[2].p, trp[5].o}, // 0 matches }; // Lookup result counts. size_t lu_ct[N_LUT] = { 8, 5, 3, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0 }; /* TODO // Index of lookup matches from trp. size_t lu_match[N_LUT][8] = { {0, 1, 2, 3, 4, 5, 6, 7}, {0, 3, 4, 5, 7}, {2, 4, 7}, {5, 7}, {0}, {0}, {7}, {}, {}, {}, {}, {}, {}, }; // Index of lookup non-matches from trp. size_t lu_no_match[N_LUT][8] = { {}, {1, 2, 6}, {0, 1, 3, 5, 6}, {0, 1, 2, 3, 4, 6}, {1, 2, 3, 4, 5, 6, 7}, {1, 2, 3, 4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6}, {0, 1, 2, 3, 4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7}, }; */ if (sif->setup_fn) sif->setup_fn (NULL, true); LSUP_Graph *gr; LSUP_Store *store; if (type == LSUP_STORE_HTABLE) { gr = LSUP_graph_new (NULL, NULL, NULL); } else { store = LSUP_store_new (type, NULL, 0); gr = LSUP_graph_new (store, NULL, NULL); } size_t ct; LSUP_graph_add (gr, trp, &ct); EXPECT_INT_EQ (ct, 8); EXPECT_INT_EQ (LSUP_graph_size (gr), 8); for (int i = 0; i < N_LUT; i++) { log_info ("Checking triple #%d on %d.", i, type); LSUP_GraphIterator *it = LSUP_graph_lookup ( gr, lu_trp[i][0], lu_trp[i][1], lu_trp[i][2], &ct); EXPECT_INT_EQ (ct, lu_ct[i]); // Verify that iteration count matches stat count. LSUP_Triple *spo = NULL; ct = 0; while (LSUP_graph_iter_next (it, &spo) != LSUP_END) { ct++; // TODO do something useful with the triple. LSUP_triple_free (spo); spo = NULL; } LSUP_triple_free (spo); EXPECT_INT_EQ (ct, lu_ct[i]); /* TODO for (int j = 0; j < 8; j++) { for (int k = 0; LSUP_graph_iter_next(it) != LSUP_END; k++) { ASSERT ( LSUP_graph_contains (trp[lu_match[j]]), "Triple not found!"); ASSERT ( !(LSUP_graph_contains (trp[lu_no_match[j]])), "Unexpected triple found!"); } } */ LSUP_graph_iter_free (it); }; free_triples (trp); LSUP_graph_free (gr); if (type != LSUP_STORE_HTABLE) LSUP_store_free (store); return 0; } static int _graph_remove (LSUP_StoreType type) { const LSUP_StoreInt *sif = LSUP_store_int (type); if (sif->setup_fn) sif->setup_fn (NULL, true); LSUP_Triple *trp = create_triples(); LSUP_Graph *gr; LSUP_Store *store; if (type == LSUP_STORE_HTABLE) { gr = LSUP_graph_new (NULL, NULL, NULL); } else { store = LSUP_store_new (type, NULL, 0); gr = LSUP_graph_new (store, NULL, NULL); } size_t ct; LSUP_graph_add (gr, trp, &ct); EXPECT_INT_EQ (ct, 8); EXPECT_INT_EQ (LSUP_graph_size (gr), 8); // Triples 0, 3, 4, 5, 7 will be removed. LSUP_graph_remove (gr, trp[0].s, NULL, NULL, &ct); EXPECT_INT_EQ (ct, 5); EXPECT_INT_EQ (LSUP_graph_size (gr), 3); ASSERT (!LSUP_graph_contains (gr, trp + 0), "Unexpected triple found!"); ASSERT (LSUP_graph_contains (gr, trp + 1), "Triple not in graph!"); ASSERT (LSUP_graph_contains (gr, trp + 2), "Triple not in graph!"); ASSERT (!LSUP_graph_contains (gr, trp + 3), "Unexpected triple found!"); ASSERT (!LSUP_graph_contains (gr, trp + 4), "Unexpected triple found!"); ASSERT (!LSUP_graph_contains (gr, trp + 5), "Unexpected triple found!"); ASSERT (LSUP_graph_contains (gr, trp + 6), "Triple not in graph!"); ASSERT (!LSUP_graph_contains (gr, trp + 7), "Unexpected triple found!"); free_triples (trp); // gr copied data. LSUP_graph_free (gr); if (type != LSUP_STORE_HTABLE) LSUP_store_free (store); // TODO Test complete removal of triples from index when they are not // in another context. return 0; } static int _graph_txn (LSUP_StoreType type) { const LSUP_StoreInt *sif = LSUP_store_int (type); if (!(sif->features & LSUP_STORE_TXN)) return 0; if (sif->setup_fn) sif->setup_fn (NULL, true); LSUP_Triple *trp = create_triples(); LSUP_Graph *gr; LSUP_Store *store = type == LSUP_STORE_HTABLE ? NULL : LSUP_store_new (type, NULL, 0); gr = LSUP_graph_new (store, NULL, NULL); void *txn; size_t ct; EXPECT_PASS (LSUP_store_begin (store, 0, &txn)); EXPECT_PASS (LSUP_graph_add_txn (txn, gr, trp, &ct)); LSUP_store_abort (store, txn); // NOTE that ct reports the count before the txn was aborted. This is // intentional. EXPECT_INT_EQ (ct, 8); EXPECT_INT_EQ (LSUP_graph_size (gr), 0); EXPECT_PASS (LSUP_store_begin (store, 0, &txn)); EXPECT_PASS (LSUP_graph_add_txn (txn, gr, trp, &ct)); LSUP_store_commit (store, txn); EXPECT_INT_EQ (ct, 8); EXPECT_INT_EQ (LSUP_graph_size (gr), 8); LSUP_graph_free (gr); if (type != LSUP_STORE_HTABLE) LSUP_store_free (store); free_triples (trp); // gr copied data. return 0; } static int test_environment() { // The env should already be initialized and re-initializing it is idempotent. EXPECT_INT_EQ (LSUP_IS_INIT, true); ASSERT (LSUP_init() > 0, "Error initializing environment!"); EXPECT_INT_EQ (LSUP_IS_INIT, true); // Tearing down is idempotent too. LSUP_done(); EXPECT_INT_EQ (LSUP_IS_INIT, false); LSUP_done(); EXPECT_INT_EQ (LSUP_IS_INIT, false); ASSERT (LSUP_init() >= 0, "Environment not initialized!"); EXPECT_INT_EQ (LSUP_IS_INIT, true); ASSERT (LSUP_init() >= 0, "Environment not initialized!"); EXPECT_INT_EQ (LSUP_IS_INIT, true); return 0; } static int test_graph_new() { #define ENTRY(a, b) \ if (_graph_new (LSUP_STORE_##a) != 0) return -1; BACKEND_TBL #undef ENTRY return 0; } static int test_graph_add() { #define ENTRY(a, b) \ if (_graph_add (LSUP_STORE_##a) != 0) return -1; BACKEND_TBL #undef ENTRY return 0; } static int test_graph_lookup() { #define ENTRY(a, b) \ if (_graph_lookup (LSUP_STORE_##a) != 0) return -1; BACKEND_TBL #undef ENTRY return 0; } static int test_graph_remove() { #define ENTRY(a, b) \ if (_graph_remove (LSUP_STORE_##a) != 0) return -1; BACKEND_TBL #undef ENTRY return 0; } static int test_graph_txn() { /* * Test transactions only if the backend supports them. */ #define ENTRY(a, b) \ if (_graph_txn (LSUP_STORE_##a) != 0) return -1; BACKEND_TBL #undef ENTRY return 0; } static int test_graph_copy() { LSUP_Triple *trp = create_triples(); LSUP_Graph *gr1 = LSUP_graph_new (NULL, NULL, NULL); ASSERT (gr1 != NULL, "Error creating graph!"); LSUP_graph_add (gr1, trp, NULL); // Copy to graph with same store type. LSUP_Graph *gr2 = LSUP_graph_new (NULL, NULL, NULL); EXPECT_PASS (LSUP_graph_copy_contents (gr1, gr2)); EXPECT_INT_EQ (LSUP_graph_size (gr1), LSUP_graph_size (gr2)); for (int i = 0; i < sizeof (trp); i++) { log_info ("checking triple #%d.", i); ASSERT ( LSUP_graph_contains (gr2, trp + i), "Triple not in copied graph!"); } // Copy to graph with a different store type. LSUP_Graph *gr3 = LSUP_graph_new (NULL, NULL, NULL); EXPECT_PASS (LSUP_graph_copy_contents (gr1, gr3)); EXPECT_INT_EQ (LSUP_graph_size (gr1), LSUP_graph_size (gr2)); for (int i = 0; i < sizeof (trp); i++) { log_info ("checking triple #%d.", i); ASSERT ( LSUP_graph_contains (gr3, trp + i), "Triple not in copied graph!"); } LSUP_graph_free (gr3); LSUP_graph_free (gr2); LSUP_graph_free (gr1); free_triples (trp); return 0; } int graph_tests() { RUN (test_environment); RUN (test_graph_new); RUN (test_graph_add); RUN (test_graph_lookup); RUN (test_graph_remove); RUN (test_graph_copy); RUN (test_graph_txn); return 0; }