# build. This means you Mac users.
# ARCH= -arch x386 -arch x86_64
-LDFLAGS= $(ARCH) -lm
+LDFLAGS= $(ARCH) -lm -L.
CFLAGS= -Wall -Wno-parentheses $(ARCH) -I. -I..
-OBJS= hexagon.o astar.o
+OBJS= hexagon.o astar.o lib/cantor.o
SRC=$(OBJS:.o=.c)
TESTS= t/cantor.t t/distance.t t/adjacency.t t/range.t t/hexbin.t \
- t/gridsize.t t/center.t t/astar.t t/polar.t
+ t/gridsize.t t/center.t t/polar.t
PREFIX=/usr/local
+RELEASE = $(shell git tag | tail -1)
-all: libhexagon.a docs testfiles
+all: libhexagon.a docs testfiles hexagon
+
+hexagon: cli/hexagon.o libhexagon.a
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lhexagon
clean:
rm -f $(OBJS) libhexagon.a $(TESTS)
+ rm -f doc/hexagon.3 doc/hexagon.pdf doc/hexagon.tex doc/hexagon.txt \
+ doc/hexagon.idx doc/hexagon.aux doc/hexagon.log doc/hexagon.toc
-t/%.t: t/%.o t/ctap.o $(OBJS)
+t/%.t: t/%.o t/ctap.o t/ctap.h $(OBJS)
$(CC) -I.. -I. -o $@ $+ $(LDFLAGS)
-testfiles: $(TESTS)
+testfiles: $(TESTS)
+
+test: libhexagon.a prove $(TESTS)
+ @./prove $(TESTS) 2>/dev/null
-test: libhexagon.a $(TESTS)
+ptest: libhexagon.a $(TESTS)
@prove --exec '' $(TESTS) 2>/dev/null
+t/ctap.c: ctap/ctap.c
+ cp $< $@
+
+t/ctap.h: ctap/ctap.h
+ cp $< $@
+
+prove.c: ctap/prove.c
+ cp $< $@
+
libhexagon.a: $(OBJS)
ar r $@ $+
ranlib $@
cp hexagon.h $(PREFIX)/include
cp doc/hexagon.3 $(PREFIX)/share/man/man3
-docs:
- make -C doc
+docs: doc/hexagon.3 doc/hexagon.txt
+
+doc/hexagon.3: doc/hexagon.pod
+ pod2man --center='hexagon library' --section=3 --release='Version $(RELEASE)' $+ > $@
+
+doc/hexagon.txt: doc/hexagon.pod
+ pod2text -l $+ > $@
+
+doc/hexagon.pdf: doc/hexagon.pod
+ pod2latex -full $+
+ pdflatex hexagon.tex
+ pdflatex hexagon.tex
+ pdflatex hexagon.tex
+ rm -f hexagon.tex hexagon.idx hexagon.aux hexagon.toc hexagon.log
.c.o:
$(CC) $(CFLAGS) -c -o $@ $<
.SUFFIXES: .c .h
+
#define DEBUG_ASTAR 1
#endif
-static double default_metric(int a, int b) {
+static double default_metric(struct HL_hex a, struct HL_hex b) {
return 1.0;
}
-static double default_heuristic(int a, int b) {
+static double default_heuristic(struct HL_hex a, struct HL_hex b) {
return (double)HL_distance(a, b);
}
state->metric = default_metric;
state->heuristic = default_heuristic;
- state->neighbor = HL_adjacent_hex;
+ state->neighbor = HL_adjhexp;
return state;
}
}
}
-static struct HL_astar_hex *add_open(struct HL_astar *s, int hex) {
+static struct HL_astar_hex *add_open(struct HL_astar *s, struct HL_hex hex) {
struct HL_astar_hex *node;
#ifdef DEBUG_ASTAR
- fprintf(stderr, "add to open %d\n", hex);
+ fprintf(stderr, "add to open %d\n", HL_cantor(hex));
#endif
if (!s || s->error) {
/* TODO check in open or closed */
node = malloc(sizeof *node);
#ifdef DEBUG_ASTAR
- fprintf(stderr, "add to open %d = %p\n", hex, node);
+ fprintf(stderr, "add to open %d = %p\n", HL_cantor(hex), node);
#endif
node->hex = hex;
}
#ifdef DEBUG_ASTAR
- fprintf(stderr, "Closing hex: (%d,%d)\n", HL_cantor_x(h->hex), HL_cantor_y(h->hex));
+ fprintf(stderr, "Closing hex: (%d,%d)\n", h->hex.x, h->hex.y);
#endif
h->open = 0;
if (h->prev) h->prev->next = h->next;
return h;
}
-struct HL_astar_hex *in_closed(struct HL_astar *s, int hex) {
+struct HL_astar_hex *in_closed(struct HL_astar *s, struct HL_hex hex) {
struct HL_astar_hex *h;
if (!s) return 0;
for (h = s->closed; h; h = h->next) {
- if (h->hex == hex) return h;
+ if (h->hex.x == hex.x && h->hex.y == hex.y) return h;
}
return NULL;
}
-struct HL_astar_hex *in_open(struct HL_astar *s, int hex) {
+struct HL_astar_hex *in_open(struct HL_astar *s, struct HL_hex hex) {
struct HL_astar_hex *h;
if (!s) return 0;
for (h = s->open; h; h = h->next) {
- if (h->hex == hex) return h;
+ if (h->hex.x == hex.x && h->hex.y == hex.y) return h;
}
return NULL;
}
void dump_list(char *name, struct HL_astar_hex *x) {
fprintf(stderr, "%s list: ", name);
for(; x; x = x->next) {
- fprintf(stderr, "(%d,%d)", HL_cantor_x(x->hex), HL_cantor_y(x->hex));
+ fprintf(stderr, "(%d,%d)", x->hex.x, x->hex.y);
}
fprintf(stderr, "\n");
}
* find a path from start to end. we use A*
*/
int HL_findpath(struct HL_astar *s, int loops) {
- int dir, y;
+ int dir;
+ struct HL_hex y;
struct HL_astar_hex *node, *x, *yopen;
int tent_better;
double cost;
s->error = 0;
- if (s->start == s->goal) return 0;
+ if (HL_distance(s->start, s->goal) == 0) return 0;
node = add_open(s, s->start);
node->g = 0.0;
#ifdef DEBUG_ASTAR
fprintf(stderr, "A* findpath: ");
- fprintf(stderr, "(%d,%d)", HL_cantor_x(s->start), HL_cantor_y(s->start));
+ fprintf(stderr, "(%d,%d)", s->start.x, s->start.y);
fprintf(stderr, " -> ");
- fprintf(stderr, "(%d,%d)", HL_cantor_x(s->goal), HL_cantor_y(s->goal));
+ fprintf(stderr, "(%d,%d)", s->goal.x, s->goal.y);
fprintf(stderr, "\n");
#endif
x = lowrank(s);
#ifdef DEBUG_ASTAR
fprintf(stderr, "checking lowest f hex = (%d,%d) f = %f, g = %f, h = %f\n",
- HL_cantor_x(x->hex), HL_cantor_y(x->hex),
+ x->hex.x, x->hex.y,
x->f, x->g, x->h
);
#endif
- if (x->hex == s->goal) {
+ if (HL_distance(x->hex, s->goal) == 0) {
break;
}
closehex(s, x);
- for (dir = 0; y = s->neighbor(x->hex, dir),dir<6; dir++) {
- if (y == 0) continue; /* no hex in that direction */
+ int n;
+ for (dir = 0; n = s->neighbor(x->hex, dir, &y),dir<6; dir++) {
+ if (n == 0) continue; /* no hex in that direction */
if (in_closed(s, y)) {
continue;
}
#ifdef DEBUG_ASTAR
fprintf(stderr, "checking dir %d, hex (%d, %d)\n", dir,
- HL_cantor_x(y), HL_cantor_y(y));
+ y.x, y.y);
#endif
cost = x->g + s->metric(x->hex, y);
yopen->f = yopen->g + yopen->h;
#ifdef DEBUG_ASTAR
fprintf(stderr, "maybe better hex = (%d,%d) f = %f, g = %f, h = %f\n",
- HL_cantor_x(yopen->hex), HL_cantor_y(yopen->hex),
+ yopen->hex.x, yopen->hex.y,
yopen->f, yopen->g, yopen->h
);
#endif
}
/* error no path ? */
- if (x->hex != s->goal) {
+ if (HL_distance(x->hex, s->goal) != 0) {
s->error = 1;
return 0;
}
if (yopen->parent) {
yopen->parent->next = yopen;
}
- if (yopen->hex == s->start) s->from = yopen;
+ if (HL_distance(yopen->hex, s->start) == 0) s->from = yopen;
#ifdef DEBUG_ASTAR
fprintf(stderr, "(%p %d %d,%d)",
yopen, yopen->hex,
- HL_cantor_x(yopen->hex),
- HL_cantor_y(yopen->hex)
+ yopen->hex.x,
+ yopen->hex.y
);
#endif
s->pathlen++;
+++ /dev/null
-RELEASE = $(shell git tag | tail -1)
-
-hexagon.3: hexagon.pod
- pod2man --center='hexagon library' --section=3 --release='Version $(RELEASE)' $+ > $@
-
-hexagon.txt: hexagon.pod
- pod2text -l $+
-
-hexagon.pdf: hexagon.pod
- pod2latex -full $+
- pdflatex hexagon.tex
- pdflatex hexagon.tex
- pdflatex hexagon.tex
- rm -f hexagon.tex hexagon.idx hexagon.aux hexagon.toc hexagon.log
-
-clean:
- rm -f hexagon.3 hexagon.pdf hexagon.tex hexagon.txt \
- hexagon.idx hexagon.aux hexagon.log hexagon.toc
#include <stdlib.h>
#include <limits.h>
-static int inversecantor(int cantor, int *x, int *y);
+/* constructor function */
+struct HL_hex HL_hex_xy(int x, int y) {
+ struct HL_hex h;
+ h.x = x;
+ h.y = y;
+ return h;
+}
+
+int HL_hex_cmp(struct HL_hex const *a, struct HL_hex const *b) {
+ if (a->x != b->x) {
+ return a->x > b->x ? 1 : -1;
+ }
+ if (a->y != b->y) {
+ return a->y > b->y ? 1 : -1;
+ }
+ return 0;
+}
+
+int HL_hex_eq(struct HL_hex const *a, struct HL_hex const *b) {
+ return a->x == b->x && a->y == b->y;
+}
/*
* This file is written by Nathan Wagner and dedicated to the public
#endif
-/*
-c = 1.0 = distance from center to vertex
-b = distance to hex side
-a = distance along hex side
-
-A = angle at point center, this is the polar coord angle
-B = angle from vertex, this is 60 degrees
-C = Other angle, which is 180 - 60 - A = 120 - A
-
-sin B = sin 60 = sqrt(3) / 2
-cos B = cos 60 = 1/2
-
-b = c * sin B / ( sin A cos B + sin B cos A)
-
-c * sin B / ( sin A cos B + sin B cos A)
-
-0.5 * sqrt3
--------------
-sinA 0.5 + sqrt3 * 0.5 cosA
-
-sqrt3
--------------
-sinA + sqrt3 cosA
-
-b=
-3
--------------
-cosA + sqrt3 sinA
-
-x = b cosA
-y = b sinA
-*/
-
#define RD (180.0 / M_PI)
/* angle ranges from 0-6 */
/* distance is 0 at origin, 1.0 at the hex side */
-/* return the polar distance ? */
-double HL_polar(double angle, double distance, double *x, double *y) {
- double A, B, C, b, c;
- double sinB, sinC;
#if 0
double sqrt3;
sqrt3 = sqrt(3.0);
+
+ 1
+ /---\
+ 2/ \0
+ / \
+ \ /
+ 3\ /5
+ \---/
+ 4
+
+ /\
+ 0/ \5
+ / \
+ | |
+ 1| |4
+ | |
+ \ /
+ 2\ /3
+ \/
+
#endif
+/* return the polar distance */
+struct HL_point HL_polar(double angle, double distance, double *d) {
+ double A, B, C, b;
+ struct HL_point pt;
+
+#if 0
+ C
+ /\
+ b / \ a
+ / \
+ / \
+ A -------- B
+ c
+
+ c = 1 ; fixed, will scale later
+ B = 60 ; exterior angle of hextant
+ C = 180-B-A = 120-A ; sum of angles of a triangle
+ A = function argument ; the polar coordinate we are calculating
+ b = c * sin(B) / sin(C) ; law of sines
+
+#endif
/* convert angle to radians */
angle = angle * M_PI / 3.0;
A = fmod(angle, M_PI/3.0); /* constrain to within an equilateral */
B = M_PI / 3.0;
- C = M_PI - A - B;
+ C = M_PI - B - A;
- sinB = sin(B);
- sinC = sin(C);
+ b = sin(B) / sin(C);
- c = 1.0;
- c = HL_vertexv[0];
-#if 0
- /* provided for completeness, but we don't use the value */
- sinA = sin(A);
- a = c * sinA / sinC;
-#endif
- b = c * sinB / sinC;
+ /* scale for hex one unit from side to side, rather than
+ * one unit from center to vertex
+ */
+/* b = b * sqrt(3.0) / 2.0 / 2.0; */
+ b = b / sqrt(3);
- if (x) {
- *x = b * cos(angle);
- }
+ pt.x = distance * b * cos(angle);
+ pt.y = distance * b * sin(angle);
- if (y) {
- *y = b * sin(angle);
+ if (d) {
+ *d = distance * b;
}
- return b;
+ return pt;
}
-void HL_vertices(int cantor, double *vc) {
+void HL_vertices(struct HL_hex hex, double *vc) {
int i;
double xc, yc;
+ struct HL_point center;
- HL_hexcenter(cantor, &xc, &yc);
+ center = HL_hexcenter(hex);
+ xc = center.x;
+ yc = center.y;
for (i=0; i<6; i++) {
*vc++ = hexptvd[i][0] + xc;
*vc++ = hexptvd[0][1] + yc;
}
+#if 0
void HL_trianglefan(int cantor, double *vc) {
HL_hexcenter(cantor, vc, vc+1);
HL_vertices(cantor, vc+2);
}
+#endif
-double HL_center_x(int cantor) {
- double x;
-
- HL_hexcenter(cantor, &x, 0);
- return x;
-}
+struct HL_point HL_hexcenter(struct HL_hex hex) {
+ int x, y;
+ struct HL_point center;
+ double yc;
-double HL_center_y(int cantor) {
- double y;
+ double stride = 1.5/sqrt(3.0);
- HL_hexcenter(cantor, 0, &y);
- return y;
-}
+ x = hex.x;
+ y = hex.y;
-int HL_hexcenter(int cantor, double *xc, double *yc) {
- int x, y;
- double stride = 1.5/sqrt(3.0);
+ center.x = x * stride;
- inversecantor(cantor, &x, &y);
+ if (x >= 0) yc = y + ((x + 1) % 2) / 2.0 - 0.5;
+ if (x < 0) yc = y + ((-x + 1) % 2) / 2.0 - 0.5;
- if (xc) *xc = x * stride;
- if (yc && x >= 0) *yc = y + ((x + 1) % 2) / 2.0 - 0.5;
- if (yc && x < 0) *yc = y + ((-x + 1) % 2) / 2.0 - 0.5;
+ center.y = yc;
- return cantor;
+ return center;
}
/*
* oriented. If that is not the case, you will need to transform
* your input coordinates first.
*/
-int HL_cantor_bin(double x, double y) {
- return HL_hexbin(1.0, x, y, 0, 0);
+struct HL_hex HL_bin(double x, double y) {
+ return HL_hexbin(1.0, x, y);
+}
+
+struct HL_isohex HL_xy2ijk(struct HL_hex hex) {
+ int pi, pj, pk;
+ int x, y;
+ struct HL_isohex iso;
+
+ x = hex.x;
+ y = hex.y;
+
+ pi = x;
+ pj = -y;
+
+ if (x < 0) {
+ pj = pj + (-x + 1) / 2;
+ } else {
+ pj = pj - x/2;
+ }
+ pk = -pi - pj;
+
+ iso.i = pi;
+ iso.j = pj;
+ iso.k = pk;
+
+ return iso;
}
+#if 0
+
static int xy2ijk(int x, int y, int *i, int *j, int *k) {
int pi, pj, pk;
return HL_cantor_xy(x,y);
}
-
static int ijk2xy(int i, int j, int k, int *x, int *y) {
int px, py;
return HL_cantor_xy(px,py);
}
-int HL_cantor_ijk(int i, int j, int k) {
- return ijk2xy(i,j,k,0,0);
+#endif
+
+struct HL_hex HL_ijk2xy(struct HL_isohex iso) {
+ int px, py;
+ struct HL_hex xy;
+ int i,j;
+
+ i = iso.i;
+ j = iso.j;
+
+ px = i;
+
+ /* py = -j - i/2; */
+ py = -j;
+
+ if (i < 0) {
+ py += (-i + 1)/2;
+ } else {
+ py -= i/2;
+ }
+
+ xy.x = px;
+ xy.y = py;
+
+ return xy;
}
-int HL_distance(int from, int to) {
+
+static void xy2ijkp(struct HL_hex h, int *ijk) {
+ struct HL_isohex iso;
+
+ iso = HL_xy2ijk(h);
+ ijk[0] = iso.i;
+ ijk[1] = iso.j;
+ ijk[2] = iso.k;
+}
+
+int HL_distance(struct HL_hex from, struct HL_hex to) {
int dist = 0, i;;
int fc[3], tc[3];
- HL_cantor_arrays(from, 0, fc);
- HL_cantor_arrays(to, 0, tc);
+ xy2ijkp(from, fc);
+ xy2ijkp(to, tc);
for (i=0;i<=2;i++) {
dist += abs(fc[i] - tc[i]);
return dist / 2;
}
-int HL_hexes_within_range(int hex, int range, int *list, int size) {
+int HL_hexes_within_range(struct HL_hex hex, int range, struct HL_hex *list, int size) {
int count = 0;
int i;
return count;
}
-int HL_hexes_at_range(int hex, int range, int *list, int size) {
+int HL_hexes_at_range(struct HL_hex hex, int range, struct HL_hex *list, int size) {
int q; /* p and q are count/loop vars */
- int c[3]; /* ijk coord array */
+ struct HL_isohex iso;
+
if (range == 0) {
if (list) {
/* i.e. if (!list || size < range * 6) */
if (!list || size < 1) return range * 6;
- HL_cantor_arrays(hex, 0, c);
- c[0] += range;
- c[2] = -c[0] - c[1];
- hex = HL_cantor_ijkp(c);
+ iso = HL_xy2ijk(hex);
+
+ iso.i += range;
+ iso.k = -iso.i - iso.j;
+
+ hex = HL_ijk2xy(iso);
for(q=0; q<size && q < range * 6; q++) {
list[q] = hex;
return range * 6;
}
-int HL_adjacent_hex(int start, int dir) {
- if (dir < 0 || dir > 5) return 0;
-
- return HL_adjhex(start, dir);
-}
-
/* direction 0 is positive X , counter clockwise from there */
-int HL_adjhex(int start, int dir) {
+struct HL_hex HL_adjhex(struct HL_hex hex, int dir) {
int c[3];
+ struct HL_isohex iso;
- HL_cantor_arrays(start, 0, c);
+ iso = HL_xy2ijk(hex);
+ c[0] = iso.i;
+ c[1] = iso.j;
+ c[2] = iso.k;
switch (dir%6) {
case 2:
c[0]--; ; c[2]++; break;
}
- return HL_cantor_ijkp(c);
-}
-
-int HL_cantor_xyp(int *xy) {
- return HL_cantor_xy(xy[0], xy[1]);
-}
-
-int HL_cantor_ijkp(int *ijk) {
- return HL_cantor_ijk(ijk[0], ijk[1], ijk[2]);
-}
-
-int HL_cantor_arrays(int can, int *xy, int *ijk) {
- return HL_cantor_decode(can, xy, xy ? xy+1 : 0,
- ijk, ijk ? ijk+1 : 0, ijk ? ijk+2 : 0);
-}
-
-int HL_cantor_decode(int can, int *x, int *y, int *i, int *j, int *k) {
- int px, py;
-
- inversecantor(can, &px, &py);
- if (x) *x = px;
- if (y) *y = py;
-
- xy2ijk(px, py, i, j, k);
-
- return can;
-}
-
-int HL_cantor_i(int cantor) {
- int i;
-
- HL_cantor_decode(cantor, 0,0, &i,0,0);
- return i;
-}
-
-int HL_cantor_j(int cantor) {
- int j;
+ iso.i = c[0];
+ iso.j = c[1];
+ iso.k = c[2];
- HL_cantor_decode(cantor, 0,0, 0,&j,0);
- return j;
+ return HL_ijk2xy(iso);
}
-int HL_cantor_k(int cantor) {
- int k;
-
- HL_cantor_decode(cantor, 0,0, 0,0,&k);
- return k;
-}
-
-int HL_cantor_x(int cantor) {
- int x;
- inversecantor(cantor, &x, 0);
- return x;
-}
-
-int HL_cantor_y(int cantor) {
- int y;
- inversecantor(cantor, 0, &y);
- return y;
+int HL_adjhexp(struct HL_hex hex, int dir, struct HL_hex *adj) {
+ if (adj) {
+ *adj = HL_adjhex(hex, dir);
+ }
+ return dir;
}
/* Determine if a map with these dimensions will overflow */
return low;
}
-static int inversenatcantor(int cantor, int *x, int *y) {
- int w, t, py;
-
- cantor -= 1;
-
- w = (int)floor((sqrt(8.0 * cantor + 1.0) - 1.0)/2.0);
- t = (w * w + w)/2;
-
- py = cantor - t;
-
- if (y) *y = py;
- if (x) *x = w - py;
-
- return cantor;
-}
-
-/*
- * map non negative integer pairs to their cantor pairing function
- * number, plus one. We add one so that the result is never zero,
- * leaving zero to be "invalid" or "none" or what have you.
- */
-
-static int natcantor(int x, int y) {
- return (x+y) * (x + y + 1) / 2 + y+1;
-}
-
-/* See http://en.wikipedia.org/wiki/Cantor_pairing_function */
-/* see also http://szudzik.com/ElegantPairing.pdf */
-/*
- * if either coordinate is negative, map the integers onto the
- * whole numbers, and then return the negative of the adjusted
- * cantor number. As for most grids negative coordinates will
- * be invalid, this will allow for a <= 0 test for invalid
- * or out of bounds (on the negative side anyway, you'll
- * still have to test for out of range on the positive side).
- *
- * TODO figure out what the maximum supported coordinates are
- * for given integer sizes.
- */
-int HL_cantor_xy(int x, int y) {
- if (x < 0 || y < 0) {
- x = abs(2 * x) - (x < 0);
- y = abs(2 * y) - (y < 0);
- return -natcantor(x, y);
- }
- return natcantor(x,y);
-}
-
-static int inversecantor(int cantor, int *x, int *y) {
- if (cantor < 0) {
- inversenatcantor(-cantor, x, y);
- if (x) {
- if (*x % 2) {
- *x = -(*x+1)/2;
- } else {
- *x = *x / 2;
- }
- }
- if (y) {
- if (*y % 2) {
- *y = -(*y+1)/2;
- } else {
- *y = *y/2;
- }
- }
- } else {
- inversenatcantor(cantor, x, y);
- }
-
- return cantor;
-}
-
struct hex {
int iso;
int x, y, z;
#define COS30 (.866025403784438646763723170752)
-int HL_hexbin(double width, double x, double y, int *i, int *j) {
+struct HL_hex HL_hexbin(double width, double x, double y) {
double z, rx, ry, rz;
double abs_dx, abs_dy, abs_dz;
int ix, iy, iz, s;
struct hex h;
+ struct HL_hex hex;
/*x = x / cos(30 * M_PI / 180.0); */ /* rotated X coord */
x = x / COS30;
h.iso = 1;
hex_xy(&h);
- if (i) *i = h.x;
- if (j) *j = h.y;
- return HL_cantor_xy(h.x, h.y);
+
+ hex.x = h.x;
+ hex.y = h.y;
+
+ return hex;
}
#ifndef HEXLIB_H_
#define HEXLIB_H_ 1
+struct HL_hex {
+ int x, y;
+};
+
+struct HL_isohex {
+ int i,j,k;
+};
+
+struct HL_point {
+ double x, y;
+};
+
+struct HL_hex HL_hex_xy(int x, int y);
+
+int HL_hex_cmp(struct HL_hex const *a, struct HL_hex const *b);
+
+int HL_hex_eq(struct HL_hex const *a, struct HL_hex const *b);
+
/*
* this library implements a generic library for hexgrids for games.
* it avoids display issues, other than some general provisions
* on a hex boundary, the result will depend on how the machine
* does the floating point math, but should be consistent.
*/
-int HL_hexbin(double width, double x, double y, int *i, int *j);
+struct HL_hex HL_hexbin(double width, double x, double y);
+struct HL_hex HL_bin(double x, double y);
/* generate the cantor mapping of a hexes x,y coordinate.
* See http://en.wikipedia.org/wiki/Cantor_pairing_function.
* is independent of the size of the grid.
*/
-/* Cartesian functions */
+/* Cantor encoding/decoding functions */
+int HL_cantor(struct HL_hex h);
int HL_cantor_xy(int x, int y);
-int HL_cantor_xyp(int *xy);
-int HL_cantor_x(int cantor);
-int HL_cantor_y(int cantor);
-
-/* Isometric functions */
-int HL_cantor_ijk(int i, int j, int k);
-int HL_cantor_ijkp(int *ijk);
-int HL_cantor_i(int cantor);
-int HL_cantor_k(int cantor);
-int HL_cantor_j(int cantor);
-
-int HL_cantor_bin(double x, double y);
-
-/* A general decode function */
-int HL_cantor_decode(int can, int *x, int *y, int *i, int *j, int *k);
-int HL_cantor_arrays(int can, int *xy, int *ijk);
+struct HL_hex HL_uncantor(int cantor);
/*
* layout assistance functions
*
* Similarly for HL_center_y() and HL_hexcenter()
*/
-double HL_center_x(int cantor);
-double HL_center_y(int cantor);
-int HL_hexcenter(int cantor, double *x, double *y);
+struct HL_point HL_hexcenter(struct HL_hex hex);
/* find a point based on a unit hex, given angle and distance from center,
* where a distance of 1 is on the hex side. probably not useful
* for points outside the hex, that is, distance should almost certainly
* be less than or equal to 1.0
*/
-double HL_polar(double angle, double distance, double *x, double *y);
+struct HL_point HL_polar(double angle, double distance, double *d);
/* how far is it on the hex grid */
-int HL_distance(int from_cantor, int to_cantor);
-
-/* returns 0 for <0 or >5 */
-int HL_adjacent_hex(int start, int dir);
+int HL_distance(struct HL_hex from, struct HL_hex to);
/* module direction */
-int HL_adjhex(int start, int dir);
-int HL_hexes_at_range(int hex, int range, int *list, int size);
-int HL_hexes_within_range(int hex, int range, int *list, int size);
+struct HL_hex HL_adjhex(struct HL_hex, int dir);
+int HL_adjhexp(struct HL_hex, int dir, struct HL_hex *adj);
+int HL_hexes_at_range(struct HL_hex, int range, struct HL_hex *list, int size);
+int HL_hexes_within_range(struct HL_hex, int range, struct HL_hex *list, int size);
typedef double (*HL_costfunc)(int start, int finish);
/*
* Plotting support
*/
-void HL_trianglefan(int cantor, double *);
-
-struct HL_hex {
- int x,y; /* hmm. would be nice to be able to just point to a generic */
-};
-
-struct hl_point {
- double x, y;
-};
+void HL_trianglefan(struct HL_hex hex, double *);
/*
* Pathfinding
*/
struct HL_astar_hex {
- int hex;
+ struct HL_hex hex;
double f, g, h;
struct HL_astar_hex *parent;
int open;
};
struct HL_astar {
- int start, goal;
+ struct HL_hex start, goal;
int pathlen;
- double (*metric)(int,int);
- double (*heuristic)(int,int);
- int (*neighbor)(int,int);
+ double (*metric)(struct HL_hex,struct HL_hex);
+ double (*heuristic)(struct HL_hex,struct HL_hex);
+ int (*neighbor)(struct HL_hex,int dir, struct HL_hex *n);
int nodes, allocated;
struct HL_astar_hex *open;
struct HL_astar_hex *closed;
void HL_astar_free(struct HL_astar *s);
struct HL_astar *HL_astar_clear(struct HL_astar *s);
struct HL_astar *HL_astar_init(struct HL_astar *s);
-void HL_vertices(int cantor, double *vc);
+void HL_vertices(struct HL_hex hex, double *vc);
extern double HL_vertexv[12];
extern double HL_fand[16];
#include "ctap.h"
int main(void) {
- int hex, start, dist;
+ struct HL_hex hex, start;
+
+ int dist;
int x, y, i;
plan(294);
for (x = -3 ; x <= 3; x++) {
for (y = -3 ; y <= 3; y++) {
- start = HL_cantor_xy(x, y);
+ start = HL_hex_xy(x, y);
for (i = 0; i < 6; i++) {
hex = HL_adjhex(start, i);
dist = HL_distance(hex, start);
- ok(dist == 1,
+ is_int((long)dist, 1L,
"%d, %d direction %d distance one",
x, y, i);
}
#include "ctap.h"
void pcheck(struct HL_astar *p, int x1, int y1, int x2, int y2, int expect) {
- int from, to, dist;
+ struct HL_hex from, to;
+ int dist;
- from = HL_cantor_xy(x1,y1);
- to = HL_cantor_xy(x2,y2);
+ from = HL_hex_xy(x1,y1);
+ to = HL_hex_xy(x2,y2);
// HL_astar_clear(p);
}
/* make hex 55 missing */
-int neighbor55(int hex, int dir) {
- int neighbor;
-
- do {
- neighbor = HL_adjacent_hex(hex, dir++);
- } while (neighbor == HL_cantor_xy(5,5));
-
- return neighbor;
+int neighbor55(struct HL_hex hex, int dir, struct HL_hex *n) {
+ int valid;
+ struct HL_hex nh;
+
+ valid = HL_adjhexp(hex, dir, &nh);
+ if (valid && nh.x == 5 && nh.y == 5) {
+ valid = 0;
+ }
+ if (valid && n) {
+ *n = nh;
+ }
+
+ return valid;
+
+ return 1;
}
int main(void) {
#include "ctap.h"
int main(void) {
- int xy[2];
- int ijk[3];
int hex;
+ struct HL_hex in;
+ struct HL_hex out;
int x, y;
- plan(81*4);
+ plan(81*2);
for (x=-4;x<=4;x++) {
for (y=-4;y<=4;y++) {
- hex = HL_cantor_xy(x,y);
- HL_cantor_arrays(hex, xy, ijk);
- ok(x == xy[0], "x check %d %d", x, y);
- ok(y == xy[1], "y check %d %d", x, y);
- ok(x == HL_cantor_x(hex), "reverse check x %d %d",hex,x);
- ok(x == HL_cantor_x(hex), "reverse check y %d %d",hex,y);
+ in.x = x;
+ in.y = y;
+ hex = HL_cantor(in);
+ out = HL_uncantor(hex);
+ is_int(x, out.x, "x check %d %d", x, y);
+ is_int(y, out.y, "y check %d %d", x, y);
}
}
#include "ctap.h"
+#define EPS 0.00000001
+
int main(void) {
- int start, x, y;
+ int x, y;
+ struct HL_hex start;
+ struct HL_point center;
+ double stride;
+
+ stride = 1.5 / sqrt(3.0);
plan(6);
x = 0; y = 0;
- start = HL_cantor_xy(x,y);
- ok(HL_center_x(start) == x * 1.5 / sqrt(3.0), "%d %d center X", x, y);
- ok(HL_center_y(start) == 0.0, "%d %d center Y", x, y);
+ start = HL_hex_xy(x,y);
+ center = HL_hexcenter(start);
+ is_double(center.x, x * stride, EPS, "%d %d center X", x, y);
+ is_double(center.y, 0.0, EPS, "%d %d center Y", x, y);
x = 1; y = 1;
- start = HL_cantor_xy(x,y);
- ok(HL_center_x(start) == x * 1.5 / sqrt(3.0), "%d %d center X", x, y);
- ok(HL_center_y(start) == 0.5, "%d %d center Y = %f", x, y,
- HL_center_y(start));
+ start = HL_hex_xy(x,y);
+ center = HL_hexcenter(start);
+ is_double(center.x, x * stride, EPS, "%d %d center X", x, y);
+ is_double(center.y, 0.5, EPS, "%d %d center Y", x, y);
+
x = 2; y = 2;
- start = HL_cantor_xy(x,y);
- ok(HL_center_x(start) == x * 1.5 / sqrt(3.0), "%d %d center X", x, y);
- ok(HL_center_y(start) == 2.0, "%d %d center Y = %f", x, y,
- HL_center_y(start));
+ start = HL_hex_xy(x,y);
+ center = HL_hexcenter(start);
+ is_double(center.x, x * stride, EPS, "%d %d center X", x, y);
+ is_double(center.y, 2.0, EPS, "%d %d center Y", x, y);
return 0;
}
#include "ctap.h"
void dcheck(int x1, int y1, int x2, int y2, int expect) {
- int from, to, dist;
- from = HL_cantor_xy(x1,y1);
- to = HL_cantor_xy(x2,y2);
+ int dist;
+ struct HL_hex from, to;
+
+ from.x = x1;
+ from.y = y1;
+ to.x = x2;
+ to.y = y2;
+
dist = HL_distance(from,to);
- ok(dist == expect,
- "distance from (%02d, %02d) to (%02d, %02d) = %d (expect %d)",
- x1, y1, x2, y2, dist, expect);
+ is_int(expect, dist,
+ "distance from (%02d, %02d) to (%02d, %02d)",
+ x1, y1, x2, y2);
}
int main(void) {
#include "ctap.h"
int main(void) {
- int x, y;
-
- plan(6);
-
- HL_hexbin(1.0, 0.444194, -4.639363, &x, &y);
- ok(x == 1 && y == 4, "hexbin 0.444194, 4.639363 = 1, 4, %d %d", x, y);
-
- HL_hexbin(1.0, 0.0, 0.0, &x, &y);
- ok(x == 0 && y == 0, "0.0 0.0 -> 0 0");
-
- HL_hexbin(0.1111111, 0.288675, 0.500000, &x, &y);
- ok(x == 3, "center bin X %d == 3", x);
- ok(y == -5, "center bin Y %d == -5", y);
-
- HL_hexbin(0.1111111, 0.866025, 0.500000, &x, &y);
- ok(x == 9, "vertex bin X %d == 9", x);
- ok(y == -5, "vertex bin Y %d == -5", y);
+ double px, py;
+ struct HL_hex h;
+
+ plan(8);
+
+ px = 0.444194; py = -4.639363;
+ h = HL_hexbin(1.0, px, py);
+ is_int(1, h.x, "hexbin x %f, %f", px, py);
+ is_int(4, h.y, "hexbin x %f, %f", px, py);
+
+ px = 0.0; py = 0.0;
+ h = HL_hexbin(1.0, px, py);
+ is_int(0, h.x, "hexbin x %f, %f", px, py);
+ is_int(0, h.y, "hexbin x %f, %f", px, py);
+
+ px = 0.288675; py = 0.5;
+ h = HL_hexbin(0.111111, px, py);
+ is_int(3, h.x, "center hexbin x %f, %f", px, py);
+ is_int(-5, h.y, "center hexbin x %f, %f", px, py);
+
+ px = 0.866025; py = 0.5;
+ h = HL_hexbin(0.1111111, px, py);
+ is_int(9, h.x, "vertex hexbin x %f, %f", px, py);
+ is_int(-5, h.y, "vertex hexbin y %f, %f", px, py);
return 0;
}
#include "ctap.h"
int icmp(const void *ap, const void *bp) {
- int a;
- int b;
+ struct HL_hex const *a;
+ struct HL_hex const *b;
- a = *((int *)(ap));
- b = *((int *)(bp));
+ a = ap;
+ b = bp;
- return a-b;
+ if (a->x < b->x) return -1;
+ if (a->x > b->x) return 1;
+ if (a->y < b->y) return -1;
+ if (a->y > b->y) return 1;
+
+ return 0;
}
-void sort(int *r, int ct) {
+void sort(struct HL_hex *r, int ct) {
qsort(r, ct, sizeof *r, icmp);
}
int main(void) {
- int start;
+ struct HL_hex start;
int i, count;
- int range[32];
- int testrange[32];
+ struct HL_hex range[32];
+ struct HL_hex testrange[32];
plan(7+4);
/* printf("range = 1 from 3,3\n"); */
- start = HL_cantor_xy(3,3);
+ start = HL_hex_xy(3,3);
count = HL_hexes_at_range(start, 1, range, 0);
ok(count == 6, "6 hexes at range 1");
count = HL_hexes_at_range(start, 1, range, count);
sort(range, 6);
- testrange[0] = HL_cantor_xy(3,2);
- testrange[1] = HL_cantor_xy(3,4);
- testrange[2] = HL_cantor_xy(4,3);
- testrange[3] = HL_cantor_xy(4,2);
- testrange[4] = HL_cantor_xy(2,3);
- testrange[5] = HL_cantor_xy(2,2);
+ testrange[0] = HL_hex_xy(3,2);
+ testrange[1] = HL_hex_xy(3,4);
+ testrange[2] = HL_hex_xy(4,3);
+ testrange[3] = HL_hex_xy(4,2);
+ testrange[4] = HL_hex_xy(2,3);
+ testrange[5] = HL_hex_xy(2,2);
sort(testrange, 6);
for (i = 0; i < count; i++) {
- ok(range[i] == testrange[i], "3, 3 range 1 hex %d", testrange[i]);
+ ok(HL_hex_eq(&range[i], &testrange[i]), "3, 3 range 1 hex %d", testrange[i]);
}
- start = HL_cantor_xy(0,3);
+ start = HL_hex_xy(0,3);
count = HL_hexes_at_range(start, 1, range, 0);
ok(count == 6, "6 hexes at range1 from 0,3");
- start = HL_cantor_xy(-1,5);
+ start = HL_hex_xy(-1,5);
count = HL_hexes_at_range(start, 1, range, 0);
ok(count == 6, "6 hexes at range1 from -1,5");
- start = HL_cantor_xy(-2,3);
+ start = HL_hex_xy(-2,3);
count = HL_hexes_at_range(start, 1, range, 0);
ok(count == 6, "6 hexes at range1 from -2,3");
- start = HL_cantor_xy(3,3);
+ start = HL_hex_xy(3,3);
count = HL_hexes_at_range(start, 2, range, 0);
ok(count == 12, "6 hexes at range1 from 3,3");
+++ /dev/null
-.Dd December 20, 2004
-.Os
-.Dt TAP 3
-.Sh NAME
-.Nm tap
-.Nd write tests that implement the Test Anything Protocol
-.Sh SYNOPSIS
-.In tap.h
-.Sh DESCRIPTION
-The
-.Nm
-library provides functions for writing test scripts that produce output
-consistent with the Test Anything Protocol. A test harness that parses
-this protocol can run these tests and produce useful reports indicating
-their success or failure.
-.Ss PRINTF STRINGS
-In the descriptions that follow, for any function that takes as the
-last two parameters
-.Dq Fa char * , Fa ...
-it can be assumed that the
-.Fa char *
-is a
-.Fn printf
--like format string, and the optional arguments are values to be placed
-in that string.
-.Ss TEST PLANS
-.Bl -tag -width indent
-.It Xo
-.Ft int
-.Fn plan_tests "unsigned int"
-.Xc
-.It Xo
-.Ft int
-.Fn plan_no_plan "void"
-.Xc
-.It Xo
-.Ft int
-.Fn plan_skip_all "char *" "..."
-.Xc
-.El
-.Pp
-You must first specify a test plan. This indicates how many tests you
-intend to run, and allows the test harness to notice if any tests were
-missed, or if the test program exited prematurely.
-.Pp
-To do this, use
-.Fn plan_tests ,
-which always returns 0. The function will cause your program to exit
-prematurely if you specify 0 tests.
-.Pp
-In some situations you may not know how many tests you will be running, or
-you are developing your test program, and do not want to update the
-.Fn plan_tests
-parameter every time you make a change. For those situations use
-.Fn plan_no_plan .
-It returns 0, and indicates to the test harness that an indeterminate number
-of tests will be run.
-.Pp
-Both
-.Fn plan_tests
-and
-.Fn plan_no_plan
-will cause your test program to exit prematurely with a diagnostic
-message if they are called more than once.
-.Pp
-If your test program detects at run time that some required functionality
-is missing (for example, it relies on a database connection which is not
-present, or a particular configuration option that has not been included
-in the running kernel) use
-.Fn plan_skip_all ,
-passing as parameters a string to display indicating the reason for skipping
-the tests.
-.Ss SIMPLE TESTS
-.Bl -tag -width indent
-.It Xo
-.Ft unsigned int
-.Fn ok "expression" "char *" "..."
-.Xc
-.It Xo
-.Ft unsigned int
-.Fn ok1 "expression"
-.Xc
-.It Xo
-.Ft unsigned int
-.Fn pass "char *" "..."
-.Xc
-.It Xo
-.Ft unsigned int
-.Fn fail "char *" "..."
-.Xc
-.El
-.Pp
-Tests are implemented as expressions checked by calls to the
-.Fn ok
-and
-.Fn ok1
-macros. In both cases
-.Fa expression
-should evaluate to true if the test succeeded.
-.Pp
-.Fn ok
-allows you to specify a name, or comment, describing the test which will
-be included in the output.
-.Fn ok1
-is for those times when the expression to be tested is self
-explanatory and does not need an associated comment. In those cases
-the test expression becomes the comment.
-.Pp
-These four calls are equivalent:
-.Bd -literal -offset indent
-int i = 5;
-
-ok(i == 5, "i equals 5"); /* Overly verbose */
-ok(i == 5, "i equals %d", i); /* Just to demonstrate printf-like
- behaviour of the test name */
-ok(i == 5, "i == 5"); /* Needless repetition */
-ok1(i == 5); /* Just right */
-.Ed
-.Pp
-It is good practice to ensure that the test name describes the meaning
-behind the test rather than what you are testing. Viz
-.Bd -literal -offset indent
-ok(db != NULL, "db is not NULL"); /* Not bad, but */
-ok(db != NULL, "Database conn. succeeded"); /* this is better */
-.Ed
-.Pp
-.Fn ok
-and
-.Fn ok1
-return 1 if the expression evaluated to true, and 0 if it evaluated to
-false. This lets you chain calls from
-.Fn ok
-to
-.Fn diag
-to only produce diagnostic output if the test failed. For example, this
-code will include diagnostic information about why the database connection
-failed, but only if the test failed.
-.Bd -literal -offset indent
-ok(db != NULL, "Database conn. succeeded") ||
- diag("Database error code: %d", dberrno);
-.Ed
-.Pp
-You also have
-.Fn pass
-and
-.Fn fail .
-From the Test::More documentation:
-.Bd -literal -offset indent
-Sometimes you just want to say that the tests have passed.
-Usually the case is you've got some complicated condition
-that is difficult to wedge into an ok(). In this case,
-you can simply use pass() (to declare the test ok) or fail
-(for not ok).
-
-Use these very, very, very sparingly.
-.Ed
-.Pp
-These are synonyms for ok(1, ...) and ok(0, ...).
-.Ss SKIPPING TESTS
-.Bl -tag -width indent
-.It Xo
-.Ft int
-.Fn skip "unsigned int" "char *" "..."
-.Xc
-.It Xo
-.Fn skip_start "expression" "unsigned int" "char *" "..."
-.Xc
-.It Xo
-.Sy skip_end
-.Xc
-.El
-.Pp
-Sets of tests can be skipped. Ordinarily you would do this because
-the test can't be run in this particular testing environment.
-.Pp
-For example, suppose some tests should be run as root. If the test is
-not being run as root then the tests should be skipped. In this
-implementation, skipped tests are flagged as being ok, with a special
-message indicating that they were skipped. It is your responsibility
-to ensure that the number of tests skipped (the first parameter to
-.Fn skip )
-is correct for the number of tests to skip.
-.Pp
-One way of implementing this is with a
-.Dq do { } while(0);
-loop, or an
-.Dq if( ) { } else { }
-construct, to ensure that there are no additional side effects from the
-skipped tests.
-.Bd -literal -offset indent
-if(getuid() != 0) {
- skip(1, "because test only works as root");
-} else {
- ok(do_something_as_root() == 0, "Did something as root");
-}
-.Ed
-.Pp
-Two macros are provided to assist with this. The previous example could
-be re-written as follows.
-.Bd -literal -offset indent
-skip_start(getuid() != 0, 1, "because test only works as root");
-
-ok(do_something_as_root() == 0, "Did something as root");
-
-skip_end; /* It's a macro, no parentheses */
-.Ed
-.Ss MARKING TESTS AS Dq TODO
-.Bl -tag -width indent
-.It Xo
-.Ft void
-.Fn todo_start "char *" "..."
-.Xc
-.It Xo
-.Ft void
-.Fn todo_end "void"
-.Xc
-.El
-.Pp
-Sets of tests can be flagged as being
-.Dq TODO .
-These are tests that you expect to fail, probably because you haven't
-fixed a bug, or finished a new feature yet. These tests will still be
-run, but with additional output that indicates that they are expected
-to fail. Should a test start to succeed unexpectedly, tools like
-.Xr prove 1
-will indicate this, and you can move the test out of the todo
-block. This is much more useful than simply commenting out (or
-.Dq #ifdef 0 ... #endif )
-the tests.
-.Bd -literal -offset indent
-todo_start("dwim() not returning true yet");
-
-ok(dwim(), "Did what the user wanted");
-
-todo_end();
-.Ed
-.Pp
-Should
-.Fn dwim
-ever start succeeding you will know about it as soon as you run the
-tests. Note that
-.Em unlike
-the
-.Fn skip_*
-family, additional code between
-.Fn todo_start
-and
-.Fn todo_end
-.Em is
-executed.
-.Ss SKIP vs. TODO
-From the Test::More documentation;
-.Bd -literal -offset indent
-If it's something the user might not be able to do, use SKIP.
-This includes optional modules that aren't installed, running
-under an OS that doesn't have some feature (like fork() or
-symlinks), or maybe you need an Internet connection and one
-isn't available.
-
-If it's something the programmer hasn't done yet, use TODO.
-This is for any code you haven't written yet, or bugs you have
-yet to fix, but want to put tests in your testing script
-(always a good idea).
-.Ed
-.Ss DIAGNOSTIC OUTPUT
-.Bl -tag -width indent
-.It Xo
-.Fr unsigned int
-.Fn diag "char *" "..."
-.Xc
-.El
-.Pp
-If your tests need to produce diagnostic output, use
-.Fn diag .
-It ensures that the output will not be considered by the TAP test harness.
-.Fn diag
-adds the necessary trailing
-.Dq \en
-for you.
-.Bd -literal -offset indent
-diag("Expected return code 0, got return code %d", rcode);
-.Ed
-.Pp
-.Fn diag
-always returns 0.
-.Ss EXIT STATUS
-.Bl -tag -width indent
-.It Xo
-.Fr int
-.Fn exit_status void
-.Xc
-.El
-.Pp
-For maximum compatability your test program should return a particular
-exit code. This is calculated by
-.Fn exit_status
-so it is sufficient to always return from
-.Fn main
-with either
-.Dq return exit_status();
-or
-.Dq exit(exit_status());
-as appropriate.
-.Sh EXAMPLES
-The
-.Pa tests
-directory in the source distribution contains numerous tests of
-.Nm
-functionality, written using
-.Nm .
-Examine them for examples of how to construct test suites.
-.Sh COMPATABILITY
-.Nm
-strives to be compatible with the Perl Test::More and Test::Harness
-modules. The test suite verifies that
-.Nm
-is bug-for-bug compatible with their behaviour. This is why some
-functions which would more naturally return nothing return constant
-values.
-.Pp
-If the
-.Lb libpthread
-is found at compile time,
-.Nm
-.Em should
-be thread safe. Indications to the contrary (and test cases that expose
-incorrect behaviour) are very welcome.
-.Sh SEE ALSO
-.Xr Test::More 1 ,
-.Xr Test::Harness 1 ,
-.Xr prove 1
-.Sh STANDARDS
-.Nm
-requires a
-.St -isoC-99
-compiler. Some of the
-.Nm
-functionality is implemented as variadic macros, and that functionality
-was not formally codified until C99. Patches to use
-.Nm
-with earlier compilers that have their own implementation of variadic
-macros will be gratefully received.
-.Sh HISTORY
-.Nm
-was written to help improve the quality and coverage of the FreeBSD
-regression test suite, and released in the hope that others find it
-a useful tool to help improve the quality of their code.
-.Sh AUTHORS
-.An "Nik Clayton" Aq nik@ngo.org.uk ,
-.Aq nik@FreeBSD.org
-.Pp
-.Nm
-would not exist without the efforts of
-.An "Michael G Schwern" Aq schqern@pobox.com ,
-.An "Andy Lester" Aq andy@petdance.com ,
-and the countless others who have worked on the Perl QA programme.
-.Sh BUGS
-Ideally, running the tests would have no side effects on the behaviour
-of the application you are testing. However, it is not always possible
-to avoid them. The following side effects of using
-.Nm
-are known.
-.Bl -bullet -offset indent
-.It
-stdout is set to unbuffered mode after calling any of the
-.Fn plan_*
-functions.
-.El
+++ /dev/null
-/*-
- * Copyright (c) 2004 Nik Clayton
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#define _GNU_SOURCE
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "tap.h"
-
-static int no_plan = 0;
-static int skip_all = 0;
-static int have_plan = 0;
-static unsigned int test_count = 0; /* Number of tests that have been run */
-static unsigned int e_tests = 0; /* Expected number of tests to run */
-static unsigned int failures = 0; /* Number of tests that failed */
-static char *todo_msg = NULL;
-static char *todo_msg_fixed = "libtap malloc issue";
-static int todo = 0;
-static int test_died = 0;
-
-/* Encapsulate the pthread code in a conditional. In the absence of
- libpthread the code does nothing */
-#ifdef HAVE_LIBPTHREAD
-#include <pthread.h>
-static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
-# define LOCK pthread_mutex_lock(&M);
-# define UNLOCK pthread_mutex_unlock(&M);
-#else
-# define LOCK
-# define UNLOCK
-#endif
-
-static void _expected_tests(unsigned int);
-static void _tap_init(void);
-static void _cleanup(void);
-
-/*
- * Generate a test result.
- *
- * ok -- boolean, indicates whether or not the test passed.
- * test_name -- the name of the test, may be NULL
- * test_comment -- a comment to print afterwards, may be NULL
- */
-unsigned int
-_gen_result(int ok, const char *func, char *file, unsigned int line,
- char *test_name, ...)
-{
- va_list ap;
- char *local_test_name = NULL;
- char *c;
- int name_is_digits;
-
- LOCK;
-
- test_count++;
-
- /* Start by taking the test name and performing any printf()
- expansions on it */
- if(test_name != NULL) {
- va_start(ap, test_name);
- vasprintf(&local_test_name, test_name, ap);
- va_end(ap);
-
- /* Make sure the test name contains more than digits
- and spaces. Emit an error message and exit if it
- does */
- if(local_test_name) {
- name_is_digits = 1;
- for(c = local_test_name; *c != '\0'; c++) {
- if(!isdigit(*c) && !isspace(*c)) {
- name_is_digits = 0;
- break;
- }
- }
-
- if(name_is_digits) {
- diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name);
- diag(" Very confusing.");
- }
- }
- }
-
- if(!ok) {
- printf("not ");
- failures++;
- }
-
- printf("ok %d", test_count);
-
- if(test_name != NULL) {
- printf(" - ");
-
- /* Print the test name, escaping any '#' characters it
- might contain */
- if(local_test_name != NULL) {
- flockfile(stdout);
- for(c = local_test_name; *c != '\0'; c++) {
- if(*c == '#')
- fputc('\\', stdout);
- fputc((int)*c, stdout);
- }
- funlockfile(stdout);
- } else { /* vasprintf() failed, use a fixed message */
- printf("%s", todo_msg_fixed);
- }
- }
-
- /* If we're in a todo_start() block then flag the test as being
- TODO. todo_msg should contain the message to print at this
- point. If it's NULL then asprintf() failed, and we should
- use the fixed message.
-
- This is not counted as a failure, so decrement the counter if
- the test failed. */
- if(todo) {
- printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
- if(!ok)
- failures--;
- }
-
- printf("\n");
-
- if(!ok)
- diag(" Failed %stest (%s:%s() at line %d)",
- todo ? "(TODO) " : "", file, func, line);
-
- free(local_test_name);
-
- UNLOCK;
-
- /* We only care (when testing) that ok is positive, but here we
- specifically only want to return 1 or 0 */
- return ok ? 1 : 0;
-}
-
-/*
- * Initialise the TAP library. Will only do so once, however many times it's
- * called.
- */
-void
-_tap_init(void)
-{
- static int run_once = 0;
-
- LOCK;
-
- if(!run_once) {
- atexit(_cleanup);
-
- /* stdout needs to be unbuffered so that the output appears
- in the same place relative to stderr output as it does
- with Test::Harness */
- setbuf(stdout, 0);
- run_once = 1;
- }
-
- UNLOCK;
-}
-
-/*
- * Note that there's no plan.
- */
-int
-plan_no_plan(void)
-{
-
- LOCK;
-
- _tap_init();
-
- if(have_plan != 0) {
- fprintf(stderr, "You tried to plan twice!\n");
- test_died = 1;
- UNLOCK;
- exit(255);
- }
-
- have_plan = 1;
- no_plan = 1;
-
- UNLOCK;
-
- return 0;
-}
-
-/*
- * Note that the plan is to skip all tests
- */
-int
-plan_skip_all(char *reason)
-{
-
- LOCK;
-
- _tap_init();
-
- skip_all = 1;
-
- printf("1..0");
-
- if(reason != NULL)
- printf(" # Skip %s", reason);
-
- printf("\n");
-
- UNLOCK;
-
- exit(0);
-}
-
-/*
- * Note the number of tests that will be run.
- */
-int
-plan_tests(unsigned int tests)
-{
-
- LOCK;
-
- _tap_init();
-
- if(have_plan != 0) {
- fprintf(stderr, "You tried to plan twice!\n");
- test_died = 1;
- UNLOCK;
- exit(255);
- }
-
- if(tests == 0) {
- fprintf(stderr, "You said to run 0 tests! You've got to run something.\n");
- test_died = 1;
- UNLOCK;
- exit(255);
- }
-
- have_plan = 1;
-
- _expected_tests(tests);
-
- UNLOCK;
-
- return 0;
-}
-
-unsigned int
-diag(char *fmt, ...)
-{
- va_list ap;
-
- LOCK;
-
- fputs("# ", stderr);
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-
- fputs("\n", stderr);
-
- UNLOCK;
-
- return 0;
-}
-
-void
-_expected_tests(unsigned int tests)
-{
-
- LOCK;
-
- printf("1..%d\n", tests);
- e_tests = tests;
-
- UNLOCK;
-}
-
-int
-skip(unsigned int n, char *fmt, ...)
-{
- va_list ap;
- char *skip_msg;
-
- LOCK;
-
- va_start(ap, fmt);
- asprintf(&skip_msg, fmt, ap);
- va_end(ap);
-
- while(n-- > 0) {
- test_count++;
- printf("ok %d # skip %s\n", test_count,
- skip_msg != NULL ?
- skip_msg : "libtap():malloc() failed");
- }
-
- free(skip_msg);
-
- UNLOCK;
-
- return 1;
-}
-
-void
-todo_start(char *fmt, ...)
-{
- va_list ap;
-
- LOCK;
-
- va_start(ap, fmt);
- vasprintf(&todo_msg, fmt, ap);
- va_end(ap);
-
- todo = 1;
-
- UNLOCK;
-}
-
-void
-todo_end(void)
-{
-
- LOCK;
-
- todo = 0;
- free(todo_msg);
-
- UNLOCK;
-}
-
-int
-exit_status(void)
-{
- int r;
-
- LOCK;
-
- /* If there's no plan, just return the number of failures */
- if(no_plan || !have_plan) {
- UNLOCK;
- return failures;
- }
-
- /* Ran too many tests? Return the number of tests that were run
- that shouldn't have been */
- if(e_tests < test_count) {
- r = test_count - e_tests;
- UNLOCK;
- return r;
- }
-
- /* Return the number of tests that failed + the number of tests
- that weren't run */
- r = failures + e_tests - test_count;
- UNLOCK;
-
- return r;
-}
-
-/*
- * Cleanup at the end of the run, produce any final output that might be
- * required.
- */
-void
-_cleanup(void)
-{
-
- LOCK;
-
- /* If plan_no_plan() wasn't called, and we don't have a plan,
- and we're not skipping everything, then something happened
- before we could produce any output */
- if(!no_plan && !have_plan && !skip_all) {
- diag("Looks like your test died before it could output anything.");
- UNLOCK;
- return;
- }
-
- if(test_died) {
- diag("Looks like your test died just after %d.", test_count);
- UNLOCK;
- return;
- }
-
-
- /* No plan provided, but now we know how many tests were run, and can
- print the header at the end */
- if(!skip_all && (no_plan || !have_plan)) {
- printf("1..%d\n", test_count);
- }
-
- if((have_plan && !no_plan) && e_tests < test_count) {
- diag("Looks like you planned %d tests but ran %d extra.",
- e_tests, test_count - e_tests);
- UNLOCK;
- return;
- }
-
- if((have_plan || !no_plan) && e_tests > test_count) {
- diag("Looks like you planned %d tests but only ran %d.",
- e_tests, test_count);
- UNLOCK;
- return;
- }
-
- if(failures)
- diag("Looks like you failed %d tests of %d.",
- failures, test_count);
-
- UNLOCK;
-}
+++ /dev/null
-/*-
- * Copyright (c) 2004 Nik Clayton
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/* '## __VA_ARGS__' is a gcc'ism. C99 doesn't allow the token pasting
- and requires the caller to add the final comma if they've ommitted
- the optional arguments */
-#ifdef __GNUC__
-# define ok(e, test, ...) ((e) ? \
- _gen_result(1, __func__, __FILE__, __LINE__, \
- test, ## __VA_ARGS__) : \
- _gen_result(0, __func__, __FILE__, __LINE__, \
- test, ## __VA_ARGS__))
-
-# define ok1(e) ((e) ? \
- _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
- _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
-
-# define pass(test, ...) ok(1, test, ## __VA_ARGS__);
-# define fail(test, ...) ok(0, test, ## __VA_ARGS__);
-
-# define skip_start(test, n, fmt, ...) \
- do { \
- if((test)) { \
- skip(n, fmt, ## __VA_ARGS__); \
- continue; \
- }
-#elif __STDC_VERSION__ >= 199901L /* __GNUC__ */
-# define ok(e, ...) ((e) ? \
- _gen_result(1, __func__, __FILE__, __LINE__, \
- __VA_ARGS__) : \
- _gen_result(0, __func__, __FILE__, __LINE__, \
- __VA_ARGS__))
-
-# define ok1(e) ((e) ? \
- _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
- _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
-
-# define pass(...) ok(1, __VA_ARGS__);
-# define fail(...) ok(0, __VA_ARGS__);
-
-# define skip_start(test, n, ...) \
- do { \
- if((test)) { \
- skip(n, __VA_ARGS__); \
- continue; \
- }
-#else /* __STDC_VERSION__ */
-# error "Needs gcc or C99 compiler for variadic macros."
-#endif /* __STDC_VERSION__ */
-
-# define skip_end } while(0);
-
-unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...);
-
-int plan_no_plan(void);
-int plan_skip_all(char *);
-int plan_tests(unsigned int);
-
-unsigned int diag(char *, ...);
-
-int skip(unsigned int, char *, ...);
-
-void todo_start(char *, ...);
-void todo_end(void);
-
-int exit_status(void);