string test="",name=0,module=0,chapter=0; array modules=({}); array chapters=({}); array tests=({}); int mode=0; int n=0; void finish_test() { if (!name) return; int m1,m2,m3; switch (mode) { case 0: write("void test_"+(m1=sizeof(modules))+ "_"+(m2=sizeof(chapters))+ "_"+(m3=sizeof(tests))+"()\n" "{\n" " write(\" test: "+name+"...\");\n" " mixed err=catch {\n"+ test+ "#"+__LINE__+" \"from "+__FILE__+"\"\n" " };\n" " if (!err) error(\"missing ok()\\n\");\n" " if (stringp(err))\n" " write(err+\"\\n\");\n" " else\n" " {\n" " failed++;\n" " err=({err[0],err[1][sizeof(err[1])-2..]});\n" " write(\"\\n\"+master()->describe_backtrace(err));\n" " }\n" "}\n\n"); break; case 1: test=replace(cpp("#define ok(S) return \"ok\"\n" "#define fail(S) return (S)\n"+ replace(test,({"#","ok()"}),({"½·½","ok(\"ok\")"})), ),"½·½","#"); write("test_any([["+test+"]], \"ok\")\n"); break; case 2: test=replace(cpp("#define ok(S) return 1\n" "#define fail(S) do { werror(\"failure; "+module+"/"+chapter+"/"+name+": \"+(S)+\"\\n\"); return 0; } while (0)\n"+ replace(test,({"#","ok()"}),({"½·½","ok(\"ok\")"})), ),"½·½","#"); write("test "+(++n)+", expected result: EQ\n" "mixed a()" "{\n"+ test+ "}\n" "mixed b() { return 1; }\n" "\n....\n"); break; } name=0; } void new_test(string _name,string file,int line) { if (name) finish_test(); if (!chapter) werror(file+":"+line+"; missing chapter\n"); name=_name; tests+=({name=_name}); test="#"+(line+1)+" \""+file+"\"\n"; werror(" generating test: "+name+"\n"); } void finish_chapter() { if (!chapter) return; finish_test(); int m1,m2; if (!mode) { write("void test_chapter_"+(m1=sizeof(modules)) +"_"+(m2=sizeof(chapters))+"()\n" "{\n" " int infailed=failed,inisok=isok;\n" " write(\" chapter: "+chapter+"\\n\");\n"); foreach (indices(tests),int n) write(" test_"+m1+"_"+m2+"_"+(n+1)+"();\n"); write(" write(\" tests failed: \"+(failed-infailed)+\"\\n\"\n" " \" tests ok: \"+(isok-inisok)+\"\\n\");\n"); write("}\n\n"); } tests=({}); werror(" generating chapter: "+chapter+"\n"); } void new_chapter(string _name,string file,int line) { if (chapter) finish_chapter(); if (!module) werror(file+":"+line+"; missing module\n"); chapters+=({chapter=_name}); } void finish_module() { if (!module) return; finish_chapter(); int m; if (!mode) { write("void test_module_"+(m=sizeof(modules))+"()\n" "{\n" " int infailed=failed,inisok=isok;\n" " write(\"module: "+module+"\\n\");\n"); foreach (indices(chapters),int n) write(" test_chapter_"+m+"_"+(n+1)+"();\n"); write(" write(\"tests failed: \"+(failed-infailed)+\"\\n\"\n" " \"tests ok: \"+(isok-inisok)+\"\\n\");\n"); write("}\n\n"); } chapters=({}); } void new_module(string name,string file,int line) { finish_module(); modules+=({module=name}); werror(" generating tests for module: "+module+"\n"); } int main(int ac,array am) { int n; if (ac>=2 && am[1]=="-t") mode=2,am=am[..0]+am[2..]; if (ac<2) { werror("usage: mktests [-t] \n"); return 1; } object f=Stdio.File(am[1],"r"); if (!mode) { write("// generated from "+am[1]+" by mktests\n"); write("// do not edit this file\n\n\n"); write("int failed,isok;\n" "\n" "void fail(string s) { failed++; throw(s); }\n" "void ok(void|string s) { isok++; throw(s||\"ok\"); }\n"); } foreach (f->read(0xffffff)/"\n",string s) { n++; if (s!="" && s[0]=='#') { string what,name; sscanf(s,"#%s %s",what,name); switch (what) { case "module": new_module(name,am[1],n); break; case "chapter": new_chapter(name,am[1],n); break; case "test": new_test(name,am[1],n); break; default: test+=s+"\n"; break; } } else test+=s+"\n"; } finish_module(); if (!mode) { write("int main()\n" "{\n"); foreach (indices(modules),int n) write(" test_module_"+(n+1)+"();\n"); write(" write(\"total tests failed: \"+failed+\"\\n\"\n" " \"total tests ok: \"+isok+\"\\n\");\n" " return !!failed;\n" "}\n\n"); } }