From: Nathan Wagner Date: Sun, 17 Jun 2018 17:41:06 +0000 (+0000) Subject: rework library to use structs X-Git-Url: https://pd.if.org/git/?p=hexagon;a=commitdiff_plain;h=06868ef42497f1cbc480831029f5f69b395dfdd2 rework library to use structs --- diff --git a/Makefile b/Makefile index 3856ab7..05a253e 100644 --- a/Makefile +++ b/Makefile @@ -2,27 +2,45 @@ # 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 $@ @@ -32,10 +50,23 @@ install: libhexagon.a hexagon.h doc/hexagon.3 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 + diff --git a/astar.c b/astar.c index fd92bfc..67889c2 100644 --- a/astar.c +++ b/astar.c @@ -17,11 +17,11 @@ #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); } @@ -42,7 +42,7 @@ struct HL_astar *HL_astar_init(struct HL_astar *state) { state->metric = default_metric; state->heuristic = default_heuristic; - state->neighbor = HL_adjacent_hex; + state->neighbor = HL_adjhexp; return state; } @@ -91,10 +91,10 @@ void HL_astar_free(struct HL_astar *s) { } } -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) { @@ -111,7 +111,7 @@ static struct HL_astar_hex *add_open(struct HL_astar *s, int hex) { /* 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; @@ -138,7 +138,7 @@ static struct HL_astar_hex *closehex(struct HL_astar *s, struct HL_astar_hex *h) } #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; @@ -152,22 +152,22 @@ static struct HL_astar_hex *closehex(struct HL_astar *s, struct HL_astar_hex *h) 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; } @@ -191,7 +191,7 @@ static struct HL_astar_hex *lowrank(struct HL_astar *s) { 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"); } @@ -201,14 +201,15 @@ void dump_list(char *name, struct HL_astar_hex *x) { * 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; @@ -217,9 +218,9 @@ int HL_findpath(struct HL_astar *s, int loops) { #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 @@ -233,23 +234,24 @@ int HL_findpath(struct HL_astar *s, int loops) { 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); @@ -280,7 +282,7 @@ int HL_findpath(struct HL_astar *s, int loops) { 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 @@ -290,7 +292,7 @@ int HL_findpath(struct HL_astar *s, int loops) { } /* error no path ? */ - if (x->hex != s->goal) { + if (HL_distance(x->hex, s->goal) != 0) { s->error = 1; return 0; } @@ -306,12 +308,12 @@ int HL_findpath(struct HL_astar *s, int loops) { 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++; diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index c00373a..0000000 --- a/doc/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -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 diff --git a/hexagon.c b/hexagon.c index 2dd1f3a..155a5d2 100644 --- a/hexagon.c +++ b/hexagon.c @@ -5,7 +5,27 @@ #include #include -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 @@ -101,53 +121,57 @@ static double hexpthd[6][2] = { #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; @@ -155,36 +179,34 @@ double HL_polar(double angle, double distance, double *x, double *y) { 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; @@ -194,36 +216,31 @@ void HL_vertices(int cantor, double *vc) { *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; } /* @@ -231,10 +248,37 @@ int HL_hexcenter(int cantor, double *xc, double *yc) { * 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; @@ -253,7 +297,6 @@ static int xy2ijk(int x, int y, int *i, int *j, int *k) { return HL_cantor_xy(x,y); } - static int ijk2xy(int i, int j, int k, int *x, int *y) { int px, py; @@ -274,16 +317,49 @@ static int ijk2xy(int i, int j, int k, int *x, int *y) { 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]); @@ -292,7 +368,7 @@ int HL_distance(int from, int to) { 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; @@ -306,9 +382,10 @@ int HL_hexes_within_range(int hex, int range, int *list, int size) { 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) { @@ -323,10 +400,12 @@ int HL_hexes_at_range(int hex, int range, int *list, int size) { /* 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 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: @@ -363,65 +440,18 @@ int HL_adjhex(int start, int dir) { 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 */ @@ -454,78 +484,6 @@ int HL_map_max_dimension(void) { 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; @@ -566,11 +524,12 @@ static int hex_iso(struct hex *h) { #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; @@ -607,7 +566,9 @@ int HL_hexbin(double width, double x, double y, int *i, int *j) { 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; } diff --git a/hexagon.h b/hexagon.h index 9578bdd..a2d4f2f 100644 --- a/hexagon.h +++ b/hexagon.h @@ -1,6 +1,24 @@ #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 @@ -25,7 +43,8 @@ * 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. @@ -44,24 +63,10 @@ int HL_hexbin(double width, double x, double y, int *i, int *j); * 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 @@ -72,27 +77,23 @@ int HL_cantor_arrays(int can, int *xy, int *ijk); * * 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); @@ -110,22 +111,14 @@ int HL_map_max_dimension(void); /* * 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; @@ -134,11 +127,11 @@ struct HL_astar_hex { }; 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; @@ -151,7 +144,7 @@ int HL_findpath(struct HL_astar *s, int loops); 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]; diff --git a/t/adjacency.c b/t/adjacency.c index 4112288..2f9d26e 100644 --- a/t/adjacency.c +++ b/t/adjacency.c @@ -3,18 +3,20 @@ #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); } diff --git a/t/astar.c b/t/astar.c index 729a7a8..5a447da 100644 --- a/t/astar.c +++ b/t/astar.c @@ -5,10 +5,11 @@ #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); @@ -24,14 +25,21 @@ void pcheck(struct HL_astar *p, int x1, int y1, int x2, int y2, int expect) { } /* 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) { diff --git a/t/cantor.c b/t/cantor.c index 1e7dbf6..7362714 100644 --- a/t/cantor.c +++ b/t/cantor.c @@ -5,21 +5,21 @@ #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); } } diff --git a/t/center.c b/t/center.c index 1c039c9..eecb80c 100644 --- a/t/center.c +++ b/t/center.c @@ -4,27 +4,36 @@ #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; } diff --git a/t/distance.c b/t/distance.c index 0fa88f0..55bfd5c 100644 --- a/t/distance.c +++ b/t/distance.c @@ -3,13 +3,18 @@ #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) { diff --git a/t/hexbin.c b/t/hexbin.c index 293d0f7..7afe270 100644 --- a/t/hexbin.c +++ b/t/hexbin.c @@ -3,23 +3,30 @@ #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; } diff --git a/t/range.c b/t/range.c index 783a089..7602845 100644 --- a/t/range.c +++ b/t/range.c @@ -9,57 +9,62 @@ #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"); diff --git a/t/tap.3 b/t/tap.3 deleted file mode 100644 index 4b23c24..0000000 --- a/t/tap.3 +++ /dev/null @@ -1,368 +0,0 @@ -.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 diff --git a/t/tap.c b/t/tap.c deleted file mode 100644 index 475c55e..0000000 --- a/t/tap.c +++ /dev/null @@ -1,436 +0,0 @@ -/*- - * 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 -#include -#include -#include - -#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 -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; -} diff --git a/t/tap.h b/t/tap.h deleted file mode 100644 index e64f9d5..0000000 --- a/t/tap.h +++ /dev/null @@ -1,89 +0,0 @@ -/*- - * 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);