switched to C unit-tests instead of bash

master
Serge A. Zaitsev 13 years ago
parent 2928f7ec0e
commit 4b5c5ed66a

@ -3,8 +3,8 @@
all: libjsmn.a
demo: libjsmn.a demo.o
$(CC) $(LDFLAGS) demo.o -L. -ljsmn -o $@
#demo: libjsmn.a demo.o
# $(CC) $(LDFLAGS) demo.o -L. -ljsmn -o $@
libjsmn.a: jsmn.o
$(AR) rc $@ $^
@ -12,8 +12,11 @@ libjsmn.a: jsmn.o
%.o: %.c jsmn.h
$(CC) -c $(CFLAGS) $< -o $@
test: all demo
sh test.sh
test: jsmn_test
./jsmn_test
jsmn_test: jsmn_test.o
$(CC) -L. -ljsmn $< -o $@
clean:
rm -f jsmn.o demo.o

141
demo.c

@ -1,141 +0,0 @@
/* This demo is not needed to be C89-compatible, so for now GCC extensions are
* used */
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include "jsmn.h"
static void jsmn_dump_obj(jsmntok_t *obj, const char *js) {
size_t len;
char *s;
if (obj->end < 0 || obj->start < 0) {
return;
}
len = obj->end - obj->start;
printf("[%3d,%3d - %2d] (%c) ", obj->start, obj->end, obj->size,
({
char c;
switch (obj->type) {
case JSMN_PRIMITIVE: c = '.'; break;
case JSMN_STRING: c = 's'; break;
case JSMN_ARRAY: c = 'A'; break;
case JSMN_OBJECT: c = 'O'; break;
default: c = '?';
}; c;
}));
s = strndup((const char *) &js[obj->start], len);
char *p;
for (p = s; *p; p++) {
printf("%c", *p == '\n' ? ' ' : *p);
}
printf("\n");
free(s);
}
void usage(void) {
fprintf(stderr, "Usage: ./demo <file.js>\n");
exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[]) {
int i;
int r;
int c;
FILE *f;
int filesize = 0;
jsmn_parser parser;
char *js = NULL;
jsmntok_t *tokens;
int block_size = 1024;
int num_tokens = 100;
while ((c = getopt(argc, argv, "ht:b:")) != -1) {
switch (c) {
case 'h':
usage();
break;
case 't':
num_tokens = atoi(optarg);
if (errno || num_tokens < 0) {
fprintf(stderr, "Invalid token number: %s!\n", optarg);
exit(EXIT_FAILURE);
}
break;
case 'b':
block_size = atoi(optarg);
if (errno || block_size < 0) {
fprintf(stderr, "Invalid block size: %s!\n", optarg);
exit(EXIT_FAILURE);
}
}
}
if (optind >= argc) {
usage();
}
if (strcmp(argv[optind], "-") == 0) {
f = stdin;
} else {
f = fopen(argv[optind], "r");
if (f == NULL) {
fprintf(stderr, "Failed to open file `%s`\n", argv[1]);
exit(EXIT_FAILURE);
}
}
tokens = malloc(num_tokens * sizeof(jsmntok_t));
if (tokens == NULL) {
fprintf(stderr, "Cannot allocate anough memory\n");
exit(EXIT_FAILURE);
}
jsmn_init_parser(&parser, js, tokens, num_tokens);
char *buf = malloc(block_size);
while (1) {
r = fread(buf, 1, block_size, f);
if (r <= 0) {
break;
}
js = (char *) realloc(js, filesize + r + 1);
if (js == NULL) {
fprintf(stderr, "Cannot allocate anough memory\n");
fclose(f);
exit(EXIT_FAILURE);
}
parser.js = js;
memcpy(js + filesize, buf, r);
filesize += r;
js[filesize] = '\0';
r = jsmn_parse(&parser);
if (r < 0) {
printf("error %d at pos %d: %s\n", r, parser.pos, &js[parser.pos]);
}
for (i = 0; i<num_tokens; i++) {
jsmn_dump_obj(&parser.tokens[i], js);
}
}
fclose(f);
free(buf);
free(tokens);
free(js);
return 0;
}

