import haxe.zip.Compress; import haxe.zip.Uncompress; import haxe.zip.FlushMode; import sys.FileSystem; import sys.db.Connection; import sys.db.Sqlite; import sys.db.Mysql; import sys.FileSystem; import haxe.io.Bytes; import haxe.io.BytesData; import cpp.Random; import cpp.net.Poll; import sys.io.File; import sys.io.FileSeek; import sys.net.Host; import sys.net.Socket; import sys.net.UdpSocket; import sys.net.UdpSocket; import sys.io.Process; #if haxe4 import sys.thread.Deque; import sys.thread.Thread; #else import cpp.vm.Deque; import cpp.vm.Thread; #end using cpp.NativeArray; using cpp.AtomicInt; // These should be ignored in haxe 3.3... import cpp.link.StaticStd; import cpp.link.StaticRegexp; import cpp.link.StaticZlib; import cpp.link.StaticMysql; import cpp.link.StaticSqlite; @:buildXml('') extern class SslTest { @:extern @:native("_hx_ssl_init") extern public static function socket_init():Void; } class Test { static var errors = new Array(); static var lastErrorCount = 0; var x:Int; public function new() { x = 1; } public static function log(t:String) { Sys.println(t); } public static function v(t:String) { Sys.println(" " + t); } public static function ok() { if (lastErrorCount==errors.length) { v("ok"); return 0; } else { lastErrorCount=errors.length; v("bad"); return 1; } } public static function error(e:String) { Sys.println("Test Failed:" + e); errors.push(e); return -1; } public static function thisFile() { #if nme_install_tool var bytes:haxe.io.Bytes = ApplicationMain.getAsset("Test.hx"); #else var bytes = sys.io.File.getBytes("Test.hx"); #end return bytes; } public static function testDb(cnx:Connection) : Int { v("connected :" + cnx); if (cnx.dbName() == "SQLite") { cnx.request(" CREATE TABLE IF NOT EXISTS UserPwd ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, money DOUBLE, password BLOB )"); cnx.request("SELECT '豪鬼' test"); } else { cnx.request(" CREATE TABLE IF NOT EXISTS UserPwd ( id INTEGER NOT NULL AUTO_INCREMENT, name TEXT, age INTEGER, money DOUBLE, password BLOB, PRIMARY KEY(id) )"); } var dels = cnx.request("DELETE FROM UserPwd"); if (dels.nfields != 0) return error("Bad DELETE'd result"); v("deleted " + dels.length + " existing rows"); cnx.request("INSERT INTO UserPwd (name,age,money,password) VALUES ('John',32,100.45,X'c0ffee')"); cnx.request("INSERT INTO UserPwd (name,age,money,password) VALUES ('Bob',14,4.50,X'deadbeef01020304')"); var rset = cnx.request("SELECT * FROM UserPwd"); var length = rset.length; v("found "+length+" users"); if (length!=2) return error("Bad user count"); for( row in rset ) { var pass:Dynamic = row.password; var password = Std.is(pass, haxe.io.BytesData) ? haxe.io.Bytes.ofData(pass) : pass; var md5 = haxe.crypto.Md5.make(password).toHex().substr(0,8); v(" user "+row.name+" is "+row.age+" years old, password:" + md5); if (md5!="5f80e231" && md5!="8ed0b363") return error("Bad binary blob store"); } return 0; } static function testDate() { log("Test date"); var now = Date.now(); v(Std.string(now)); var then = Date.fromString("1977-06-11"); v(Std.string(then)); if (now.getTime()<=then.getTime()) return error("Date fromString - time travel"); var later = DateTools.makeUtc(1996,5,4,17,55,11); v(Std.string(later)); var diff:Float = untyped __global__.__hxcpp_timezone_offset(now.mSeconds); v("timezone offet:" + diff); return ok(); } public static function testCompress() { log("Test compress"); var bytes = thisFile(); var compress = new Compress(9); compress.setFlushMode(FlushMode.FINISH); var buffer = haxe.io.Bytes.alloc(bytes.length * 2 + 100); var r = compress.execute(bytes,0,buffer,0); compress.close(); var compressed = buffer.sub(0,r.write); v("compressed size " + compressed.length ); v("try closing too many times..."); var caughtError = false; try { compress.close(); } catch(e:Dynamic) { v("correctly caught " + e ); caughtError = true; } if (!caughtError) error("Zlib closed without throwing error"); var decompressed = Uncompress.run(compressed); v("decompressed size:" + decompressed.length + "/" + bytes.length); if (decompressed.compare(bytes)!=0) return error("Compress/Uncompress mismatch"); return ok(); } public static function testRegexp() { log("Test Regexp/BytesInput"); // This utf8 comment is parsed by the code ... // su©©ess var success = false; var utf8Match = ~/su©+ess/u; // This comment is parsed by the code ... //XML var match = ~/^..XML/; var input = new haxe.io.BytesInput(thisFile()); try { while(true) { var line = input.readLine(); if (match.match(line)) { var xml = Xml.parse(line.substr(5)); v("found xml:" + xml.firstElement().get("value")); if (xml.firstElement().get("value")!="Hello World!") return error("Bad universal greeting"); } if (utf8Match.match(line)) { v("found utf8:" + utf8Match.matched(0)); success = true; } } } catch (e:Dynamic) { } if (!success) return error("Could not find success in utf8 code"); return ok(); } public static function testRegexpMixedUnicode() { var success = true; // when matching a utf8 subject string against a utf16 pattern and vice versa for (pattern => subject in ["[A-Za-zÀ-ÖØ-öø-ÿ0-9]+" => "a", "[A-Z]+" => "ÀÖA"]) { if (new EReg(pattern, "").match(subject)) { v('"$subject" matches against ~/$pattern/'); } else { return error('"$subject" does not match against ~/$pattern/'); } } return ok(); } public static function testSqlite() { log("Test sqlite"); var dbFile = "hxcpp.db"; var cnx = Sqlite.open(dbFile); if (testDb(cnx)!=0) return error("db error"); cnx.close(); return ok(); } public static function testMysql() { log("Test mysql"); var cnx:sys.db.Connection = null; try { cnx = Mysql.connect({ host : "localhost", port : 3306, user : "hxcpp", pass : "hxcpp", socket : null, database : "hxcpp" }); } catch(e:Dynamic) { v("mysql got error - ok because no server" ); } if (cnx!=null) { if (testDb(cnx)!=0) error("TestDB failed"); cnx.close(); } return ok(); } public static function testRandom() { log("Test Random"); var rand = new Random(); var f0 = rand.float(); var f1 = rand.float(); v('samples $f0,$f1'); if (f0==f1) return error("Not random enough"); rand.setSeed(1); var i0 = rand.int(256); rand.setSeed(2); var i1 = rand.int(256); v('int samples $i0,$i1'); if (i0!=91 || i1!=217) return error("Non-repeatable random seed, should be 91,217"); var tries = 0; while(rand.int(1000)!=999) tries++; v('tries to 1000 = $tries'); if (tries!=749) return error("Non-repeatable random iterations"); return ok(); } public static function tryFunc( func ) : Bool { try { func(); return true; } catch(e:Dynamic) { } return false; } public static function testFile() { log("Test File"); try { var filename = "testfile.txt"; tryFunc( function() FileSystem.deleteFile(filename) ); tryFunc( function() FileSystem.deleteFile(filename+"-copy") ); tryFunc( function() FileSystem.deleteFile(filename+".bin") ); tryFunc( function() FileSystem.deleteFile(filename+".out") ); var contents = "line1\nline2\n"; v("compare..."); File.saveContent(filename,contents); if ( File.getContent(filename)!=contents ) return error("getContent mismatch"); v("copy..."); File.copy(filename,filename+"-copy"); var bytes = File.getBytes(filename+"-copy"); if ( bytes.compare( Bytes.ofString(contents) ) !=0 ) return error("copy getBytes mismatch"); File.saveBytes(filename+".bin",bytes); v("file in..."); var fileIn = File.read(filename+".bin"); if (fileIn.readByte()!=contents.charCodeAt(0)) return error("File readByte mismatch"); var buffer = Bytes.alloc(5); buffer.set(0,'-'.code); buffer.set(4,'+'.code); if (fileIn.readBytes(buffer,1,3)!=3) return error("Could not read 3 bytes"); v( "read 3: " + buffer.toString() ); if ( buffer.toString() != "-ine+" ) return error("Bad sub-buffer readBytes"); v("seek..."); if (fileIn.tell()!=4) return error("tell!=4"); fileIn.seek(4, SeekCur ); if (fileIn.tell()!=8) return error("SeekCur tell!=8"); fileIn.seek(7, SeekBegin ); if (fileIn.tell()!=7) return error("SeekSet tell!=7"); var rest = Bytes.alloc( contents.length - fileIn.tell() ); fileIn.readBytes(rest,0,rest.length); if (fileIn.eof()) return error("File at end, but not eof"); fileIn.seek( -contents.length, SeekEnd ); if (fileIn.tell()!=0) return error("File seek from end to beginning failed"); fileIn.close(); v("write..."); var fileOut = File.write(filename+".out"); fileOut.writeByte('W'.code); if (fileOut.writeBytes(buffer,1,3)!=3) return error("Could not write 3 bytes"); if (fileOut.tell()!=4) return error("Bad tell on file write"); fileOut.flush(); if (File.getContent(filename+".out")!="Wine") return error("Bad reading after flush"); fileOut.seek(1,SeekBegin); fileOut.writeByte('a'.code); fileOut.close(); var contents = File.getContent(filename+".out"); v("have :" + contents); if (contents!="Wane") return error("Bad readback after seek"); v("cleanup..."); FileSystem.deleteFile(filename); FileSystem.deleteFile(filename + "-copy"); FileSystem.deleteFile(filename + ".bin"); FileSystem.deleteFile(filename + ".out"); return ok(); } catch(e:Dynamic) { return error("Unexpected error in testFile: " + e); } } public static function testHost() { log("Test Host"); try { var localhost = Host.localhost(); v('localhost :$localhost'); var host = new Host(localhost); v('host :$host'); // var reverse = host.reverse(); // v('reverse :$reverse'); return ok(); } catch(e:Dynamic) { return error("Unexpected error in testHost: " + e); } } public static function testFileSystem() { try { log("Test FileSystem"); tryFunc( function() FileSystem.deleteFile("dir/file.txt") ); tryFunc( function() FileSystem.deleteFile("dir/txt.file") ); tryFunc( function() FileSystem.deleteFile("dir/child") ); tryFunc( function() FileSystem.deleteDirectory("dir") ); v("create dir"); if (!tryFunc( function() FileSystem.createDirectory("dir") ) ) return error("Could not create 'dir'"); if (!tryFunc( function() FileSystem.createDirectory("dir/child") ) ) return error("Could not create 'dir/child'"); File.saveContent("dir/file.txt","hello"); var stat = FileSystem.stat("dir/file.txt"); v(Std.string(stat)); if (stat.size!=5) return error("File does not contain 5 bytes"); if ( Std.string(stat.ctime).length != 19) return error("File ctime does not appear to be a date"); v("exists"); if (!FileSystem.exists("dir")) return error("'dir' should exist"); if (!FileSystem.exists("dir/file.txt")) return error("'/file.txt' should exist"); var files = FileSystem.readDirectory("dir"); v("dir contents:" + files); if (files.length!=2 || files.indexOf("file.txt")<0 || files.indexOf("child")<0) return error("Unexpected dir contents " + (files.indexOf("file.txt") + "," + files.indexOf("child")) ); if (tryFunc( function() FileSystem.deleteDirectory("dir/junk") ) ) return error("No error deleting junk directory"); if (tryFunc( function() FileSystem.deleteFile("dir/junk") ) ) return error("No error deleting junk file"); if (tryFunc( function() FileSystem.deleteFile("dir/child") ) ) return error("No error deleting directory as file"); if (tryFunc( function() FileSystem.deleteDirectory("dir/file.txt") ) ) return error("No error deleting file as directory"); var fullPath = FileSystem.fullPath("dir/child"); v('fullPath: $fullPath'); var fullPath = FileSystem.fullPath("dir/file.txt"); v('fullPath: $fullPath'); v("isDirectory..."); if (FileSystem.isDirectory("dir/file.txt")) return error("file appears to be a directory"); if (!FileSystem.isDirectory("dir/child")) return error("directory appears to not be a directory"); if (FileSystem.isDirectory("dir/junk")) return error("junk appears to be a directory"); if (FileSystem.isDirectory("dir/file.txt")) return error("file appears to be a directory"); v("rename..."); if (tryFunc( function() FileSystem.rename("dir/a", "dir/b")) ) return error("No error renaming missing file"); FileSystem.rename("dir/file.txt","dir/txt.file"); if (!FileSystem.exists("dir/txt.file") || FileSystem.exists("dir/file.txt")) return error("Rename seemed to go wrong " + FileSystem.readDirectory("dir")); v("cleanup.."); FileSystem.deleteFile("dir/txt.file"); FileSystem.deleteDirectory("dir/child"); FileSystem.deleteDirectory("dir"); if (FileSystem.readDirectory(".").indexOf("dir")>=0) return error("Directory removed, but sill there?:" + FileSystem.readDirectory(".")); return ok(); } catch(e:Dynamic) { return error("Unexpected error in testFileSystem: " + e); } } public static function testSys() { log("Test Sys"); try { Sys.putEnv("mykey","123"); var env = Sys.getEnv("mykey"); v("got env:" + env); if (env!="123") return error("Bad environment get"); v("little sleep..."); var t0 = Sys.time(); Sys.sleep(0.1); var t1 = Sys.time(); v("Slept for: " + (t1-t0)); if (t1<=t0 || (t1-t0)>10) return error("Too sleepy"); v("CpuTime: " + Sys.cpuTime()); v("Cwd: " + Sys.getCwd()); v("Program Path: " + Sys.programPath()); var env = Sys.environment(); v("Environment mykey: " + env.get("mykey") ); if (env.get("mykey")!="123") return error("Could not find mykey in environment"); v("Ignore getChar auto test"); v("Args: " + Sys.args()); v("SystemName: " + Sys.systemName()); v("Skipping Sys.setTimeLocale" + Sys.setTimeLocale); // Sys.command return ok(); } catch(e:Dynamic) { return error("Unexpected error in testSys: " + e); } } public static function testCommand() { log("Test Command"); try { var code = Sys.command( Sys.programPath(), ["exit", "13"]); if (code!=13) return error('Process exited with code $code, not 13'); return ok(); } catch(e:Dynamic) { return error("Unexpected error in testCommand: " + e); } } public static function runAsProcess() { var args = Sys.args(); var job = args.shift(); if (job=="exit") { Sys.exit( Std.parseInt(args[0]) ); } else if (job=="socket") { socketClient(); Sys.exit(0); } else Sys.println('Unknown job : "$job"'); Sys.exit(-99); } public static function testPoll() { log("Test poll"); try { var poll = new Poll(4); poll.prepare([],[]); var t0 = Sys.time(); v("poll..."); poll.poll([],0.1); var t = Sys.time()-t0; v('took ${t}s'); /* if (t<0.1) return error("Timeout too soon"); if (t>10) return error("Timeout too slow"); */ return ok(); } catch(e:Dynamic) { return error("Unexpected error in testPoll: " + e); } } public static function testUdpSocket() { log("Test UdpSocket"); try { var udp = new UdpSocket(); udp.close(); return ok(); } catch(e:Dynamic) { return error("Unexpected error in testUdpSocket: " + e); } } static var socketClientRunning = true; public static function readOutput(proc:Process) { Thread.create( function() { while(true) { try { var line = proc.stdout.readLine(); v("---> " + line); } catch(e:Dynamic) { break; } } var code = proc.exitCode(); v("process exit code :" + code); socketClientRunning = false; }); } public static function testSocket() { log("Test Socket"); try { v("Spawn client.."); var proc = new Process( Sys.programPath(), ["socket"] ); readOutput(proc); v("connect localhost.."); var host = new Host("localhost"); var socket = new Socket(); socket.bind(host,0xcccc); v("listen..."); socket.listen(1); v("accept..."); var connected = socket.accept(); v("connected!"); var input = connected.input; var output = connected.output; v("send..."); connected.setFastSend(true); connected.write("ping"); var buffer = Bytes.alloc(8); if (input.readBytes(buffer,0,8)!=8) return error("Could not read from socket"); var got = buffer.toString(); v('got $got'); if (got!="pingpong") return error("Bad socket read"); v("close connection.."); connected.close(); v("close original.."); socket.close(); for(wait in 0...10) { if (!socketClientRunning) break; v("wait for client..."); Sys.sleep(0.1); } if (socketClientRunning) return error("Socket client did not finish"); return ok(); } catch(e:Dynamic) { return error("Unexpected error in Socket: " + e); } } public static function socketClient() { log("Client proc..."); var host = new Host("localhost"); var socket:Socket = null; for(attempt in 0...10) { try { log("Client connect..."); socket = new Socket(); socket.connect(host,0xcccc); break; } catch(e:Dynamic) { socket.close(); socket = null; log("Client failed :" + e); } log("sleep..."); Sys.sleep(0.25); } if (socket==null) Sys.exit(-1); v("connected client"); var input = socket.input; var output = socket.output; var buffer = Bytes.alloc(4); input.readBytes(buffer,0,4); v("client got " + buffer); output.writeBytes( Bytes.ofString( buffer.toString() + "pong" ), 0, 8); output.flush(); v("bye"); socket.shutdown(true,true); } public static function testSsl() : Int { log("Test ssl"); SslTest.socket_init(); return ok(); } public static function testSerialization() : Int { log("Test serialization"); var orig:haxe.Int64 = haxe.Int64.make(0xdeadbeef,0xbeefdead); var recon:haxe.Int64 = haxe.Unserializer.run(haxe.Serializer.run(orig)); if (orig!=recon) error('Bad Int64 serialization $orig != $recon'); return ok(); } // Hide from optimizer static function getOne() return 1; public static function testThread() { log("Test thread"); v("atomics.."); var a:AtomicInt = getOne(); var aPtr = cpp.Pointer.addressOf(a); if (aPtr.exchangeIf(2,3)) error("Bad exchageIf " + a); if (!aPtr.exchangeIf(1,3)) error("No exchageIf " + a); if (a!=3) error("Bad exchageIf value "); if (aPtr.atomicInc()!=3) error("Bad atomicInc return"); if (a!=4) error("Bad atomicInc value " + a); if (aPtr.atomicDec()!=4) error("Bad atomicDec return"); if (a!=3) error("Bad atomicDec value " + a); a = getOne(); v('deque a=$a'); var q = new Deque(); q.add(1); q.add(2); q.add(3); var mainThread = Thread.current(); // +100000 Thread.create(function() { q.pop(true); for(i in 0...100000) aPtr.atomicInc(); mainThread.sendMessage(null); }); // +100000 Thread.create(function() { q.pop(true); for(i in 0...100000) aPtr.atomicInc(); mainThread.sendMessage(null); } ); // -200000 Thread.create(function() { q.pop(true); for(i in 0...200000) aPtr.atomicDec(); mainThread.sendMessage(null); } ); v("wait for reply.."); for(i in 0...3) { Thread.readMessage(true); v('got $i'); } if (a!=1) error('Bad deque count : $a'); return ok(); } public static function testFloatReads() { log("Test float bytes"); var bytes =haxe.io.Bytes.alloc(1+4+8); bytes.fill(0,1,46); // Test unaligned read/write bytes.setFloat(1,1.25); bytes.setDouble(5,1.25); if (bytes.get(0)!=46) error("Bad byte 0"); if (bytes.getDouble(5)!=bytes.getFloat(1)) error("Bad byte read/write"); return ok(); } public dynamic function getX() return x; public static function testDynamicMember() { log("Test dynamic member"); var t = new Test(); if (t.getX()!=1) error("Bad dynamic member function"); return ok(); } @:noDebug public static function testNoDebug() { log("Test noDebug"); // Just testing to see it it compiles... if ( null == new haxe.io.BytesBuffer() ) error("Bad alloc"); return ok(); } @:noDebug public static function testNoDebugNoAlloc() { log("Test noDebug, no alloc"); return ok(); } public static function testIntParsing() { log("Test int parsing"); var val = Std.parseInt('0x1'); if (val != 1) { error('parsed hex value was not 1, $val'); } var val = Std.parseInt(' 0x1'); if (val != 1) { error('parsed hex value was not 1, $val'); } var val = Std.parseInt('\t0x1'); if (val != 1) { error('parsed hex value was not 1, $val'); } var val = Std.parseInt(' 0x'); if (val != 0) { error('parsed hex value was not 0, $val'); } var val = Std.parseInt(' 0xyz'); if (val != 0) { error('parsed hex value was not 0, $val'); } var val = Std.parseInt('-0x1'); if (val != -1) { error('parsed hex value was not 1, $val'); } var val = Std.parseInt(' -0x1'); if (val != -1) { error('parsed hex value was not 1, $val'); } var val = Std.parseInt('\t-0x1'); if (val != -1) { error('parsed hex value was not 1, $val'); } var val = Std.parseInt(' -0x'); if (val != 0) { error('parsed hex value was not 0, $val'); } var val = Std.parseInt(' -0xyz'); if (val != 0) { error('parsed hex value was not 0, $val'); } var val = Std.parseInt(' 5'); if (val != 5) { error('parsed int value was not 5, $val'); } var val = Std.parseInt(' \t\n5'); if (val != 5) { error('parsed int value was not 5, $val'); } return ok(); } public static function main() { var exitCode = 0; if (Sys.args().length>0) { runAsProcess(); } else { exitCode |= testDate(); exitCode |= testCompress(); exitCode |= testRegexp(); exitCode |= testRegexpMixedUnicode(); exitCode |= testSqlite(); exitCode |= testMysql(); exitCode |= testRandom(); exitCode |= testFile(); exitCode |= testFileSystem(); exitCode |= testHost(); exitCode |= testSys(); exitCode |= testCommand(); exitCode |= testPoll(); exitCode |= testUdpSocket(); exitCode |= testSocket(); exitCode |= testThread(); exitCode |= testSsl(); exitCode |= testSerialization(); exitCode |= testFloatReads(); exitCode |= testDynamicMember(); exitCode |= testNoDebug(); exitCode |= testNoDebugNoAlloc(); exitCode |= testIntParsing(); if (exitCode!=0) Sys.println("############# Errors running tests:\n " + errors.join("\n ") ); else Sys.println("All tests passed."); Sys.exit(exitCode); } } }