diff options
-rw-r--r-- | ccan/aga/_info | 1 | ||||
-rw-r--r-- | ccan/aga/test/api-adjacency.c | 93 | ||||
-rw-r--r-- | ccan/aga/test/chain.c | 27 | ||||
-rw-r--r-- | ccan/aga/test/error-graph.c | 56 | ||||
-rw-r--r-- | ccan/aga/test/full.c | 49 | ||||
-rw-r--r-- | ccan/aga/test/grid.c | 84 | ||||
-rw-r--r-- | ccan/aga/test/parallel.c | 62 | ||||
-rw-r--r-- | ccan/aga/test/simple-graph.c | 11 | ||||
-rw-r--r-- | ccan/aga/test/simple-graph.h | 217 | ||||
-rw-r--r-- | ccan/aga/test/traversal1.c | 121 | ||||
-rw-r--r-- | ccan/aga/test/trivial.c | 39 |
11 files changed, 760 insertions, 0 deletions
diff --git a/ccan/aga/_info b/ccan/aga/_info index 22b9a66f..9e44b2cf 100644 --- a/ccan/aga/_info +++ b/ccan/aga/_info @@ -36,6 +36,7 @@ int main(int argc, char *argv[]) if (strcmp(argv[1], "testdepends") == 0) { printf("ccan/container_of\n"); + printf("ccan/ptrint\n"); return 0; } diff --git a/ccan/aga/test/api-adjacency.c b/ccan/aga/test/api-adjacency.c new file mode 100644 index 00000000..26cb52b3 --- /dev/null +++ b/ccan/aga/test/api-adjacency.c @@ -0,0 +1,93 @@ +#include "config.h" + +#include <stddef.h> +#include <assert.h> + +#include <ccan/aga/aga.h> + +#include <ccan/tap/tap.h> + +#include "simple-graph.h" + +static void test_adjacency(const char *name, + const struct simple_graph *sg, + const struct adjacency_list *at) +{ + int i; + + for (i = 0; at[i].from != 0; i++) { + const void *e; + struct aga_edge_info ei; + int j = 0; + const struct aga_node *from; + int err; + + assert(i < MAX_NODES); + + from = &sg->nodes[at[i].from]; + + aga_for_each_edge_info(e, ei, err, &sg->g, from) { + const struct aga_node *cmpto; + + assert(j < MAX_EDGES); + cmpto = &sg->nodes[at[i].to[j]]; + ok(cmpto == ei.to, + "%s: %p #%d -> #%ld (expected #%d -> #%d)", name, e, + at[i].from, (ei.to - sg->nodes), + at[i].from, at[i].to[j]); + + j++; + } + if (at[i].to[j] < 0) { + ok(err == at[i].to[j], "%s: %p #%d -> ERROR %d", + name, e, at[i].from, at[i].to[j]); + continue; /* Move onto next node on errors */ + } + assert(j < MAX_EDGES); + ok(at[i].to[j] == 0, + "%s: %p #%d -> --- (expected #%d -> #%d)", name, e, + at[i].from, at[i].from, at[i].to[j]); + } +} + +int main(void) +{ + struct trivial_graph tg; + struct parallel_graph pg; + struct full_graph fg; + struct chain_graph cg; + struct grid_graph gg1, gg2; + struct error_graph eg; + struct traversal1_graph t1g; + + plan_tests(1 + 5 + 30 + 22 + 21 + 33 + 6 + 21); + + trivial_graph_init(&tg); + test_adjacency("trivial", &tg.sg, trivial_adjacency); + + parallel_graph_init(&pg, 3); + test_adjacency("parallel nlinks 3", &pg.sg, + parallel_adjacency_nlinks3); + + full_graph_init(&fg, 5); + test_adjacency("full 5", &fg.sg, full_adjacency_5); + + chain_graph_init(&cg, 8); + test_adjacency("chain 8", &cg.fg.sg, chain_adjacency_8); + + grid_graph_init(&gg1, 3, 3, true, true, false, false); + test_adjacency("grid 3x3 right-down", &gg1.sg, + grid_adjacency_3x3_rightdown); + + grid_graph_init(&gg2, 3, 3, true, true, true, true); + test_adjacency("grid 3x3 all", &gg2.sg, + grid_adjacency_3x3_all); + + error_graph_init(&eg); + test_adjacency("error graph", &eg.sg, error_adjacency); + + traversal1_graph_init(&t1g); + test_adjacency("traversal1 graph", &t1g.sg, traversal1_adjacency); + + return exit_status(); +} diff --git a/ccan/aga/test/chain.c b/ccan/aga/test/chain.c new file mode 100644 index 00000000..fbb0f90d --- /dev/null +++ b/ccan/aga/test/chain.c @@ -0,0 +1,27 @@ +#include "config.h" + +#include <stdlib.h> + +#include <ccan/container_of/container_of.h> + +#include <ccan/aga/aga.h> + +#include "simple-graph.h" + +static int chain_edge_info(const struct aga_graph *g, + const struct aga_node *node, + struct aga_node *edge, + struct aga_edge_info *ei) +{ + if ((edge == node + 1) || (node == edge + 1)) + ei->to = edge; + + return 0; +} + +void chain_graph_init(struct chain_graph *cg, int nnodes) +{ + cg->fg.nnodes = nnodes; + simple_graph_init(&cg->fg.sg, full_first_edge, full_next_edge, + chain_edge_info); +} diff --git a/ccan/aga/test/error-graph.c b/ccan/aga/test/error-graph.c new file mode 100644 index 00000000..a52544b1 --- /dev/null +++ b/ccan/aga/test/error-graph.c @@ -0,0 +1,56 @@ +#include "config.h" + +#include <assert.h> + +#include <ccan/aga/aga.h> +#include <ccan/container_of/container_of.h> +#include <ccan/ptrint/ptrint.h> + +#include "simple-graph.h" + +static ptrint_t *error_first_edge(const struct aga_graph *g, + const struct aga_node *n) +{ + return int2ptr(1); +} + +static ptrint_t *error_next_edge(const struct aga_graph *g, + const struct aga_node *n, + ptrint_t *edge) +{ + assert(edge == int2ptr(1)); + + return NULL; +} + +static int error_edge_info(const struct aga_graph *g, const struct aga_node *n, + ptrint_t *edge, struct aga_edge_info *ei) +{ + struct error_graph *eg = container_of(g, struct error_graph, sg.g); + int fromindex = n - eg->sg.nodes; + + switch (fromindex) { + case 1: + ei->to = &eg->sg.nodes[2]; + break; + + case 2: + ei->to = NULL; + break; + + case 3: + ei->to = &eg->sg.nodes[4]; + break; + + default: + return -1; + } + + return 0; +} + +void error_graph_init(struct error_graph *eg) +{ + simple_graph_init(&eg->sg, error_first_edge, error_next_edge, + error_edge_info); +} diff --git a/ccan/aga/test/full.c b/ccan/aga/test/full.c new file mode 100644 index 00000000..f04486af --- /dev/null +++ b/ccan/aga/test/full.c @@ -0,0 +1,49 @@ +#include "config.h" + +#include <stdlib.h> +#include <assert.h> + +#include <ccan/container_of/container_of.h> + +#include <ccan/aga/aga.h> + +#include "simple-graph.h" + +struct aga_node *full_first_edge(const struct aga_graph *g, + const struct aga_node *node) +{ + struct full_graph *fg = container_of(g, struct full_graph, sg.g); + + return &fg->sg.nodes[1]; +} + +struct aga_node *full_next_edge(const struct aga_graph *g, + const struct aga_node *node, + struct aga_node *edge) +{ + struct full_graph *fg = container_of(g, struct full_graph, sg.g); + int index = (edge - fg->sg.nodes); + + if (index < fg->nnodes) + return &fg->sg.nodes[index + 1]; + else + return NULL; +} + +static int full_edge_info(const struct aga_graph *g, + const struct aga_node *node, + struct aga_node *edge, + struct aga_edge_info *ei) +{ + ei->to = edge; + return 0; +} + +void full_graph_init(struct full_graph *fg, int nnodes) +{ + assert(nnodes < MAX_NODES); + + fg->nnodes = nnodes; + simple_graph_init(&fg->sg, full_first_edge, full_next_edge, + full_edge_info); +} diff --git a/ccan/aga/test/grid.c b/ccan/aga/test/grid.c new file mode 100644 index 00000000..9f03a27f --- /dev/null +++ b/ccan/aga/test/grid.c @@ -0,0 +1,84 @@ +#include "config.h" + +#include <assert.h> + +#include <ccan/container_of/container_of.h> +#include <ccan/ptrint/ptrint.h> + +#include <ccan/aga/aga.h> + +#include "simple-graph.h" + +static ptrint_t *grid_first_edge(const struct aga_graph *g, + const struct aga_node *n) +{ + return int2ptr(1); +} + +static ptrint_t *grid_next_edge(const struct aga_graph *g, + const struct aga_node *n, + ptrint_t *e) +{ + int index = ptr2int(e); + + if (index < 4) + return int2ptr(index + 1); + else + return NULL; +} + +static int grid_edge_info(const struct aga_graph *g, + const struct aga_node *n, + ptrint_t *e, struct aga_edge_info *ei) +{ + struct grid_graph *gg = container_of(g, struct grid_graph, sg.g); + int ni = n - gg->sg.nodes; + int x = ((ni - 1) % gg->nx) + 1; + int y = ((ni - 1) / gg->nx) + 1; + int i = ptr2int(e); + + assert((x >= 1) && (x <= gg->nx)); + assert((y >= 1) && (y <= gg->ny)); + + switch (i) { + case 1: /* right */ + if (gg->right && (x != gg->nx)) + ei->to = &gg->sg.nodes[ni + 1]; + break; + + case 2: /* down */ + if (gg->down && (y != gg->ny)) + ei->to = &gg->sg.nodes[ni + gg->nx]; + break; + + case 3: /* left */ + if (gg->left && (x != 1)) + ei->to = &gg->sg.nodes[ni - 1]; + break; + + case 4: /* up */ + if (gg->up && (y != 1)) + ei->to = &gg->sg.nodes[ni - gg->nx]; + break; + + default: + assert(0); + } + return 0; +} + +void grid_graph_init(struct grid_graph *gg, int nx, int ny, + bool right, bool down, bool left, bool up) +{ + assert((nx * ny) < MAX_NODES); + + gg->nx = nx; + gg->ny = ny; + gg->right = right; + gg->down = down; + gg->left = left; + gg->up = up; + + simple_graph_init(&gg->sg, grid_first_edge, grid_next_edge, + grid_edge_info); +} diff --git a/ccan/aga/test/parallel.c b/ccan/aga/test/parallel.c new file mode 100644 index 00000000..997280bc --- /dev/null +++ b/ccan/aga/test/parallel.c @@ -0,0 +1,62 @@ +#include "config.h" + +#include <assert.h> + +#include <ccan/aga/aga.h> +#include <ccan/container_of/container_of.h> +#include <ccan/ptrint/ptrint.h> + +#include "simple-graph.h" + +static ptrint_t *parallel_first_edge(const struct aga_graph *g, + const struct aga_node *n) +{ + struct parallel_graph *pg = container_of(g, struct parallel_graph, sg.g); + + if (n != &pg->sg.nodes[1]) { + assert(n == &pg->sg.nodes[2]); + return NULL; + } + + if (pg->nlinks) + return int2ptr(1); + else + return NULL; +} + +static ptrint_t *parallel_next_edge(const struct aga_graph *g, + const struct aga_node *n, + ptrint_t *edge) +{ + struct parallel_graph *pg = container_of(g, struct parallel_graph, sg.g); + int index = ptr2int(edge); + + if (n != &pg->sg.nodes[1]) { + assert(n == &pg->sg.nodes[2]); + return NULL; + } + + if (index < pg->nlinks) + return int2ptr(index + 1); + else + return NULL; +} + +static int parallel_edge_info(const struct aga_graph *g, const struct aga_node *n, + ptrint_t *edge, struct aga_edge_info *ei) +{ + struct parallel_graph *pg = container_of(g, struct parallel_graph, sg.g); + + assert(n == &pg->sg.nodes[1]); + + ei->to = &pg->sg.nodes[2]; + return 0; +} + +void parallel_graph_init(struct parallel_graph *pg, int nlinks) +{ + pg->nlinks = nlinks; + + simple_graph_init(&pg->sg, parallel_first_edge, parallel_next_edge, + parallel_edge_info); +} diff --git a/ccan/aga/test/simple-graph.c b/ccan/aga/test/simple-graph.c new file mode 100644 index 00000000..d475a7c5 --- /dev/null +++ b/ccan/aga/test/simple-graph.c @@ -0,0 +1,11 @@ +#include <ccan/aga/aga.h> + +#include "simple-graph.h" + +void simple_graph_init_(struct simple_graph *sg) +{ + int i; + + for (i = 0; i < MAX_NODES; i++) + aga_node_init(&sg->nodes[i]); +} diff --git a/ccan/aga/test/simple-graph.h b/ccan/aga/test/simple-graph.h new file mode 100644 index 00000000..3f4f9acc --- /dev/null +++ b/ccan/aga/test/simple-graph.h @@ -0,0 +1,217 @@ +#ifndef _TEST_GRAPHS_H +#define _TEST_GRAPHS_H + +#include <stdbool.h> + +#define MAX_NODES 16 +#define MAX_EDGES 16 /* Max edges per node */ + +struct simple_graph { + struct aga_graph g; + /* We don't use nodes[0] just to avoid awkward -1 and +1 in + * the code */ + struct aga_node nodes[MAX_NODES + 1]; +}; + +void simple_graph_init_(struct simple_graph *sg); +#define simple_graph_init(sg_, fefn_, nefn_, eifn_) \ + do { \ + simple_graph_init_((sg_)); \ + aga_init_graph(&(sg_)->g, (fefn_), (nefn_), (eifn_)); \ + } while (0) + +struct adjacency_list { + int from; + int to[MAX_EDGES]; +}; + +/* Trivial graph + * + * (A) + * + * The simplest possible graph: one node, no edges + */ +struct trivial_graph { + struct simple_graph sg; +}; +void trivial_graph_init(struct trivial_graph *tg); +static const struct adjacency_list trivial_adjacency[] = { + {1, {}}, + {}, +}; + +/* Parallel graph + * + * -- + * / \ + * (A) => (B) + * \ / + * -- + * + * Two nodes (A & B), with PARALLEL_NLINKS edges all from A->B. + */ +struct parallel_graph { + int nlinks; + struct simple_graph sg; +}; +void parallel_graph_init(struct parallel_graph *pg, int nlinks); +static const struct adjacency_list parallel_adjacency_nlinks3[] = { + {1, {2, 2, 2}}, + {2, {}}, + {}, +}; + +/* Full graph + * + * n nodes with an edge from every node to every other node (including + * itself) + */ +struct full_graph { + int nnodes; + struct simple_graph sg; +}; +void full_graph_init(struct full_graph *fg, int nnodes); +static const struct adjacency_list full_adjacency_5[] = { + {1, {1, 2, 3, 4, 5}}, + {2, {1, 2, 3, 4, 5}}, + {3, {1, 2, 3, 4, 5}}, + {4, {1, 2, 3, 4, 5}}, + {5, {1, 2, 3, 4, 5}}, + {}, +}; +struct aga_node *full_first_edge(const struct aga_graph *g, + const struct aga_node *node); +struct aga_node *full_next_edge(const struct aga_graph *g, + const struct aga_node *node, + struct aga_node *edge); + + +/* Chain graph + * + * --> --> --> + * A B C D + * <-- <-- <-- + * + * nnodes nodes arranged in a linear sequence, edges from each node to + * the previous and next + */ +struct chain_graph { + struct full_graph fg; +}; +void chain_graph_init(struct chain_graph *cg, int nnodes); +static const struct adjacency_list chain_adjacency_8[] = { + {1, {2}}, + {2, {1, 3}}, + {3, {2, 4}}, + {4, {3, 5}}, + {5, {4, 6}}, + {6, {5, 7}}, + {7, {6, 8}}, + {8, {7}}, + {}, +}; + + +/* Grid graph(s) + * + * A -> B -> C + * | | | + * v v v + * D -> E -> F + * | | | + * v v v + * G -> H -> I + * + * nx * ny nodes arranged in an nx * ny grid. Depending on + * parameters, edges to the node to the right / down / left / up of + * each node + */ +struct grid_graph { + int nx, ny; + bool right, down, left, up; + struct simple_graph sg; +}; +void grid_graph_init(struct grid_graph *gg, int nx, int ny, + bool right, bool down, bool left, bool up); +static const struct adjacency_list grid_adjacency_3x3_rightdown[] = { + {1, {2, 4}}, + {2, {3, 5}}, + {3, {6}}, + {4, {5, 7}}, + {5, {6, 8}}, + {6, {9}}, + {7, {8}}, + {8, {9}}, + {9, {}}, + {}, +}; +static const struct adjacency_list grid_adjacency_3x3_all[] = { + {1, {2, 4}}, + {2, {3, 5, 1}}, + {3, {6, 2}}, + {4, {5, 7, 1}}, + {5, {6, 8, 4, 2}}, + {6, {9, 5, 3}}, + {7, {8, 4}}, + {8, {9, 7, 5}}, + {9, {8, 6}}, + {}, +}; + +/* Error graph + * + * A -> B + * + * C -> D -> ??? + * + * This is for testing reporting of errors by the edge_info function. + * 5 nodes are arranged as above, with the link from D always + * returning an error. + */ +struct error_graph { + struct simple_graph sg; +}; +void error_graph_init(struct error_graph *eg); +static const struct adjacency_list error_adjacency[] = { + {1, {2}}, + {2, {}}, + {3, {4}}, + {4, {-1}}, + {}, +}; + +/* Traversal-1 graph + * + * -> D <- + * / \ + * -> B G <- + * / \ / \ + * A => E <= I + * \ / \ / + * -> C H <- + * \ / + * -> F <- + * + * This provides an example of a graph which can't be traversed (with + * DFS or BFS) from a single starting node. It can be traversed with + * two starting points, but several nodes can be reached from either, + * complicating the logic to avoid double-traversal. + */ +struct traversal1_graph { + struct simple_graph sg; +}; +void traversal1_graph_init(struct traversal1_graph *t1g); +static const struct adjacency_list traversal1_adjacency[] = { + {1, {2, 3}}, + {2, {4, 5}}, + {3, {5, 6}}, + {4, {}}, + {5, {}}, + {6, {}}, + {7, {5, 4}}, + {8, {6, 5}}, + {9, {8, 7}}, + {}, +}; + +#endif /* _TEST_GRAPHS_H */ diff --git a/ccan/aga/test/traversal1.c b/ccan/aga/test/traversal1.c new file mode 100644 index 00000000..bce99ae3 --- /dev/null +++ b/ccan/aga/test/traversal1.c @@ -0,0 +1,121 @@ +#include "config.h" + +#include <assert.h> + +#include <ccan/container_of/container_of.h> +#include <ccan/ptrint/ptrint.h> + +#include <ccan/aga/aga.h> + +#include "simple-graph.h" + +static ptrint_t *traversal1_first_edge(const struct aga_graph *g, + const struct aga_node *n) +{ + struct traversal1_graph *t1g = container_of(g, struct traversal1_graph, + sg.g); + int ni = n - t1g->sg.nodes; + + switch (ni) { + case 1: + case 2: + case 3: + + case 7: + case 8: + case 9: + return int2ptr(1); + + case 4: + case 5: + case 6: + return NULL; + + default: + assert(0); + } +} + +static ptrint_t *traversal1_next_edge(const struct aga_graph *g, + const struct aga_node *n, + ptrint_t *e) +{ + struct traversal1_graph *t1g = container_of(g, struct traversal1_graph, + sg.g); + int ni = n - t1g->sg.nodes; + int index = ptr2int(e); + + assert((ni < 4) || (ni > 6)); + if (index == 1) + return int2ptr(2); + else if (index == 2) + return NULL; + else + assert(0); +} + +static int traversal1_edge_info(const struct aga_graph *g, + const struct aga_node *n, + ptrint_t *e, struct aga_edge_info *ei) +{ + struct traversal1_graph *t1g = container_of(g, struct traversal1_graph, + sg.g); + int ni = n - t1g->sg.nodes; + int index = ptr2int(e); + + assert((index == 1) || (index == 2)); + + switch (ni) { + case 1: + if (index == 1) + ei->to = &t1g->sg.nodes[2]; + else + ei->to = &t1g->sg.nodes[3]; + break; + + case 2: + if (index == 1) + ei->to = &t1g->sg.nodes[4]; + else + ei->to = &t1g->sg.nodes[5]; + break; + case 3: + if (index == 1) + ei->to = &t1g->sg.nodes[5]; + else + ei->to = &t1g->sg.nodes[6]; + break; + + case 7: + if (index == 1) + ei->to = &t1g->sg.nodes[5]; + else + ei->to = &t1g->sg.nodes[4]; + break; + + case 8: + if (index == 1) + ei->to = &t1g->sg.nodes[6]; + else + ei->to = &t1g->sg.nodes[5]; + break; + + case 9: + if (index == 1) + ei->to = &t1g->sg.nodes[8]; + else + ei->to = &t1g->sg.nodes[7]; + break; + + default: + assert(0); + } + return 0; +} + +void traversal1_graph_init(struct traversal1_graph *t1g) +{ + simple_graph_init(&t1g->sg, traversal1_first_edge, + traversal1_next_edge, + traversal1_edge_info); +} diff --git a/ccan/aga/test/trivial.c b/ccan/aga/test/trivial.c new file mode 100644 index 00000000..8d428456 --- /dev/null +++ b/ccan/aga/test/trivial.c @@ -0,0 +1,39 @@ +#include "config.h" + +#include <assert.h> + +#include <ccan/container_of/container_of.h> + +#include <ccan/aga/aga.h> + +#include "simple-graph.h" + +static const void *trivial_first_edge(const struct aga_graph *g, + const struct aga_node *node) +{ + struct trivial_graph *tg = container_of(g, struct trivial_graph, sg.g); + + assert(node == &tg->sg.nodes[1]); + return NULL; +} + +static const void *trivial_next_edge(const struct aga_graph *g, + const struct aga_node *node, + const void *edge) +{ + assert(0); +} + +static int trivial_edge_info(const struct aga_graph *g, + const struct aga_node *node, + const void *edge, + struct aga_edge_info *ei) +{ + assert(0); +} + +void trivial_graph_init(struct trivial_graph *tg) +{ + simple_graph_init(&tg->sg, trivial_first_edge, trivial_next_edge, + trivial_edge_info); +} |