commit 7dcddcf0d1223ff7b232dcbba1565442ece06514
parent d62aac93757f84cc1ccd2cf9d30847b66685d647
Author: William Casarin <jb55@jb55.com>
Date: Thu, 26 Oct 2017 11:34:59 -0700
test: tests working
Diffstat:
10 files changed, 603 insertions(+), 14 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -5,3 +5,4 @@
/btcs
/TAGS
*.d
+/run_tests
diff --git a/Makefile b/Makefile
@@ -1,22 +1,32 @@
-CFLAGS=-O0 -g -Wall -Wno-unused-variable
+CFLAGS=-O0 -Ideps -std=c99 -g -Wall -Wno-unused-variable -Wno-unused-function
-OBJS=script.o \
- parser.tab.o \
- lex.yy.o \
- oplookup.o \
- op.o \
- main.o \
- stack.o
+DEPS=script.c \
+ oplookup.c \
+ op.c \
+ stack.c
+
+CLIDEPS=parser.tab.c \
+ lex.yy.c \
+ main.c
+
+TESTDEPS=test.c
+
+# DEPS=oplookup.h script.h misc.h Makefile op.h stack.h
+TESTDEPS+=$(wildcard deps/*/*.c)
+OBJS=$(DEPS:.c=.o)
+CLIOBJS=$(CLIDEPS:.c=.o)
+TESTOBJS=$(TESTDEPS:.c=.o)
GEN=parser.tab.c \
parser.tab.h \
lex.yy.c \
oplookup.c \
oplookup.h \
- $(OBJS)
+ $(OBJS) \
+ $(CLIOBJS) \
+ $(TESTOBJS) \
-DEPS=oplookup.h script.h misc.h Makefile op.h stack.h
PREFIX ?= /usr/local
BIN=btcs
@@ -24,9 +34,12 @@ BIN=btcs
all: $(BIN)
include $(OBJS:.o=.d)
+include $(CLIOBJS:.o=.d)
+include $(TESTOBJS:.o=.d)
op.c: oplookup.h oplookup.c
+
%.d: %.c
@rm -f $@; \
$(CC) -MM $(CFLAGS) $< > $@
@@ -44,8 +57,14 @@ install: $(BIN)
mkdir -p $(PREFIX)/bin
cp $(BIN) $(PREFIX)/bin
-$(BIN): $(GEN) $(DEPS) $(OBJS)
- $(CC) $(CFLAGS) -o $@ $(OBJS)
+$(BIN): $(OBJS) $(CLIOBJS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(CLIOBJS)
+
+run_tests: $(OBJS) $(TESTOBJS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(TESTOBJS)
+
+test: run_tests
+ @./run_tests
clean:
rm -f $(GEN)
@@ -54,4 +73,4 @@ clean:
TAGS:
etags -o - *.c > $@
-.PHONY: TAGS
+.PHONY: TAGS test
diff --git a/README.md b/README.md
@@ -1,6 +1,8 @@
# btcs
+[![Build Status](https://travis-ci.org/jb55/btcs.svg)](https://travis-ci.org/jb55/btcs)
+
bitcoin script parser/evaluator/compiler/decompiler
## Motivation
diff --git a/deps/tap.c/package.json b/deps/tap.c/package.json
@@ -0,0 +1,23 @@
+{
+ "name" : "tap.c",
+ "version" : "0.0.1",
+ "description" : "Write tap tests in C.",
+ "src": [ "tap.c", "tap.h" ],
+ "repository" : {
+ "type" : "git",
+ "url" : "git://github.com/thlorenz/tap.c.git"
+ },
+ "homepage" : "https://github.com/thlorenz/tap.c",
+ "dependencies" : {
+ },
+ "keywords": ["tap", "test", "check", "tap.c" ] ,
+ "author" : {
+ "name" : "Thorsten Lorenz",
+ "email" : "thlorenz@gmx.de",
+ "url" : "http://thlorenz.com"
+ },
+ "license" : {
+ "type": "GPLv2",
+ "url": "https://github.com/thlorenz/tap.c/blob/master/COPYING"
+ }
+}
diff --git a/deps/tap.c/tap.c b/deps/tap.c/tap.c
@@ -0,0 +1,331 @@
+/*
+libtap - Write tests in C
+Copyright 2012 Jake Gelbman <gelbman@gmail.com>
+This file is licensed under the GPLv2
+*/
+
+#define _BSD_SOURCE 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include "tap.h"
+
+static int expected_tests = NO_PLAN;
+static int failed_tests;
+static int current_test;
+static char *todo_mesg;
+
+static char *
+vstrdupf (const char *fmt, va_list args) {
+ char *str;
+ int size;
+ va_list args2;
+ va_copy(args2, args);
+ if (!fmt)
+ fmt = "";
+ size = vsnprintf(NULL, 0, fmt, args2) + 2;
+ str = malloc(size);
+ if (!str) {
+ perror("malloc error");
+ exit(1);
+ }
+ vsprintf(str, fmt, args);
+ va_end(args2);
+ return str;
+}
+
+void
+tap_plan (int tests, const char *fmt, ...) {
+ expected_tests = tests;
+ if (tests == SKIP_ALL) {
+ char *why;
+ va_list args;
+ va_start(args, fmt);
+ why = vstrdupf(fmt, args);
+ va_end(args);
+ printf("1..0 ");
+ note("SKIP %s\n", why);
+ exit(0);
+ }
+ if (tests != NO_PLAN) {
+ printf("1..%d\n", tests);
+ }
+}
+
+int
+vok_at_loc (const char *file, int line, int test, const char *fmt,
+ va_list args)
+{
+ char *name = vstrdupf(fmt, args);
+ printf("%sok %d", test ? "" : "not ", ++current_test);
+ if (*name)
+ printf(" - %s", name);
+ if (todo_mesg) {
+ printf(" # TODO");
+ if (*todo_mesg)
+ printf(" %s", todo_mesg);
+ }
+ printf("\n");
+ if (!test) {
+ if (*name)
+ diag(" Failed%s test '%s'\n at %s line %d.",
+ todo_mesg ? " (TODO)" : "", name, file, line);
+ else
+ diag(" Failed%s test at %s line %d.",
+ todo_mesg ? " (TODO)" : "", file, line);
+ if (!todo_mesg)
+ failed_tests++;
+ }
+ free(name);
+ return test;
+}
+
+int
+ok_at_loc (const char *file, int line, int test, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ return test;
+}
+
+static int
+mystrcmp (const char *a, const char *b) {
+ return a == b ? 0 : !a ? -1 : !b ? 1 : strcmp(a, b);
+}
+
+#define eq(a, b) (!mystrcmp(a, b))
+#define ne(a, b) (mystrcmp(a, b))
+
+int
+is_at_loc (const char *file, int line, const char *got, const char *expected,
+ const char *fmt, ...)
+{
+ int test = eq(got, expected);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" got: '%s'", got);
+ diag(" expected: '%s'", expected);
+ }
+ return test;
+}
+
+int
+isnt_at_loc (const char *file, int line, const char *got, const char *expected,
+ const char *fmt, ...)
+{
+ int test = ne(got, expected);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" got: '%s'", got);
+ diag(" expected: anything else");
+ }
+ return test;
+}
+
+int
+cmp_ok_at_loc (const char *file, int line, int a, const char *op, int b,
+ const char *fmt, ...)
+{
+ int test = eq(op, "||") ? a || b
+ : eq(op, "&&") ? a && b
+ : eq(op, "|") ? a | b
+ : eq(op, "^") ? a ^ b
+ : eq(op, "&") ? a & b
+ : eq(op, "==") ? a == b
+ : eq(op, "!=") ? a != b
+ : eq(op, "<") ? a < b
+ : eq(op, ">") ? a > b
+ : eq(op, "<=") ? a <= b
+ : eq(op, ">=") ? a >= b
+ : eq(op, "<<") ? a << b
+ : eq(op, ">>") ? a >> b
+ : eq(op, "+") ? a + b
+ : eq(op, "-") ? a - b
+ : eq(op, "*") ? a * b
+ : eq(op, "/") ? a / b
+ : eq(op, "%") ? a % b
+ : diag("unrecognized operator '%s'", op);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" %d", a);
+ diag(" %s", op);
+ diag(" %d", b);
+ }
+ return test;
+}
+
+static void
+vdiag_to_fh (FILE *fh, const char *fmt, va_list args) {
+ char *mesg, *line;
+ int i;
+ if (!fmt)
+ return;
+ mesg = vstrdupf(fmt, args);
+ line = mesg;
+ for (i = 0; *line; i++) {
+ char c = mesg[i];
+ if (!c || c == '\n') {
+ mesg[i] = '\0';
+ fprintf(fh, "# %s\n", line);
+ if (!c)
+ break;
+ mesg[i] = c;
+ line = mesg + i + 1;
+ }
+ }
+ free(mesg);
+ return;
+}
+
+int
+diag (const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vdiag_to_fh(stderr, fmt, args);
+ va_end(args);
+ return 0;
+}
+
+int
+note (const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vdiag_to_fh(stdout, fmt, args);
+ va_end(args);
+ return 0;
+}
+
+int
+exit_status () {
+ int retval = 0;
+ if (expected_tests == NO_PLAN) {
+ printf("1..%d\n", current_test);
+ }
+ else if (current_test != expected_tests) {
+ diag("Looks like you planned %d test%s but ran %d.",
+ expected_tests, expected_tests > 1 ? "s" : "", current_test);
+ retval = 255;
+ }
+ if (failed_tests) {
+ diag("Looks like you failed %d test%s of %d run.",
+ failed_tests, failed_tests > 1 ? "s" : "", current_test);
+ if (expected_tests == NO_PLAN)
+ retval = failed_tests;
+ else
+ retval = expected_tests - current_test + failed_tests;
+ }
+ return retval;
+}
+
+int
+bail_out (int ignore, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ printf("Bail out! ");
+ vprintf(fmt, args);
+ printf("\n");
+ va_end(args);
+ exit(255);
+ return 0;
+}
+
+void
+tap_skip (int n, const char *fmt, ...) {
+ char *why;
+ va_list args;
+ va_start(args, fmt);
+ why = vstrdupf(fmt, args);
+ va_end(args);
+ while (n --> 0) {
+ printf("ok %d ", ++current_test);
+ note("skip %s\n", why);
+ }
+ free(why);
+}
+
+void
+tap_todo (int ignore, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ todo_mesg = vstrdupf(fmt, args);
+ va_end(args);
+}
+
+void
+tap_end_todo () {
+ free(todo_mesg);
+ todo_mesg = NULL;
+}
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <regex.h>
+
+#if defined __APPLE__ || defined BSD
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+/* Create a shared memory int to keep track of whether a piece of code executed
+dies. to be used in the dies_ok and lives_ok macros. */
+int
+tap_test_died (int status) {
+ static int *test_died = NULL;
+ int prev;
+ if (!test_died) {
+ test_died = mmap(0, sizeof (int), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ *test_died = 0;
+ }
+ prev = *test_died;
+ *test_died = status;
+ return prev;
+}
+
+int
+like_at_loc (int for_match, const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...)
+{
+ int test;
+ regex_t re;
+ va_list args;
+ int err = regcomp(&re, expected, REG_EXTENDED);
+ if (err) {
+ char errbuf[256];
+ regerror(err, &re, errbuf, sizeof errbuf);
+ fprintf(stderr, "Unable to compile regex '%s': %s at %s line %d\n",
+ expected, errbuf, file, line);
+ exit(255);
+ }
+ err = regexec(&re, got, 0, NULL, 0);
+ regfree(&re);
+ test = for_match ? !err : err;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ if (for_match) {
+ diag(" '%s'", got);
+ diag(" doesn't match: '%s'", expected);
+ }
+ else {
+ diag(" '%s'", got);
+ diag(" matches: '%s'", expected);
+ }
+ }
+ return test;
+}
+#endif
+
diff --git a/deps/tap.c/tap.h b/deps/tap.c/tap.h
@@ -0,0 +1,114 @@
+/*
+libtap - Write tests in C
+Copyright 2012 Jake Gelbman <gelbman@gmail.com>
+This file is licensed under the GPLv2
+*/
+
+#ifndef __TAP_H__
+#define __TAP_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef va_copy
+#ifdef __va_copy
+#define va_copy __va_copy
+#else
+#define va_copy(d, s) ((d) = (s))
+#endif
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+int vok_at_loc (const char *file, int line, int test, const char *fmt,
+ va_list args);
+int ok_at_loc (const char *file, int line, int test, const char *fmt,
+ ...);
+int is_at_loc (const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...);
+int isnt_at_loc (const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...);
+int cmp_ok_at_loc (const char *file, int line, int a, const char *op,
+ int b, const char *fmt, ...);
+int bail_out (int ignore, const char *fmt, ...);
+void tap_plan (int tests, const char *fmt, ...);
+int diag (const char *fmt, ...);
+int note (const char *fmt, ...);
+int exit_status (void);
+void tap_skip (int n, const char *fmt, ...);
+void tap_todo (int ignore, const char *fmt, ...);
+void tap_end_todo (void);
+
+#define NO_PLAN -1
+#define SKIP_ALL -2
+#define ok(...) ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define is(...) is_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define isnt(...) isnt_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define cmp_ok(...) cmp_ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define plan(...) tap_plan(__VA_ARGS__, NULL)
+#define done_testing() return exit_status()
+#define BAIL_OUT(...) bail_out(0, "" __VA_ARGS__, NULL)
+#define pass(...) ok(1, "" __VA_ARGS__)
+#define fail(...) ok(0, "" __VA_ARGS__)
+
+#define skip(test, ...) do {if (test) {tap_skip(__VA_ARGS__, NULL); break;}
+#define end_skip } while (0)
+
+#define todo(...) tap_todo(0, "" __VA_ARGS__, NULL)
+#define end_todo tap_end_todo()
+
+#define dies_ok(...) dies_ok_common(1, __VA_ARGS__)
+#define lives_ok(...) dies_ok_common(0, __VA_ARGS__)
+
+#ifdef _WIN32
+#define like(...) tap_skip(1, "like is not implemented on Windows")
+#define unlike tap_skip(1, "unlike is not implemented on Windows")
+#define dies_ok_common(...) \
+ tap_skip(1, "Death detection is not supported on Windows")
+#else
+#define like(...) like_at_loc(1, __FILE__, __LINE__, __VA_ARGS__, NULL)
+#define unlike(...) like_at_loc(0, __FILE__, __LINE__, __VA_ARGS__, NULL)
+int like_at_loc (int for_match, const char *file, int line,
+ const char *got, const char *expected,
+ const char *fmt, ...);
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+int tap_test_died (int status);
+#define dies_ok_common(for_death, code, ...) \
+ do { \
+ int cpid; \
+ int it_died; \
+ tap_test_died(1); \
+ cpid = fork(); \
+ switch (cpid) { \
+ case -1: \
+ perror("fork error"); \
+ exit(1); \
+ case 0: \
+ close(1); \
+ close(2); \
+ code \
+ tap_test_died(0); \
+ exit(0); \
+ } \
+ if (waitpid(cpid, NULL, 0) < 0) { \
+ perror("waitpid error"); \
+ exit(1); \
+ } \
+ it_died = tap_test_died(0); \
+ if (!it_died) \
+ {code} \
+ ok(for_death ? it_died : !it_died, "" __VA_ARGS__); \
+ } while (0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/op.h b/op.h
@@ -336,10 +336,14 @@ stack_push_val(struct stack *stack, struct val val) {
val_print(val);
printf("\n");
#endif
- void *tmp = 0;
stack_push_small(stack, &val, sizeof(struct val));
}
+static inline void
+stack_push_op(struct stack *stack, enum opcode opcode) {
+ stack_push_small(stack, &opcode, sizeof(enum opcode));
+}
+
static inline struct val
stack_top_val(struct stack *stack, int ind) {
struct val val;
diff --git a/script.c b/script.c
@@ -9,6 +9,11 @@ void script_add_error(const char *serror) {
fprintf(stderr, "error: %s\n", serror);
}
+void script_add_warning(const char *warning) {
+ // TODO: set_error
+ fprintf(stderr, "warning: %s\n", warning);
+}
+
struct val
script_num(int n) {
// TODO: scriptnum arithmetic
diff --git a/stack.c b/stack.c
@@ -7,6 +7,7 @@
void
stack_clear(struct stack *stack) {
memset(stack->bottom, 0, stack->capacity);
+ stack->top = stack->bottom;
}
int
diff --git a/test.c b/test.c
@@ -0,0 +1,89 @@
+
+#include "script.h"
+#include "op.h"
+#include "ok/ok.h"
+#include "tap.c/tap.h"
+
+typedef void (program)(struct stack *script, struct stack *stack,\
+ struct stack *expected);
+
+#define RUNTEST(test) run_test(script, stack, expected, test)
+#define TEST(test) static inline void test(struct stack *script, \
+ struct stack *stack, \
+ struct stack *expected \
+ )
+
+/* static void */
+/* test_nip(struct stack *stack, struct stack *expected) { */
+/* stack_push_op(OP_1); */
+/* } */
+
+static inline void
+ok_stacks_equal(struct stack *s1, struct stack *s2, const char *context) {
+ void **b1 = s1->bottom;
+ void **b2 = s2->bottom;
+
+ size_t s1size = stack_size(s1);
+ size_t s2size = stack_size(s2);
+
+ cmp_ok(s1size, "==", s2size, "%s: expected stack size", context);
+ is(memcmp(b1, b2, s1size*sizeof(void*)), 0, "%s: expected stack memory", context);
+}
+
+TEST(test_simple) {
+ stack_push_op(script, OP_1);
+
+ script_eval(script, stack);
+
+ struct val one = { .is_big = 0, .type = VT_INT, .val = 1 };
+ stack_push_val(expected, one);
+ ok_stacks_equal(stack, expected, "test_simple");
+}
+
+TEST(test_nip) {
+ stack_push_op(script, OP_1);
+ stack_push_op(script, OP_2);
+ stack_push_op(script, OP_NIP);
+
+ script_eval(script, stack);
+
+ struct val two = { .is_big = 0, .type = VT_INT, .val = 2 };
+ stack_push_val(expected, two);
+ ok_stacks_equal(stack, expected, "test_nip");
+}
+
+
+static inline void
+run_test(struct stack *script, struct stack *stack, struct stack *expected,
+ program *prog)
+{
+ stack_clear(script);
+ stack_clear(stack);
+ stack_clear(expected);
+ prog(script, stack, expected);
+}
+
+
+int
+main(int argc, char *argv[]) {
+ struct stack _stack, _expected, _script;
+ struct stack *script = &_script;
+ struct stack *stack = &_stack;
+ struct stack *expected = &_expected;
+
+ plan(4);
+
+ stack_init(script);
+ stack_init(stack);
+ stack_init(expected);
+
+ RUNTEST(test_simple);
+ RUNTEST(test_nip);
+
+ stack_free(expected);
+ stack_free(stack);
+ stack_free(script);
+
+ done_testing();
+ return 0;
+}