updatified makefile. to infinity and maybe a little further? can't hardly be verbose...
[tinyjs-rewrite.git] / src / run_tests.cpp
blob8b5792c0d970bca96feead190d019b543590a76b
1 /*
2 * TinyJS
4 * A single-file Javascript-alike engine
6 * Authored By Gordon Williams <gw@pur3.co.uk>
8 * Copyright (C) 2009 Pur3 Ltd
10 * Permission is hereby granted, free of charge, to any person obtaining a copy of
11 * this software and associated documentation files (the "Software"), to deal in
12 * the Software without restriction, including without limitation the rights to
13 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
14 * of the Software, and to permit persons to whom the Software is furnished to do
15 * so, subject to the following conditions:
17 * The above copyright notice and this permission notice shall be included in all
18 * copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
30 * This is a program to run all the tests in the tests folder...
33 #include <assert.h>
34 #include <sys/stat.h>
35 #include <string>
36 #include <sstream>
37 #include <stdio.h>
38 #include "tinyjs.h"
40 #ifdef MTRACE
41 #include <mcheck.h>
42 #endif
44 //#define INSANE_MEMORY_DEBUG
46 #ifdef INSANE_MEMORY_DEBUG
47 // needs -rdynamic when compiling/linking
48 #include <execinfo.h>
49 #include <malloc.h>
50 #include <map>
51 #include <vector>
53 /* Prototypes for our hooks. */
54 static void *my_malloc_hook (size_t, const void *);
55 static void my_free_hook (void*, const void *);
56 static void *(*old_malloc_hook) (size_t, const void *);
57 static void (*old_free_hook) (void*, const void *);
59 /* pointer map */
60 std::map<void*, void**> malloced;
63 void **get_stackframe()
65 void **trace = (void**)malloc(sizeof(void*)*17);
66 int trace_size = 0;
68 for (int i=0; i<17; i++)
70 trace[i]=(void*)0;
72 trace_size = backtrace(trace, 16);
73 return trace;
76 void print_stackframe(char *header, void **trace)
78 char **messages = (char **)NULL;
79 int trace_size = 0;
80 trace_size = 0;
81 while (trace[trace_size])
83 trace_size++;
85 messages = backtrace_symbols(trace, trace_size);
86 printf("%s\n", header);
87 for (int i=0; i<trace_size; ++i)
89 printf("%s\n", messages[i]);
91 //free(messages);
94 static void* my_malloc_hook(size_t size, const void *caller)
96 /* Restore all old hooks */
97 __malloc_hook = old_malloc_hook;
98 __free_hook = old_free_hook;
99 /* Call recursively */
100 void *result = malloc (size);
101 /* we call malloc here, so protect it too. */
102 //printf ("malloc (%u) returns %p\n", (unsigned int) size, result);
103 malloced[result] = get_stackframe();
105 /* Restore our own hooks */
106 __malloc_hook = my_malloc_hook;
107 __free_hook = my_free_hook;
108 return result;
111 static void my_free_hook(void *ptr, const void *caller)
113 /* Restore all old hooks */
114 __malloc_hook = old_malloc_hook;
115 __free_hook = old_free_hook;
116 /* Call recursively */
117 free (ptr);
118 /* we call malloc here, so protect it too. */
119 //printf ("freed pointer %p\n", ptr);
120 if(malloced.find(ptr) == malloced.end())
123 fprintf(stderr, "INVALID FREE\n");
124 void *trace[16];
125 int trace_size = 0;
126 trace_size = backtrace(trace, 16);
127 backtrace_symbols_fd(trace, trace_size, STDERR_FILENO);
130 else
132 malloced.erase(ptr);
134 /* Restore our own hooks */
135 __malloc_hook = my_malloc_hook;
136 __free_hook = my_free_hook;
139 void memtracing_init()
141 old_malloc_hook = __malloc_hook;
142 old_free_hook = __free_hook;
143 __malloc_hook = my_malloc_hook;
144 __free_hook = my_free_hook;
147 long gethash(void **trace)
149 unsigned long hash = 0;
150 while (*trace)
152 hash = ((hash << 1)^(hash >> 63) ^ ((unsigned long)(*trace)));
153 trace++;
155 return hash;
158 void memtracing_kill()
160 int i;
161 int count;
162 long hash;
163 bool done;
164 char header[256];
165 std::map<long, void**> hashToReal;
166 std::map<long, int> counts;
167 std::vector<std::pair<int, long>> sorting;
168 /* Restore all old hooks */
169 __malloc_hook = old_malloc_hook;
170 __free_hook = old_free_hook;
171 auto it = malloced.begin();
172 while (it!=malloced.end())
174 hash = gethash(it->second);
175 hashToReal[hash] = it->second;
176 if (counts.find(hash) == counts.end())
178 counts[hash] = 1;
180 else
182 counts[hash]++;
184 it++;
186 auto = counts.begin();
187 while (countit != counts.end())
189 sorting.push_back(std::pair<int, long>(countit->second, countit->first));
190 countit++;
192 // sort
193 done = false;
194 while (!done)
196 done = true;
197 for(i=0;i<sorting.size()-1;i++)
199 if(sorting[i].first < sorting[i+1].first)
201 auto t = sorting[i];
202 sorting[i] = sorting[i+1];
203 sorting[i+1] = t;
204 done = false;
208 for(i=0; i<sorting.size(); i++)
210 hash = sorting[i].second;
211 count = sorting[i].first;
212 sprintf(header, "--------------------------- LEAKED %d", count);
213 print_stackframe(header, hashToReal[hash]);
216 #endif // INSANE_MEMORY_DEBUG
219 TinyJS::Interpreter* new_tjs()
221 auto s = new TinyJS::Interpreter;
222 TinyJS::registerFunctions(s);
223 TinyJS::registerMathFunctions(s);
224 return s;
227 bool fexists(const char* filename, size_t* fsizedest=NULL)
229 struct stat st;
230 if(stat(filename, &st) != 0)
232 if(fsizedest != NULL)
234 (*fsizedest) = st.st_size;
236 return true;
238 return false;
241 char* readFile(const char* filename)
243 size_t size;
244 long actualsize;
245 char* buffer;
246 FILE* file;
247 printf("TEST %s ", filename);
248 if (fexists(filename, &size) == false)
250 printf("Cannot stat file '%s'\n", filename);
251 return NULL;
253 file = fopen(filename, "rb");
254 /* if we open as text, the number of bytes read may be > the size we read */
255 if(!file)
257 printf("Unable to open file! '%s'\n", filename);
258 return NULL;
260 buffer = new char[size+1];
261 actualsize = fread(buffer,1,size,file);
262 buffer[actualsize]=0;
263 buffer[size]=0;
264 fclose(file);
265 return buffer;
268 bool run_test(const char *filename)
270 bool pass;
271 char* buffer;
272 char fn[64];
273 TinyJS::Interpreter* s;
274 if((buffer = readFile(filename)) == NULL)
276 return false;
278 s = new_tjs();
279 s->getRoot()->addChild("result", new TinyJS::Variable("0", TinyJS::SCRIPTVAR_INTEGER));
282 s->execute(buffer);
284 catch(TinyJS::RuntimeError *e)
286 printf("ERROR: %s\n", e->text.c_str());
288 pass = s->getRoot()->getParameter("result")->getBool();
289 if (pass)
291 printf("PASS\n");
293 else
295 sprintf(fn, "%s.fail.js", filename);
296 FILE *f = fopen(fn, "wt");
297 if(f)
299 std::ostringstream symbols;
300 s->getRoot()->getJSON(symbols);
301 fprintf(f, "%s", symbols.str().c_str());
302 fclose(f);
304 printf("FAIL - symbols written to %s\n", fn);
306 delete[] buffer;
307 return pass;
310 int main(int argc, char **argv)
312 int test_num = 1;
313 int count = 0;
314 int passed = 0;
315 FILE* fh;
316 #ifdef MTRACE
317 mtrace();
318 #endif
319 #ifdef INSANE_MEMORY_DEBUG
320 memtracing_init();
321 #endif
322 printf("TinyJS test runner\n");
323 printf("Usage:\n");
324 printf(" ./run_tests test.js # run just one test\n");
325 printf(" ./run_tests # run all tests\n");
326 if (argc == 2)
328 return !run_test(argv[1]);
330 while (test_num < 1000)
332 char fn[32];
333 sprintf(fn, "tests/test%03d.js", test_num);
334 // check if the file exists - if not, assume we're at the end of our tests
335 if(fexists(fn) == false)
337 break;
339 if (run_test(fn))
341 passed++;
343 count++;
344 test_num++;
346 printf("Done. %d tests, %d pass, %d fail\n", count, passed, count-passed);
347 #ifdef INSANE_MEMORY_DEBUG
348 memtracing_kill();
349 #endif
350 #ifdef _DEBUG
351 #ifdef _WIN32
352 _CrtDumpMemoryLeaks();
353 #endif
354 #endif
355 #ifdef MTRACE
356 muntrace();
357 #endif
358 return 0;