Browse Source

Pass count tests with context.

Stefano Cossu 4 years ago
parent
commit
42be19b02b
3 changed files with 253 additions and 150 deletions
  1. 7 4
      include/store_mdb.h
  2. 185 80
      src/store_mdb.c
  3. 61 66
      test/test_store_mdb.c

+ 7 - 4
include/store_mdb.h

@@ -44,6 +44,8 @@ typedef LSUP_rc (*store_match_fn_t)(const LSUP_TripleKey spok, void *data);
  * and checking that it's a writable directory. If the path is not specified
  * in the LSUP_STORE_PATH environment variable, a default directory is used.
  *
+ * TODO Add clear parameter.
+ *
  * @param[in,out] path Path of the suggested directory to use. It may be NULL,
  *  in which case it will be set either to the environment variable
  *  LSUP_STORE_PATH, or if that is not set, a default local path.
@@ -148,7 +150,7 @@ LSUP_rc LSUP_store_lookup(
         LSUP_MDBIterator **it, size_t *ct);
 
 
-/** @brief Get iterator results and advance the cursor.
+/** @brief Yield the matching triples and advance the iterator.
  *
  * This function also checks if the matching triple is associated with a
  * context, if one was specified. If no associated contexts are found, the next
@@ -164,10 +166,11 @@ LSUP_rc LSUP_store_lookup(
  * @param it[in] Opaque iterator handle obtained with #LSUP_store_lookup.
  *
  * @param sspo[out] #LSUP_SerTriple to be populated with three serialized terms
- * if found, NULL if not found.
+ * if found, NULL if not found. Internal callers may pass NULL if they don't
+ * need the serialized triples.
  *
- * @return LSUP_OK if results were found; LSUP_END otherwise. TODO handle
- * errors.
+ * @return LSUP_OK if results were found; LSUP_END if no (more) results were
+ * found; LSUP_DB_ERR if a MDB_* error occurred.
  */
 LSUP_rc LSUP_store_it_next(LSUP_MDBIterator *it, LSUP_SerTerm **sspo);
 

+ 185 - 80
src/store_mdb.c

