btcs

bitcoin script parser/evaluator/compiler/decompiler
git clone git://jb55.com/btcs
Log | Files | Refs | README | LICENSE

commit 7dcddcf0d1223ff7b232dcbba1565442ece06514
parent d62aac93757f84cc1ccd2cf9d30847b66685d647
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 26 Oct 2017 11:34:59 -0700

test: tests working

Diffstat:
M.gitignore | 1+
MMakefile | 45++++++++++++++++++++++++++++++++-------------
MREADME.md | 2++
Adeps/tap.c/package.json | 23+++++++++++++++++++++++
Adeps/tap.c/tap.c | 331+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adeps/tap.c/tap.h | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mop.h | 6+++++-
Mscript.c | 5+++++
Mstack.c | 1+
Atest.c | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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; +}