You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

295 lines
7.8 KiB

/*
* TinyJS
*
* A single-file Javascript-alike engine
*
* Authored By Gordon Williams <gw@pur3.co.uk>
*
* Copyright (C) 2009 Pur3 Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* This is a program to run all the tests in the tests folder...
*/
#include "TinyJS.h"
#include "TinyJS_Functions.h"
#include "TinyJS_MathFunctions.h"
#include <assert.h>
#include <sys/stat.h>
#include <string>
#include <sstream>
#include <stdio.h>
#ifdef MTRACE
#include <mcheck.h>
#endif
//#define INSANE_MEMORY_DEBUG
#ifdef INSANE_MEMORY_DEBUG
// needs -rdynamic when compiling/linking
#include <execinfo.h>
#include <malloc.h>
#include <map>
#include <vector>
using namespace std;
void **get_stackframe() {
void **trace = (void**)malloc(sizeof(void*)*17);
int trace_size = 0;
for (int i=0;i<17;i++) trace[i]=(void*)0;
trace_size = backtrace(trace, 16);
return trace;
}
void print_stackframe(char *header, void **trace) {
char **messages = (char **)NULL;
int trace_size = 0;
trace_size = 0;
while (trace[trace_size]) trace_size++;
messages = backtrace_symbols(trace, trace_size);
printf("%s\n", header);
for (int i=0; i<trace_size; ++i) {
printf("%s\n", messages[i]);
}
//free(messages);
}
/* Prototypes for our hooks. */
static void *my_malloc_hook (size_t, const void *);
static void my_free_hook (void*, const void *);
static void *(*old_malloc_hook) (size_t, const void *);
static void (*old_free_hook) (void*, const void *);
map<void *, void **> malloced;
static void *my_malloc_hook(size_t size, const void *caller) {
/* Restore all old hooks */
__malloc_hook = old_malloc_hook;
__free_hook = old_free_hook;
/* Call recursively */
void *result = malloc (size);
/* we call malloc here, so protect it too. */
//printf ("malloc (%u) returns %p\n", (unsigned int) size, result);
malloced[result] = get_stackframe();
/* Restore our own hooks */
__malloc_hook = my_malloc_hook;
__free_hook = my_free_hook;
return result;
}
static void my_free_hook(void *ptr, const void *caller) {
/* Restore all old hooks */
__malloc_hook = old_malloc_hook;
__free_hook = old_free_hook;
/* Call recursively */
free (ptr);
/* we call malloc here, so protect it too. */
//printf ("freed pointer %p\n", ptr);
if (malloced.find(ptr) == malloced.end()) {
/*fprintf(stderr, "INVALID FREE\n");
void *trace[16];
int trace_size = 0;
trace_size = backtrace(trace, 16);
backtrace_symbols_fd(trace, trace_size, STDERR_FILENO);*/
} else
malloced.erase(ptr);
/* Restore our own hooks */
__malloc_hook = my_malloc_hook;
__free_hook = my_free_hook;
}
void memtracing_init() {
old_malloc_hook = __malloc_hook;
old_free_hook = __free_hook;
__malloc_hook = my_malloc_hook;
__free_hook = my_free_hook;
}
long gethash(void **trace) {
unsigned long hash = 0;
while (*trace) {
hash = (hash<<1) ^ (hash>>63) ^ (unsigned long)*trace;
trace++;
}
return hash;
}
void memtracing_kill() {
/* Restore all old hooks */
__malloc_hook = old_malloc_hook;
__free_hook = old_free_hook;
map<long, void**> hashToReal;
map<long, int> counts;
map<void *, void **>::iterator it = malloced.begin();
while (it!=malloced.end()) {
long hash = gethash(it->second);
hashToReal[hash] = it->second;
if (counts.find(hash) == counts.end())
counts[hash] = 1;
else
counts[hash]++;
it++;
}
vector<pair<int, long> > sorting;
map<long, int>::iterator countit = counts.begin();
while (countit!=counts.end()) {
sorting.push_back(pair<int, long>(countit->second, countit->first));
countit++;
}
// sort
bool done = false;
while (!done) {
done = true;
for (int i=0;i<sorting.size()-1;i++) {
if (sorting[i].first < sorting[i+1].first) {
pair<int, long> t = sorting[i];
sorting[i] = sorting[i+1];
sorting[i+1] = t;
done = false;
}
}
}
for (int i=0;i<sorting.size();i++) {
long hash = sorting[i].second;
int count = sorting[i].first;
char header[256];
sprintf(header, "--------------------------- LEAKED %d", count);
print_stackframe(header, hashToReal[hash]);
}
}
#endif // INSANE_MEMORY_DEBUG
bool run_test(const char *filename) {
printf("TEST %s ", filename);
struct stat results;
if (!stat(filename, &results) == 0) {
printf("Cannot stat file! '%s'\n", filename);
return false;
}
int size = results.st_size;
FILE *file = fopen( filename, "rb" );
/* if we open as text, the number of bytes read may be > the size we read */
if( !file ) {
printf("Unable to open file! '%s'\n", filename);
return false;
}
char *buffer = new char[size+1];
long actualRead = fread(buffer,1,size,file);
buffer[actualRead]=0;
buffer[size]=0;
fclose(file);
CTinyJS s;
registerFunctions(&s);
registerMathFunctions(&s);
s.root->addChild("result", new CScriptVar("0",SCRIPTVAR_INTEGER));
try {
s.execute(buffer);
} catch (CScriptException *e) {
printf("ERROR: %s\n", e->text.c_str());
}
bool pass = s.root->getParameter("result")->getBool();
if (pass)
printf("PASS\n");
else {
char fn[64];
sprintf(fn, "%s.fail.js", filename);
FILE *f = fopen(fn, "wt");
if (f) {
std::ostringstream symbols;
s.root->getJSON(symbols);
fprintf(f, "%s", symbols.str().c_str());
fclose(f);
}
printf("FAIL - symbols written to %s\n", fn);
}
delete[] buffer;
return pass;
}
int main(int argc, char **argv)
{
#ifdef MTRACE
mtrace();
#endif
#ifdef INSANE_MEMORY_DEBUG
memtracing_init();
#endif
printf("TinyJS test runner\n");
printf("USAGE:\n");
printf(" ./run_tests test.js : run just one test\n");
printf(" ./run_tests : run all tests\n");
if (argc==2) {
return !run_test(argv[1]);
}
int test_num = 1;
int count = 0;
int passed = 0;
while (test_num<1000) {
char fn[32];
sprintf(fn, "tests/test%03d.js", test_num);
// check if the file exists - if not, assume we're at the end of our tests
FILE *f = fopen(fn,"r");
if (!f) break;
fclose(f);
if (run_test(fn))
passed++;
count++;
test_num++;
}
printf("Done. %d tests, %d pass, %d fail\n", count, passed, count-passed);
#ifdef INSANE_MEMORY_DEBUG
memtracing_kill();
#endif
#ifdef _DEBUG
#ifdef _WIN32
_CrtDumpMemoryLeaks();
#endif
#endif
#ifdef MTRACE
muntrace();
#endif
return 0;
}