@@ -49,9 +49,15 @@ struct MDBStore {
 
 /** @brief Iterator operation.
  *
- * Function executed for each iteration of a #MDBIterator.
+ * Function executed for each iteration of a #MDBIterator. It assumes that a
+ * result triple has already been found and is ready to be composed and
+ * yielded.
+ *
+ * Upon call, the rc value of the iterator structure is set to the MDB_* rc
+ * value for the next result. It is up to the caller to evaluate this value
+ * and decide whether to call the function again.
  */
-typedef LSUP_rc (*iter_op_fn_t)(struct MDBIterator *it);
+typedef void (*iter_op_fn_t)(struct MDBIterator *it);
 
 
 /** @brief Triple iterator.
@@ -91,7 +97,7 @@ struct MDBIterator {
 #define MAIN_TABLE \
     ENTRY(  T_ST,    "t:st",    0               )   /* Key to ser. term */  \
     ENTRY(  SPO_C,   "spo:c",   DUPFIXED_MASK   )   /* Triple to context */ \
-    ENTRY(  C_,      "c:",      0               )   /* Track empty contexts */\
+    ENTRY(  C_,      "c:",      0               )   /* Track empty ctx */   \
     ENTRY(  PFX_NS,  "pfx:ns",  0               )   /* Prefix to NS */      \
 
 /**
@@ -580,16 +586,24 @@ LSUP_store_lookup(
 }
 
 
-/** @brief Yield the matching triples and advance the iterator.
- */
 LSUP_rc
 LSUP_store_it_next(LSUP_MDBIterator *it, LSUP_SerTerm **sspo)
 {
-    LSUP_rc rc = (
-            it->rc == MDB_NOTFOUND ?
-            LSUP_END : it->iter_op_fn(it));
+    // Only advance if the previous it->rc wasn't already at the end.
+    if(it->rc == MDB_NOTFOUND) return LSUP_END;
+
+    if(it->rc != MDB_SUCCESS) {
+        fprintf(stderr, mdb_strerror(it->rc));
+        return LSUP_DB_ERR;
+    }
+
+    LSUP_rc rc;
+
+    it->iter_op_fn(it);
+
+    if (it->ck) {
+        rc = LSUP_NORESULT;  // Intermediary value, will never be returned.
 
-    if (it->ck && rc != LSUP_END) {
         MDB_cursor *cur;
         MDB_val key, data;
 
@@ -600,32 +614,47 @@ LSUP_store_it_next(LSUP_MDBIterator *it, LSUP_SerTerm **sspo)
         data.mv_data = &it->ck;
         data.mv_size = KLEN;
 
-        do {
+        while (rc == LSUP_NORESULT) {
+            TRACE(STR, "begin ctx loop.");
             // If ctx is specified, look if the matching triple is associated
             // with it. If not, move on to the next triple.
             // The loop normally exits when a triple with matching ctx is found
-            // (LSUP_OK), or if there are no more triples (LSUP_END).
+            // (LSUP_OK), if there are no more triples (LSUP_END), or if there
+            // is an error (LSUPP_DB_ERR).
             key.mv_data = it->spok;
 
-            if (
-                mdb_cursor_get(cur, &key, &data, MDB_GET_BOTH) == MDB_NOTFOUND
-            ) {
-                if (it->iter_op_fn(it) == LSUP_END) rc = LSUP_END;
-                else rc = LSUP_NORESULT;
+            int db_rc = mdb_cursor_get(cur, &key, &data, MDB_GET_BOTH);
+
+            if (db_rc == MDB_SUCCESS) {
+                rc = LSUP_OK;
+                TRACE(STR, "Triple found for context.");
+            }
+
+            else if (db_rc == MDB_NOTFOUND) {
+                TRACE(STR, "No triples found for context.");
+                if (it->rc == MDB_NOTFOUND) rc = LSUP_END;
+                else it->iter_op_fn(it);
+
+            } else {
+                fprintf(stderr, mdb_strerror(it->rc));
+                rc = LSUP_DB_ERR;
             }
 
-        } while (rc == LSUP_NORESULT);
+        }
 
         mdb_cursor_close(cur);
-    }
 
-    if (rc == LSUP_OK) {
-        LSUP_store_key_to_sterm(it->store, *it->spok[0], *sspo);
-        LSUP_store_key_to_sterm(it->store, *it->spok[1], *sspo + 1);
-        LSUP_store_key_to_sterm(it->store, *it->spok[2], *sspo + 2);
+    } else rc = LSUP_OK;
 
-    // TODO error handling.
-    } else *sspo = NULL;
+    if (sspo) {
+        if (rc == LSUP_OK) {
+            LSUP_store_key_to_sterm(it->store, *it->spok[0], *sspo);
+            LSUP_store_key_to_sterm(it->store, *it->spok[1], *sspo + 1);
+            LSUP_store_key_to_sterm(it->store, *it->spok[2], *sspo + 2);
+
+        // TODO error handling.
+        } else *sspo = NULL;
+    }
 
     return rc;
 }
@@ -861,18 +890,12 @@ index_triple(
  *
  * Cursor: spo:c
  */
-inline static LSUP_rc
+inline static void
 it_next_0bound(struct MDBIterator *it)
 {
-    LSUP_Key *spok = (LSUP_Key*)it->data.mv_data;
-
-    *it->spok[0] = spok[0];
-    *it->spok[1] = spok[1];
-    *it->spok[2] = spok[2];
+    it->spok = (LSUP_TripleKey*)&it->data.mv_data;
 
     it->rc = mdb_cursor_get(it->cur, &it->key, NULL, MDB_NEXT);
-
-    return LSUP_OK;
 }
 
 
@@ -882,26 +905,33 @@ it_next_0bound(struct MDBIterator *it)
  *
  * Cursor: s:po, p:so, or o:sp.
  */
-inline static LSUP_rc
+inline static void
 it_next_1bound(struct MDBIterator *it)
 {
-    LSUP_Key **lu_dset = it->data.mv_data;
+    LSUP_DoubleKey *lu_dset = it->data.mv_data;
 
-    *it->spok[it->term_order[0]] = it->luk[0];
-    *it->spok[it->term_order[1]] = lu_dset[it->i][0];
-    *it->spok[it->term_order[2]] = lu_dset[it->i][1];
+    it->spok[0][it->term_order[0]] = it->luk[0];
+    it->spok[0][it->term_order[1]] = lu_dset[it->i][0];
+    it->spok[0][it->term_order[2]] = lu_dset[it->i][1];
+
+    TRACE(
+            "Composed triple: {%lu %lu %lu}",
+            it->spok[0][0], it->spok[0][1], it->spok[0][2]);
 
     // Ensure next block within the same page is not beyond the last.
-    if(it->i < it->data.mv_size / DBL_KLEN - 1)
+    if(it->i < it->data.mv_size / DBL_KLEN - 1) {
         it->i ++;
-    else {
+        TRACE("Increasing page cursor to %lu.", it->i);
+        TRACE("it->rc: %d", it->rc);
+
+    } else {
         // If the last block in the page is being yielded,
         // move cursor to beginning of next page.
         it->i = 0;
+        TRACE("Reset page cursor to %lu.", it->i);
         it->rc = mdb_cursor_get(it->cur, &it->key, &it->data, MDB_NEXT_MULTIPLE);
+        TRACE("it->rc: %d", it->rc);
     }
-
-    return LSUP_OK;
 }
 
 
@@ -911,17 +941,17 @@ it_next_1bound(struct MDBIterator *it)
  *
  * Cursor: po:s, so:p, or sp:o.
  */
-inline static LSUP_rc
+inline static void
 it_next_2bound(struct MDBIterator *it)
 {
     LSUP_Key *lu_dset = it->data.mv_data;
 
-    *it->spok[it->term_order[0]] = it->luk[0];
-    *it->spok[it->term_order[1]] = it->luk[1];
-    *it->spok[it->term_order[2]] = lu_dset[it->i];
+    it->spok[0][it->term_order[0]] = it->luk[0];
+    it->spok[0][it->term_order[1]] = it->luk[1];
+    it->spok[0][it->term_order[2]] = lu_dset[it->i];
 
     // Ensure next block within the same page is not beyond the last.
-    if(it->i < it->data.mv_size / DBL_KLEN - 1)
+    if(it->i < it->data.mv_size / KLEN - 1)
         it->i ++;
     else {
         // If the last block in the page is being yielded,
@@ -929,8 +959,6 @@ it_next_2bound(struct MDBIterator *it)
         it->i = 0;
         it->rc = mdb_cursor_get(it->cur, &it->key, &it->data, MDB_NEXT_MULTIPLE);
     }
-
-    return LSUP_OK;
 }
 
 
@@ -940,11 +968,10 @@ it_next_2bound(struct MDBIterator *it)
  * which was already set in the first result, or there was none, i.e. it->rc is
  * already MDB_NOTFOUND and this function will not be called.
  */
-inline static LSUP_rc
+inline static void
 it_next_3bound(struct MDBIterator *it)
 {
     it->rc = MDB_NOTFOUND;
-    return LSUP_OK;
 }
 
 
@@ -986,7 +1013,12 @@ lookup_0bound(struct MDBStore *store, struct MDBIterator *it, size_t *ct)
     it->rc = mdb_cursor_get(it->cur, &it->key, &it->data, MDB_FIRST);
     it->iter_op_fn = it_next_0bound;
 
-    return it->rc == MDB_SUCCESS ? LSUP_OK : LSUP_NORESULT;
+    if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
+        fprintf(stderr, "Database error: %s", mdb_strerror(it->rc));
+        return LSUP_DB_ERR;
+    }
+
+    return LSUP_OK;
 }
 
 
@@ -997,26 +1029,47 @@ lookup_1bound(
 {
     it->term_order = (const uint8_t*)lookup_ordering_1bound[idx0];
 
-    TRACE("Looking up 1 bound term: %lx\n", it->luk[0]);
+    TRACE("Looking up 1 bound term: %lu\n", it->luk[0]);
 
-    if(store->txn) it->txn = store->txn;
-    else {
-        it->rc = mdb_txn_begin(store->env, NULL, MDB_RDONLY, &it->txn);
-        if (it->rc != MDB_SUCCESS) abort();
+    if(!it->txn) {
+        if(store->txn) it->txn = store->txn;
+        else {
+            it->rc = mdb_txn_begin(store->env, NULL, MDB_RDONLY, &it->txn);
+            if (it->rc != MDB_SUCCESS) abort();
+        }
     }
 
-
     mdb_cursor_open(it->txn, store->dbi[lookup_indices[idx0]], &it->cur);
 
     it->key.mv_data = it->luk;
     it->key.mv_size = KLEN;
 
     if(ct) {
+        // If a context is specified, the only way to count triples matching
+        // the context is to loop over them.
         if (it->ck != NULL_KEY) {
-            it->rc = mdb_cursor_get(
-                    it->cur, &it->key, &it->data, MDB_GET_MULTIPLE);
-            while (it->rc != MDB_NOTFOUND) {
+            struct MDBIterator *ct_it;
+            CRITICAL(ct_it = malloc(sizeof(struct MDBIterator)));
+
+            ct_it->luk[0] = it->luk[0];
+            LSUP_TripleKey ct_spok;
+            ct_it->spok = &ct_spok;
+            ct_it->ck = it->ck;
+            ct_it->store = it->store;
+            ct_it->txn = it->txn;
+            ct_it->key = it->key;
+            ct_it->data = it->data;
+            ct_it->i = 0;
+            lookup_1bound(store, idx0, ct_it, NULL);
+
+            while (LSUP_store_it_next(ct_it, NULL) != LSUP_END) {
+                ct[0] ++;
+                TRACE("Counter increased to %lu.", *ct);
             }
+
+            mdb_cursor_close(ct_it->cur);
+            free(ct_it);
+
         } else {
             it->rc = mdb_cursor_get(it->cur, &it->key, &it->data, MDB_SET);
             if (it->rc == MDB_SUCCESS) mdb_cursor_count(it->cur, ct);
@@ -1030,7 +1083,12 @@ lookup_1bound(
     if (it->rc == MDB_SUCCESS)
         it->rc = mdb_cursor_get(it->cur, &it->key, &it->data, MDB_GET_MULTIPLE);
 
-    return it->rc == MDB_SUCCESS ? LSUP_OK : LSUP_NORESULT;
+    if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
+        fprintf(stderr, "Database error: %s", mdb_strerror(it->rc));
+        return LSUP_DB_ERR;
+    }
+
+    return LSUP_OK;
 }
 
 
@@ -1082,8 +1140,13 @@ lookup_2bound(
     luk[luk1_offset] = it->luk[0];
     luk[luk2_offset] = it->luk[1];
 
-    if(store->txn) it->txn = store->txn;
-    else mdb_txn_begin(store->env, NULL, MDB_RDONLY, &it->txn);
+    if(!it->txn) {
+        if(store->txn) it->txn = store->txn;
+        else {
+            it->rc = mdb_txn_begin(store->env, NULL, MDB_RDONLY, &it->txn);
+            if (it->rc != MDB_SUCCESS) abort();
+        }
+    }
 
     it->key.mv_data = luk;
     it->key.mv_size = DBL_KLEN;
@@ -1091,7 +1154,33 @@ lookup_2bound(
     mdb_cursor_open(it->txn, dbi, &it->cur);
     it->rc = mdb_cursor_get(it->cur, &it->key, &it->data, MDB_SET);
 
-    if(ct && it->rc == MDB_SUCCESS) mdb_cursor_count(it->cur, ct);
+    if(ct) {
+        // If a context is specified, the only way to count triples matching
+        // the context is to loop over them.
+        if (it->ck != NULL_KEY) {
+            struct MDBIterator *ct_it;
+            CRITICAL(ct_it = malloc(sizeof(struct MDBIterator)));
+
+            ct_it->luk[0] = it->luk[0];
+            ct_it->luk[1] = it->luk[1];
+            LSUP_TripleKey ct_spok;
+            ct_it->spok = &ct_spok;
+            ct_it->ck = it->ck;
+            ct_it->store = it->store;
+            ct_it->txn = it->txn;
+            lookup_2bound(store, idx0, idx1, ct_it, NULL);
+
+            while (LSUP_store_it_next(ct_it, NULL) != LSUP_END) {
+                ct[0] ++;
+            }
+            if (ct_it->cur) mdb_cursor_close(ct_it->cur);
+            free(ct_it);
+
+        } else {
+            it->rc = mdb_cursor_get(it->cur, &it->key, &it->data, MDB_SET);
+            if (it->rc == MDB_SUCCESS) mdb_cursor_count(it->cur, ct);
+        }
+    }
 
     it->i = 0;
     it->iter_op_fn = it_next_2bound;
@@ -1100,7 +1189,12 @@ lookup_2bound(
     if (it->rc == MDB_SUCCESS)
         it->rc = mdb_cursor_get(it->cur, &it->key, &it->data, MDB_GET_MULTIPLE);
 
-    return it->rc == MDB_SUCCESS ? LSUP_OK : LSUP_NORESULT;
+    if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
+        fprintf(stderr, "Database error: %s", mdb_strerror(it->rc));
+        return LSUP_DB_ERR;
+    }
+
+    return LSUP_OK;
 }
 
 
@@ -1110,31 +1204,42 @@ lookup_3bound(struct MDBStore *store, struct MDBIterator *it, size_t *ct)
     TRACE(
             "Looking up 3 bound: {%lx, %lx, %lx}",
             it->luk[0], it->luk[1], it->luk[2]);
-    LSUP_rc rc = LSUP_NORESULT;
 
     if(store->txn) it->txn = store->txn;
     else mdb_txn_begin(store->env, NULL, MDB_RDONLY, &it->txn);
 
-    MDB_cursor *cur;
-    it->rc = mdb_cursor_open(it->txn, store->dbi[IDX_S_PO], &cur);
-
     it->key.mv_data = it->luk;
-    it->key.mv_size = KLEN;
-    it->data.mv_data = it->luk + 1;
-    it->data.mv_size = DBL_KLEN;
 
-    it->rc = mdb_cursor_get(cur, &it->key, &it->data, MDB_GET_BOTH);
-    if(ct && it->rc  == MDB_SUCCESS) *ct = 1;
+    if(it->ck != NULL_KEY) {
+        it->rc = mdb_cursor_open(it->txn, store->dbi[IDX_SPO_C], &it->cur);
+
+        it->key.mv_size = TRP_KLEN;
+        it->data.mv_data = &it->ck;
+        it->data.mv_size = KLEN;
 
-    if (it->rc == MDB_NOTFOUND) {
-        rc = LSUP_NORESULT;
     } else {
-        it->iter_op_fn = it_next_3bound;
-        it->spok = &it->luk;
-        rc = LSUP_OK;
+        it->rc = mdb_cursor_open(it->txn, store->dbi[IDX_S_PO], &it->cur);
+
+        it->key.mv_size = KLEN;
+        it->data.mv_data = it->luk + 1;
+        it->data.mv_size = DBL_KLEN;
     }
 
-    return rc;
+    it->rc = mdb_cursor_get(it->cur, &it->key, &it->data, MDB_GET_BOTH);
+
+    mdb_cursor_close(it->cur);
+
+    if(ct && it->rc == MDB_SUCCESS) *ct = 1;
+
+    it->iter_op_fn = it_next_3bound;
+    it->spok = &it->luk;
+
+    if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
+        fprintf(stderr, "Database error: %s", mdb_strerror(it->rc));
+        return LSUP_DB_ERR;
+    }
+
+    return LSUP_OK;
 }
 
 

+ 61 - 66
test/test_store_mdb.c

@@ -81,10 +81,8 @@ static int test_triple_store()
         LSUP_MDBIterator *it;
 
         TRACE("Testing triple lookup #%d.\n", i);
-        int rc = LSUP_store_lookup(store, lut[i], &it, &ct);
-        if (results[i] > 0) EXPECT_INT_EQ(rc, LSUP_OK);
-        else EXPECT_INT_EQ(rc, LSUP_NORESULT);
 
+        EXPECT_PASS(LSUP_store_lookup(store, lut[i], &it, &ct));
         EXPECT_INT_EQ(ct, results[i]);
 
         LSUP_store_it_free(it);
@@ -155,97 +153,97 @@ static int test_quad_store()
     LSUP_Term *ctx3 = LSUP_uri_new("urn:c:3");
     LSUP_SerTerm sc3_s;
     LSUP_SerTerm *sc3 = &sc3_s;
-    LSUP_term_serialize(ctx2, &sc3_s);
+    LSUP_term_serialize(ctx3, &sc3_s);
 
     LSUP_SerTerm *lut[41][4] = {
         // Any context
-        {NULL, NULL, NULL, NULL},
+        {NULL, NULL, NULL, NULL},                               // #0
 
-        {ser_trp[0].s, NULL, NULL, NULL},
-        {NULL, ser_trp[0].p, NULL, NULL},
-        {NULL, ser_trp[0].s, NULL, NULL},
-        {NULL, NULL, ser_trp[6].o, NULL},
+        {ser_trp[0].s, NULL, NULL, NULL},                       // #1
+        {NULL, ser_trp[0].p, NULL, NULL},                       // #2
+        {NULL, ser_trp[0].s, NULL, NULL},                       // #3
+        {NULL, NULL, ser_trp[6].o, NULL},                       // #4
 
-        {ser_trp[4].s, ser_trp[4].p, NULL, NULL},
-        {NULL, ser_trp[7].p, ser_trp[7].o, NULL},
-        {ser_trp[5].s, NULL, ser_trp[5].o, NULL},
+        {ser_trp[4].s, ser_trp[4].p, NULL, NULL},               // #5
+        {NULL, ser_trp[7].p, ser_trp[7].o, NULL},               // #6
+        {ser_trp[5].s, NULL, ser_trp[5].o, NULL},               // #7
 
-        {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o, NULL},
-        {ser_trp[2].s, ser_trp[4].p, ser_trp[5].o, NULL},
+        {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o, NULL},       // #8
+        {ser_trp[2].s, ser_trp[4].p, ser_trp[5].o, NULL},       // #9
 
         // Context 1 (trp[0÷5])
-        {NULL, NULL, NULL, sc1},
+        {NULL, NULL, NULL, sc1},                                // #10
 
-        {ser_trp[0].s, NULL, NULL, sc1},
-        {ser_trp[2].s, NULL, NULL, sc1},
-        {NULL, ser_trp[0].p, NULL, sc1},
-        {NULL, ser_trp[6].p, NULL, sc1},
-        {NULL, NULL, ser_trp[6].o, sc1},
+        {ser_trp[0].s, NULL, NULL, sc1},                        // #11
+        {ser_trp[2].s, NULL, NULL, sc1},                        // #12
+        {NULL, ser_trp[0].p, NULL, sc1},                        // #13
+        {NULL, ser_trp[6].p, NULL, sc1},                        // #14
+        {NULL, NULL, ser_trp[6].o, sc1},                        // #15
 
-        {ser_trp[4].s, ser_trp[4].p, NULL, sc1},
-        {NULL, ser_trp[7].p, ser_trp[7].o, sc1},
-        {ser_trp[5].s, NULL, ser_trp[5].o, sc1},
+        {ser_trp[4].s, ser_trp[4].p, NULL, sc1},                // #16
+        {NULL, ser_trp[7].p, ser_trp[7].o, sc1},                // #17
+        {ser_trp[5].s, NULL, ser_trp[5].o, sc1},                // #18
 
-        {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o, sc1},
-        {ser_trp[6].s, ser_trp[6].p, ser_trp[6].o, sc1},
+        {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o, sc1},        // #19
+        {ser_trp[6].s, ser_trp[6].p, ser_trp[6].o, sc1},        // #20
 
 
         // Context 2 (trp[4÷9])
-        {NULL, NULL, NULL, sc2},
+        {NULL, NULL, NULL, sc2},                                // #21
 
-        {ser_trp[0].s, NULL, NULL, sc2},
-        {NULL, ser_trp[0].p, NULL, sc2},
-        {NULL, ser_trp[0].s, NULL, sc2},
-        {NULL, NULL, ser_trp[6].o, sc2},
+        {ser_trp[0].s, NULL, NULL, sc2},                        // #22
+        {NULL, ser_trp[0].p, NULL, sc2},                        // #23
+        {NULL, ser_trp[0].s, NULL, sc2},                        // #24
+        {NULL, NULL, ser_trp[6].o, sc2},                        // #25
 
-        {ser_trp[4].s, ser_trp[4].p, NULL, sc2},
-        {NULL, ser_trp[7].p, ser_trp[7].o, sc2},
-        {ser_trp[5].s, NULL, ser_trp[5].o, sc2},
+        {ser_trp[4].s, ser_trp[4].p, NULL, sc2},                // #26
+        {NULL, ser_trp[7].p, ser_trp[7].o, sc2},                // #27
+        {ser_trp[5].s, NULL, ser_trp[5].o, sc2},                // #28
 
-        {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o, sc2},
-        {ser_trp[6].s, ser_trp[6].p, ser_trp[6].o, sc2},
+        {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o, sc2},        // #29
+        {ser_trp[6].s, ser_trp[6].p, ser_trp[6].o, sc2},        // #30
 
 
         // Non-existing context
-        {NULL, NULL, NULL, sc3},
+        {NULL, NULL, NULL, sc3},                                // #31
 
-        {ser_trp[0].s, NULL, NULL, sc3},
-        {NULL, ser_trp[0].p, NULL, sc3},
-        {NULL, ser_trp[0].s, NULL, sc3},
-        {NULL, NULL, ser_trp[6].o, sc3},
+        {ser_trp[0].s, NULL, NULL, sc3},                        // #32
+        {NULL, ser_trp[0].p, NULL, sc3},                        // #33
+        {NULL, ser_trp[0].s, NULL, sc3},                        // #34
+        {NULL, NULL, ser_trp[6].o, sc3},                        // #35
 
-        {ser_trp[4].s, ser_trp[4].p, NULL, sc3},
-        {NULL, ser_trp[7].p, ser_trp[7].o, sc3},
-        {ser_trp[5].s, NULL, ser_trp[5].o, sc3},
+        {ser_trp[4].s, ser_trp[4].p, NULL, sc3},                // #36
+        {NULL, ser_trp[7].p, ser_trp[7].o, sc3},                // #37
+        {ser_trp[5].s, NULL, ser_trp[5].o, sc3},                // #38
 
-        {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o, sc3},
-        {ser_trp[6].s, ser_trp[6].p, ser_trp[6].o, sc3},
+        {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o, sc3},        // #39
+        {ser_trp[6].s, ser_trp[6].p, ser_trp[6].o, sc3},        // #40
     };
 
-    size_t results[40] = {
+    size_t results[41] = {
         // NULL ctx
-        8,
-        5, 1, 0, 1,
-        2, 1, 2,
-        1, 0,
+        8,                      // #0
+        5, 1, 0, 1,             // #1-#4
+        2, 1, 2,                // #5-#7
+        1, 0,                   // #8-#9
 
         // ctx1
-        6,
-        4, 1, 0, 0,
-        1, 0, 0,
-        1, 0,
+        6,                      // #10
+        4, 1, 1, 0, 0,          // #11-#15
+        1, 0, 1,                // #16-#18
+        1, 0,                   // #19-#20
 
         // ctx2
-        4,
-        3, 0, 0, 1,
-        2, 1, 1,
-        1, 1,
+        4,                      // #21
+        3, 0, 0, 1,             // #22-#25
+        2, 1, 2,                // #26-#28
+        1, 1,                   // #29-#30
 
         // ctx3
-        0,
-        0, 0, 0, 0,
-        0, 0, 0,
-        0, 0,
+        0,                      // #31-#32
+        0, 0, 0, 0,             // #33-#36
+        0, 0, 0,                // #37-#39
+        0, 0,                   // #40
     };
 
     for (int i = 0; i < 41; i++) {
@@ -270,10 +268,7 @@ static int test_quad_store()
         else printf("NULL");
         printf("}\n");
 
-        int rc = LSUP_store_lookup(store, lut[i], &it, &ct);
-        if (results[i] > 0) EXPECT_INT_EQ(rc, LSUP_OK);
-        else EXPECT_INT_EQ(rc, LSUP_NORESULT);
-
+        EXPECT_PASS(LSUP_store_lookup(store, lut[i], &it, &ct));
         EXPECT_INT_EQ(ct, results[i]);
 
         LSUP_store_it_free(it);