@ -0,0 +1,57 @@
#include <stdio.h>
#include "jsmn.c"
static int test_passed = 0;
static int test_failed = 0;
/* Terminate current test with error */
#define fail() return __LINE__
/* Successfull end of the test case */
#define done() return 0
/* Check single condition */
#define check(cond) do { if (!(cond)) fail(); } while (0)
/* Test runner */
static void test(int (*func)(void), const char *name) {
int r = func();
if (r == 0) {
test_passed++;
} else {
test_failed++;
printf("FAILED: %s (at line %d)\n", name, r);
}
}
#define TOKEN_EQ(t, tok_start, tok_end, tok_type) \
((t).start == tok_start \
&& (t).end == tok_end \
&& (t).type == (tok_type))
#define TOKEN_PRINT(t) \
printf("start: %d, end: %d, type: %d\n", (t).start, (t).end, (t).type)
int test_primitive() {
int r;
jsmn_parser p;
jsmntok_t tokens[10];
jsmn_init_parser(&p, "{\"a\": 0}", tokens, 10);
r = jsmn_parse(&p);
check(r == JSMN_SUCCESS);
check(TOKEN_EQ(tokens[0], 0, 8, JSMN_OBJECT));
check(TOKEN_EQ(tokens[1], 2, 3, JSMN_STRING));
check(TOKEN_EQ(tokens[2], 6, 7, JSMN_PRIMITIVE));
return 0;
}
int main() {
test(test_primitive, "test primitive values");
return 0;
}

@ -1,134 +0,0 @@
#!/bin/bash
#
# Test script is organized like this:
# o two variables (PASSED and FAILED) hold the total
# number of passed/faled tests
# o expect() function performs a single test. First
# argument of the function is the variable name and
# the second is an expected value. PASSED/FAILED
# values are updated automatically
#
# Most tests look like:
# |
# | expect "varName" "expectedValue" << JSON_END
# | ..json data here...
# | JSON_END
# |
#
PASSED=0
FAILED=0
function expect() {
ret=$(./demo -t 10 -b 256 - | grep -A 1 "$1" | tail -n 1 | cut -c 20-)
if [ "x$ret" = "x$2" ]; then
PASSED=$(($PASSED+1))
else
echo "Failed: $1 != $2"
FAILED=$(($FAILED+1))
fi
}
#
# TEST SET: Basic types (simple values)
#
expect 'boolVar' 'true' << JSON_END
"boolVar" : true
JSON_END
expect 'boolVar' 'false'<< JSON_END
"boolVar" : false
JSON_END
expect 'intVar' '12345' << JSON_END
"intVar" : 12345
JSON_END
expect 'floatVar' '12.345' << JSON_END
"floatVar" : 12.345
JSON_END
expect 'nullVar' 'null' << JSON_END
"nullVar" : null
JSON_END
expect 'strVar' 'hello' << JSON_END
"strVar" : "hello"
JSON_END
#
# TEST SET: Simple types (boundary values)
#
expect 'intVar' '0' << JSON_END
"intVar" : 0
JSON_END
expect 'intVar' '-0' << JSON_END
"intVar" : -0
JSON_END
expect 'floarVar' '-0.0' << JSON_END
"floarVar" : -0.0
JSON_END
expect 'strVar' '\n\r\b\t \u1234' << JSON_END
"strVar" : "\n\r\b\t \u1234"
JSON_END
expect 'strVar' '' << JSON_END
"strVar" : ""
JSON_END
#
# TEST SET: Array types
#
expect 'arr' '[1,2,3,4,5]' << JSON_END
"arr" : [1,2,3,4,5]
JSON_END
expect 'arr' '[1, 2.3, "4", null, true]' << JSON_END
"arr" : [1, 2.3, "4", null, true]
JSON_END
expect 'arr' '[]' << JSON_END
"arr" : []
JSON_END
#
# TEST SET: Object types
#
expect 'obj' '{"a":"b"}' << JSON_END
"obj":{"a":"b"}
JSON_END
expect 'objField' 'value' << JSON_END
{
"foo" : "bar",
"objField" : "value"
}
JSON_END
expect 'foo' 'bar' << JSON_END
{
"foo" : "bar"
"a" : [
{
"x" : "y",
"z" : "zz"
},
3,
false,
true,
"end"
],
}
JSON_END
echo
echo "Passed: $PASSED"
echo "Failed: $FAILED"
echo
Loading…
Cancel
Save