Commit 2192bdd9 authored by David Gibson's avatar David Gibson

aga: Simple test graphs

This adds code for a number of example graphs for use in tests of the aga
module.  They also demonstrate several different ways of constructing
graphs using the aga callbacks.

It adds one actual testcase, which just verifies that the example graph
look like what they're supposed to.  Specifically it computes a set of
adjacency lists for the example graphs from the callback code, then
compares that to a canned example of what it should be.
Signed-off-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
parent 3b7409ea
......@@ -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;
}
......
#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();
}
#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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#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]);
}
#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 */
#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);
}
#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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment