def test_emcc_ports(self): restore() # listing ports out = self.do([PYTHON, EMCC, '--show-ports']) assert 'Available ports:' in out, out assert 'SDL2' in out, out assert 'SDL2_image' in out, out # using ports INCLUDING_MESSAGE = 'including port' RETRIEVING_MESSAGE = 'retrieving port' BUILDING_MESSAGE = 'building port' from tools import system_libs PORTS_DIR = system_libs.Ports.get_dir() for i in [0, 1]: print i if i == 0: try_delete(PORTS_DIR) else: self.do([PYTHON, EMCC, '--clear-ports']) assert not os.path.exists(PORTS_DIR) if i == 0: Cache.erase() # test with cache erased and without # Building a file that doesn't need ports should not trigger anything output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp')]) assert INCLUDING_MESSAGE not in output assert RETRIEVING_MESSAGE not in output assert BUILDING_MESSAGE not in output assert not os.path.exists(PORTS_DIR) # Building a file that need a port does trigger stuff output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2']) assert INCLUDING_MESSAGE in output, output assert RETRIEVING_MESSAGE in output, output assert BUILDING_MESSAGE in output, output assert os.path.exists(PORTS_DIR) def second_use(): # Using it again avoids retrieve and build output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2']) assert INCLUDING_MESSAGE in output, output assert RETRIEVING_MESSAGE not in output, output assert BUILDING_MESSAGE not in output, output second_use() # if the version isn't sufficient, we retrieve and rebuild open(os.path.join(PORTS_DIR, 'sdl2', 'SDL2-master', 'version.txt'), 'w').write('1') # current is >= 2, so this is too old output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2']) assert INCLUDING_MESSAGE in output, output assert RETRIEVING_MESSAGE in output, output assert BUILDING_MESSAGE in output, output assert os.path.exists(PORTS_DIR) second_use()
def test_zzz_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long src = ( open(path_from_root("tests", "bullet", "Demos", "Benchmarks", "BenchmarkDemo.cpp"), "r").read() + open(path_from_root("tests", "bullet", "Demos", "Benchmarks", "main.cpp"), "r").read() ) def lib_builder(name, native, env_init): return self.get_library( "bullet", [ os.path.join("src", ".libs", "libBulletDynamics.a"), os.path.join("src", ".libs", "libBulletCollision.a"), os.path.join("src", ".libs", "libLinearMath.a"), ], configure_args=["--disable-demos", "--disable-dependency-tracking"], native=native, cache_name_extra=name, env_init=env_init, ) emcc_args = ["-s", 'DEAD_FUNCTIONS=["__ZSt9terminatev"]'] self.do_benchmark( "bullet", src, "\nok.\n", emcc_args=emcc_args, shared_args=[ "-I" + path_from_root("tests", "bullet", "src"), "-I" + path_from_root("tests", "bullet", "Demos", "Benchmarks"), ], lib_builder=lib_builder, )
def test_llvm_fastcomp(self): if os.environ.get('EMCC_FAST_COMPILER') != '1': return self.skip('not using fastcomp') WARNING = 'fastcomp in use, but LLVM has not been built with the JavaScript backend as a target' restore() # Should see js backend during sanity check assert check_fastcomp() output = self.check_working(EMCC) assert WARNING not in output, output # Fake incorrect llc output, no mention of js backend restore() f = open(CONFIG_FILE, 'a') f.write('LLVM_ROOT = "' + path_from_root('tests', 'fake') + '"') f.close() if not os.path.exists(path_from_root('tests', 'fake')): os.makedirs(path_from_root('tests', 'fake')) f = open(path_from_root('tests', 'fake', 'llc'), 'w') f.write('#!/bin/sh\n') f.write('echo "llc fake output\nRegistered Targets:\nno j-s backend for you!"') f.close() os.chmod(path_from_root('tests', 'fake', 'llc'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) output = self.check_working(EMCC, WARNING)
def test_nostdincxx(self): restore() Cache.erase() try: old = os.environ.get("EMCC_LLVM_TARGET") or "" for compiler in [EMCC, EMXX]: for target in ["i386-pc-linux-gnu", "le32-unknown-nacl"]: print compiler, target os.environ["EMCC_LLVM_TARGET"] = target out, err = Popen( [PYTHON, EMCC, path_from_root("tests", "hello_world.cpp"), "-v"], stdout=PIPE, stderr=PIPE ).communicate() out2, err2 = Popen( [PYTHON, EMCC, path_from_root("tests", "hello_world.cpp"), "-v", "-nostdinc++"], stdout=PIPE, stderr=PIPE, ).communicate() assert out == out2 def focus(e): assert "search starts here:" in e, e assert e.count("End of search list.") == 1, e return e[e.index("search starts here:") : e.index("End of search list.") + 20] err = focus(err) err2 = focus(err2) assert err == err2, err + "\n\n\n\n" + err2 finally: if old: os.environ["EMCC_LLVM_TARGET"] = old
def test_emconfig(self): restore() (fd, custom_config_filename) = tempfile.mkstemp(prefix=".emscripten_config_") orig_config = open(CONFIG_FILE, "r").read() # Move the ~/.emscripten to a custom location. tfile = os.fdopen(fd, "w") tfile.write(orig_config) tfile.close() # Make a syntax error in the original config file so that attempting to access it would fail. open(CONFIG_FILE, "w").write("asdfasdfasdfasdf\n'''" + orig_config) temp_dir = tempfile.mkdtemp(prefix="emscripten_temp_") os.chdir(temp_dir) self.do([EMCC, "-O2", "--em-config", custom_config_filename, path_from_root("tests", "hello_world.c")]) result = run_js("a.out.js") # Clean up created temp files. os.remove(custom_config_filename) os.chdir(path_from_root()) shutil.rmtree(temp_dir) self.assertContained("hello, world!", result)
def test_nodejs_sockets_echo(self): # This test checks that sockets work when the client code is run in Node.js # Run with ./runner.py sockets.test_nodejs_sockets_echo if not NODE_JS in JS_ENGINES: return self.skip('node is not present') sockets_include = '-I'+path_from_root('tests', 'sockets') # Websockify-proxied servers can't run dgram tests harnesses = [ # Websockify doesn't seem to like ws.WebSocket clients TODO check if this is a ws issue or Websockify issue #(WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 49160), 0), (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 49161), 0), (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 49162), 1) ] for harness, datagram in harnesses: with harness: Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', path_from_root('tests', 'sockets', 'client.js'), '-DSOCKK=%d' % harness.listen_port, '-DREPORT_RESULT=int dummy'], stdout=PIPE, stderr=PIPE).communicate() self.assertContained('do_msg_read: read 14 bytes', run_js(path_from_root('tests', 'sockets', 'client.js'), engine=NODE_JS)) # Tidy up files that might have been created by this test. try_delete(path_from_root('tests', 'sockets', 'client.js')) try_delete(path_from_root('tests', 'sockets', 'client.js.map'))
def test_emconfig(self): restore() (fd, custom_config_filename) = tempfile.mkstemp(prefix='.emscripten_config_') orig_config = open(CONFIG_FILE, 'r').read() # Move the ~/.emscripten to a custom location. tfile = os.fdopen(fd, "w") tfile.write(orig_config) tfile.close() # Make a syntax error in the original config file so that attempting to access it would fail. open(CONFIG_FILE, 'w').write('asdfasdfasdfasdf\n\'\'\'' + orig_config) temp_dir = tempfile.mkdtemp(prefix='emscripten_temp_') os.chdir(temp_dir) self.do([PYTHON, EMCC, '-O2', '--em-config', custom_config_filename, path_from_root('tests', 'hello_world.c')]) result = run_js('a.out.js') # Clean up created temp files. os.remove(custom_config_filename) os.chdir(path_from_root()) shutil.rmtree(temp_dir) self.assertContained('hello, world!', result)
def test_zzz_bullet( self ): # Called thus so it runs late in the alphabetical cycle... it is long src = open(path_from_root('tests', 'bullet', 'Demos', 'Benchmarks', 'BenchmarkDemo.cpp'), 'r').read() + \ open(path_from_root('tests', 'bullet', 'Demos', 'Benchmarks', 'main.cpp'), 'r').read() def lib_builder(name, native, env_init): return self.get_library( 'bullet', [ os.path.join('src', '.libs', 'libBulletDynamics.a'), os.path.join('src', '.libs', 'libBulletCollision.a'), os.path.join('src', '.libs', 'libLinearMath.a') ], configure_args=[ '--disable-demos', '--disable-dependency-tracking' ], native=native, cache_name_extra=name, env_init=env_init) emcc_args = ['-s', 'DEAD_FUNCTIONS=["__ZSt9terminatev"]'] self.do_benchmark( 'bullet', src, '\nok.\n', emcc_args=emcc_args, shared_args=[ '-I' + path_from_root('tests', 'bullet', 'src'), '-I' + path_from_root('tests', 'bullet', 'Demos', 'Benchmarks') ], lib_builder=lib_builder)
def test_nodejs_sockets_echo(self): # This test checks that sockets work when the client code is run in Node.js # Run with ./runner.py sockets.test_nodejs_sockets_echo if not NODE_JS in JS_ENGINES: return self.skip('node is not present') sockets_include = '-I'+path_from_root('tests', 'sockets') harnesses = [ (WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 59160), 0), (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 59162), 0), (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 59164), 1) ] harnesses = filter_harnesses(harnesses) # Basic test of node client against both a Websockified and compiled echo server. for harness, datagram in harnesses: with harness: Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', 'client.js', '-DSOCKK=%d' % harness.listen_port, '-DTEST_DGRAM=%d' % datagram, '-DREPORT_RESULT=int dummy'], stdout=PIPE, stderr=PIPE).communicate() out = run_js('client.js', engine=NODE_JS, full_output=True) self.assertContained('do_msg_read: read 14 bytes', out) # Test against a Websockified server with compile time configured WebSocket subprotocol. We use a Websockified # server because as long as the subprotocol list contains binary it will configure itself to accept binary # This test also checks that the connect url contains the correct subprotocols. print "\nTesting compile time WebSocket configuration.\n" for harness in filter_harnesses([ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 59166) ]): with harness: Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', 'client.js', '-s', 'SOCKET_DEBUG=1', '-s', 'WEBSOCKET_SUBPROTOCOL="base64, binary"', '-DSOCKK=59166', '-DREPORT_RESULT=int dummy'], stdout=PIPE, stderr=PIPE).communicate() out = run_js('client.js', engine=NODE_JS, full_output=True) self.assertContained('do_msg_read: read 14 bytes', out) self.assertContained(['connect: ws://127.0.0.1:59166, base64,binary', 'connect: ws://127.0.0.1:59166/, base64,binary'], out) # Test against a Websockified server with runtime WebSocket configuration. We specify both url and subprotocol. # In this test we have *deliberately* used the wrong port '-DSOCKK=12345' to configure the echo_client.c, so # the connection would fail without us specifying a valid WebSocket URL in the configuration. print "\nTesting runtime WebSocket configuration.\n" for harness in filter_harnesses([ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 59168) ]): with harness: open(os.path.join(self.get_dir(), 'websocket_pre.js'), 'w').write(''' var Module = { websocket: { url: 'ws://localhost:59168/testA/testB', subprotocol: 'text, base64, binary', } }; ''') Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', 'client.js', '--pre-js', 'websocket_pre.js', '-s', 'SOCKET_DEBUG=1', '-DSOCKK=12345', '-DREPORT_RESULT=int dummy'], stdout=PIPE, stderr=PIPE).communicate() out = run_js('client.js', engine=NODE_JS, full_output=True) self.assertContained('do_msg_read: read 14 bytes', out) self.assertContained('connect: ws://localhost:59168/testA/testB, text,base64,binary', out)
def test_zzz_zlib(self): src = open(path_from_root('tests', 'zlib', 'benchmark.c'), 'r').read() emcc_args = self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']) + \ ['-I' + path_from_root('tests', 'zlib')] native_args = self.get_library('zlib_native', os.path.join('libz.a'), make_args=['libz.a'], native=True) + \ ['-I' + path_from_root('tests', 'zlib')] self.do_benchmark('zlib', src, '''ok.''', force_c=True, emcc_args=emcc_args, native_args=native_args)
def test_sdl_audio_mix(self): shutil.copyfile(path_from_root('tests', 'sounds', 'pluck.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'music.ogg')) shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg')) open(os.path.join(self.get_dir(), 'sdl_audio_mix.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix.c')).read())) Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix.c'), '--preload-file', 'sound.ogg', '--preload-file', 'music.ogg', '--preload-file', 'noise.ogg', '-o', 'page.html']).communicate() self.run_browser('page.html', '', '/report_result?1')
def test_zzz_box2d(self): # Called thus so it runs late in the alphabetical cycle... it is long src = open(path_from_root('tests', 'box2d', 'Benchmark.cpp'), 'r').read() js_lib = self.get_library('box2d', [os.path.join('box2d.a')], configure=None) native_lib = self.get_library('box2d_native', [os.path.join('box2d.a')], configure=None, native=True) emcc_args = js_lib + ['-I' + path_from_root('tests', 'box2d')] native_args = native_lib + ['-I' + path_from_root('tests', 'box2d')] self.do_benchmark('box2d', src, 'frame averages', emcc_args=emcc_args, native_args=native_args)
def make_fake(report): f = open(CONFIG_FILE, 'a') f.write('LLVM_ROOT = "' + path_from_root('tests', 'fake', 'bin') + '"\n') f.close() f = open(path_from_root('tests', 'fake', 'bin', 'llc'), 'w') f.write('#!/bin/sh\n') f.write('echo "llc fake output\nRegistered Targets:\n%s"' % report) f.close() os.chmod(path_from_root('tests', 'fake', 'bin', 'llc'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
def test_sdl_audio(self): shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav')) shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg')) shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'the_entertainer.ogg')) open(os.path.join(self.get_dir(), 'bad.ogg'), 'w').write('I claim to be audio, but am lying') open(os.path.join(self.get_dir(), 'sdl_audio.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio.c')).read())) # use closure to check for a possible bug with closure minifying away newer Audio() attributes Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--embed-file', 'the_entertainer.ogg', '--preload-file', 'noise.ogg', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate() self.run_browser('page.html', '', '/report_result?1')
def test_node(self): NODE_WARNING = "node version appears too old" NODE_WARNING_2 = "cannot check node version" restore() # Clang should report the version number we expect, and emcc should not warn assert check_node_version() output = self.check_working(EMCC) assert NODE_WARNING not in output, output # Fake a different node version restore() f = open(CONFIG_FILE, "a") f.write('NODE_JS = "' + path_from_root("tests", "fake", "nodejs") + '"') f.close() if not os.path.exists(path_from_root("tests", "fake")): os.makedirs(path_from_root("tests", "fake")) try: os.environ["EM_IGNORE_SANITY"] = "1" for version, succeed in [ ("v0.7.9", False), ("v0.8.0", True), ("v0.8.1", True), ("v0.10.21-pre", True), ("cheez", False), ]: f = open(path_from_root("tests", "fake", "nodejs"), "w") f.write("#!/bin/sh\n") f.write( """if [ $1 = "--version" ]; then echo "%s" else %s $@ fi """ % (version, NODE_JS) ) f.close() os.chmod(path_from_root("tests", "fake", "nodejs"), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) if not succeed: if version[0] == "v": self.check_working(EMCC, NODE_WARNING) else: self.check_working(EMCC, NODE_WARNING_2) else: output = self.check_working(EMCC) assert NODE_WARNING not in output, output finally: del os.environ["EM_IGNORE_SANITY"]
def test_zzz_java_nbody( self ): # tests xmlvm compiled java, including bitcasts of doubles, i64 math, etc. if CORE_BENCHMARKS: return args = [path_from_root('tests', 'nbody-java', x) for x in os.listdir(path_from_root('tests', 'nbody-java')) if x.endswith('.c')] + \ ['-I' + path_from_root('tests', 'nbody-java')] self.do_benchmark( 'nbody_java', '', '''Time(s)''', force_c=True, emcc_args=args + ['-s', 'PRECISE_I64_MATH=1', '--llvm-lto', '2'], native_args=args + ['-lgc', '-std=c99', '-target', 'x86_64-pc-linux-gnu', '-lm'])
def test_enet(self): try_delete(self.in_dir('enet')) shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet')) pwd = os.getcwd() os.chdir(self.in_dir('enet')) Popen([PYTHON, path_from_root('emconfigure'), './configure']).communicate() Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] os.chdir(pwd) for harness in [ CompiledServerHarness(os.path.join('sockets', 'test_enet_server.c'), enet, 49210) ]: with harness: self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=enet + ['-DSOCKK=%d' % harness.listen_port])
def test_node(self): NODE_WARNING = 'node version appears too old' NODE_WARNING_2 = 'cannot check node version' restore_and_set_up() # Clang should report the version number we expect, and emcc should not warn assert check_node_version() output = self.check_working(EMCC) assert NODE_WARNING not in output, output # Fake a different node version restore_and_set_up() f = open(CONFIG_FILE, 'a') f.write('NODE_JS = "' + path_from_root('tests', 'fake', 'nodejs') + '"') f.close() if not os.path.exists(path_from_root('tests', 'fake')): os.makedirs(path_from_root('tests', 'fake')) try: os.environ['EM_IGNORE_SANITY'] = '1' for version, succeed in [('v0.8.0', False), ('v4.1.0', False), ('v4.1.1', True), ('v4.2.3-pre', True), ('cheez', False)]: print(version, succeed) f = open(path_from_root('tests', 'fake', 'nodejs'), 'w') f.write('#!/bin/sh\n') f.write('''if [ $1 = "--version" ]; then echo "%s" else %s $@ fi ''' % (version, NODE_JS)) f.close() os.chmod(path_from_root('tests', 'fake', 'nodejs'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) if not succeed: if version[0] == 'v': self.check_working(EMCC, NODE_WARNING) else: self.check_working(EMCC, NODE_WARNING_2) else: output = self.check_working(EMCC) assert NODE_WARNING not in output, output finally: del os.environ['EM_IGNORE_SANITY']
def __enter__(self): import socket, websockify # compile the server Popen([PYTHON, EMCC, path_from_root('tests', self.filename), '-o', 'server.js'] + self.args).communicate() process = Popen([NODE_JS, 'server.js']) self.pids.append(process.pid)
def test_relooper(self): RELOOPER = Cache.get_path("relooper.js") restore() for phase in range(2): # 0: we wipe the relooper dir. 1: we have it, so should just update if phase == 0: Cache.erase() try_delete(RELOOPER) for i in range(4): print >> sys.stderr, phase, i opt = min(i, 2) try_delete("a.out.js") output = Popen( [PYTHON, EMCC, path_from_root("tests", "hello_world_loop.cpp"), "-O" + str(opt), "-g"], stdout=PIPE, stderr=PIPE, ).communicate() self.assertContained("hello, world!", run_js("a.out.js")) output = "\n".join(output) assert ("bootstrapping relooper succeeded" in output) == (i == 1), ( "only bootstrap on first O2: " + output ) assert os.path.exists(RELOOPER) == (i >= 1), "have relooper on O2: " + output src = open("a.out.js").read() main = src.split("function _main()")[1].split("\n}\n")[0] assert ( "while (1) {" in main or "while(1){" in main or "while(1) {" in main or "} while ($" in main or "}while($" in main ) == (i >= 1), ("reloop code on O2: " + main) assert ("switch" not in main) == (i >= 1), "reloop code on O2: " + main
def lua(self, benchmark, expected, output_parser=None, args_processor=None): shutil.copyfile(path_from_root("tests", "lua", benchmark + ".lua"), benchmark + ".lua") def lib_builder(name, native, env_init): ret = self.get_library( "lua_native" if native else "lua", [os.path.join("src", "lua"), os.path.join("src", "liblua.a")], make=["make", "generic"], configure=None, native=native, cache_name_extra=name, env_init=env_init, ) if native: return ret shutil.copyfile(ret[0], ret[0] + ".bc") ret[0] += ".bc" return ret self.do_benchmark( "lua_" + benchmark, "", expected, force_c=True, args=[benchmark + ".lua", DEFAULT_ARG], emcc_args=["--embed-file", benchmark + ".lua"], lib_builder=lib_builder, native_exec=os.path.join("building", "lua_native", "src", "lua"), output_parser=output_parser, args_processor=args_processor, )
def test_zzz_java_nbody(self): # tests xmlvm compiled java, including bitcasts of doubles, i64 math, etc. if CORE_BENCHMARKS: return args = [ path_from_root("tests", "nbody-java", x) for x in os.listdir(path_from_root("tests", "nbody-java")) if x.endswith(".c") ] + ["-I" + path_from_root("tests", "nbody-java")] self.do_benchmark( "nbody_java", "", """Time(s)""", force_c=True, emcc_args=args + ["-s", "PRECISE_I64_MATH=1", "--llvm-lto", "2"], native_args=args + ["-lgc", "-std=c99", "-target", "x86_64-pc-linux-gnu", "-lm"], )
def __enter__(self): import socket, websockify # compile the server # NOTE empty filename support is a hack to support # the current test_enet if self.filename: Popen( [CLANG_CC, path_from_root("tests", self.filename), "-o", "server", "-DSOCKK=%d" % self.target_port] + get_clang_native_args() + self.args ).communicate() process = Popen([os.path.abspath("server")]) self.pids.append(process.pid) # start the websocket proxy print >> sys.stderr, "running websockify on %d, forward to tcp %d" % (self.listen_port, self.target_port) wsp = websockify.WebSocketProxy( verbose=True, listen_port=self.listen_port, target_host="127.0.0.1", target_port=self.target_port, run_once=True, ) self.websockify = multiprocessing.Process(target=wsp.start_server) self.websockify.start() self.pids.append(self.websockify.pid) print "[Websockify on process %s]" % str(self.pids[-2:])
def test_firstrun(self): for command in commands: wipe() def make_executable(name): with open(os.path.join(temp_bin, name), 'w') as f: os.fchmod(f.fileno(), stat.S_IRWXU) try: temp_bin = tempfile.mkdtemp() old_environ_path = os.environ['PATH'] os.environ['PATH'] = temp_bin + os.pathsep + old_environ_path make_executable('llvm-dis') make_executable('node') make_executable('python2') output = self.do(command) finally: os.environ['PATH'] = old_environ_path shutil.rmtree(temp_bin) self.assertContained('Welcome to Emscripten!', output) self.assertContained('This is the first time any of the Emscripten tools has been run.', output) self.assertContained('A settings file has been copied to %s, at absolute path: %s' % (EM_CONFIG, CONFIG_FILE), output) self.assertContained('It contains our best guesses for the important paths, which are:', output) self.assertContained('LLVM_ROOT', output) self.assertContained('NODE_JS', output) if platform.system() is not 'Windows': # os.chmod can't make files executable on Windows self.assertIdentical(temp_bin, re.search("^ *LLVM_ROOT *= (.*)$", output, re.M).group(1)) self.assertIdentical(os.path.join(temp_bin, 'node'), re.search("^ *NODE_JS *= (.*)$", output, re.M).group(1)) self.assertContained('Please edit the file if any of those are incorrect', output) self.assertContained('This command will now exit. When you are done editing those paths, re-run it.', output) assert output.split()[-1].endswith('===='), 'We should have stopped: ' + output config_file = open(CONFIG_FILE).read() template_file = open(path_from_root('tools', 'settings_template_readonly.py')).read() self.assertNotContained('~/.emscripten', config_file) self.assertContained('~/.emscripten', template_file) self.assertNotContained('{{{', config_file) self.assertNotContained('}}}', config_file) self.assertContained('{{{', template_file) self.assertContained('}}}', template_file) for content in ['EMSCRIPTEN_ROOT', 'LLVM_ROOT', 'NODE_JS', 'TEMP_DIR', 'COMPILER_ENGINE', 'JS_ENGINES']: self.assertContained(content, config_file) # The guessed config should be ok XXX This depends on your local system! it is possible `which` guesses wrong #try_delete('a.out.js') #output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c')], stdout=PIPE, stderr=PIPE).communicate() #self.assertContained('hello, world!', run_js('a.out.js'), output) # Second run, with bad EM_CONFIG for settings in ['blah', 'LLVM_ROOT="blarg"; JS_ENGINES=[]; COMPILER_ENGINE=NODE_JS=SPIDERMONKEY_ENGINE=[]']: f = open(CONFIG_FILE, 'w') f.write(settings) f.close() output = self.do(command) if 'LLVM_ROOT' not in settings: self.assertContained('Error in evaluating %s' % EM_CONFIG, output) elif 'runner.py' not in ' '.join(command): self.assertContained('CRITICAL', output) # sanity check should fail
def test_relooper(self): assert os.environ.get('EMCC_FAST_COMPILER') is None try: os.environ['EMCC_FAST_COMPILER'] = '0' RELOOPER = Cache.get_path('relooper.js') restore() for phase in range(2): # 0: we wipe the relooper dir. 1: we have it, so should just update if phase == 0: Cache.erase() try_delete(RELOOPER) for i in range(4): print >> sys.stderr, phase, i opt = min(i, 2) try_delete('a.out.js') output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop.cpp'), '-O' + str(opt), '-g'], stdout=PIPE, stderr=PIPE).communicate() self.assertContained('hello, world!', run_js('a.out.js')) output = '\n'.join(output) assert ('bootstrapping relooper succeeded' in output) == (i == 1), 'only bootstrap on first O2: ' + output assert os.path.exists(RELOOPER) == (i >= 1), 'have relooper on O2: ' + output src = open('a.out.js').read() main = src.split('function _main()')[1].split('\n}\n')[0] assert ('while (1) {' in main or 'while(1){' in main or 'while(1) {' in main or '} while ($' in main or '}while($' in main) == (i >= 1), 'reloop code on O2: ' + main assert ('switch' not in main) == (i >= 1), 'reloop code on O2: ' + main finally: del os.environ['EMCC_FAST_COMPILER']
def test_sdl_audio_panning(self): shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.wav'), os.path.join(self.get_dir(), 'the_entertainer.wav')) open(os.path.join(self.get_dir(), 'sdl_audio_panning.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_panning.c')).read())) # use closure to check for a possible bug with closure minifying away newer Audio() attributes Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_panning.c'), '--preload-file', 'the_entertainer.wav', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play"]']).communicate() self.run_browser('page.html', '', '/report_result?1')
def test_nostdincxx(self): restore() Cache.erase() for compiler in [EMCC, EMXX]: print compiler out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '-v'], stdout=PIPE, stderr=PIPE).communicate() out2, err2 = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '-v', '-nostdinc++'], stdout=PIPE, stderr=PIPE).communicate() assert out == out2 def focus(e): assert 'search starts here:' in e, e assert e.count('End of search list.') == 1, e return e[e.index('search starts here:'):e.index('End of search list.')+20] err = focus(err) err2 = focus(err2) assert err == err2, err + '\n\n\n\n' + err2
def lua(self, benchmark, expected, output_parser=None, args_processor=None): shutil.copyfile( path_from_root('tests', 'lua', benchmark + '.lua'), benchmark + '.lua') def lib_builder(name, native, env_init): ret = self.get_library( 'lua_native' if native else 'lua', [os.path.join('src', 'lua'), os.path.join('src', 'liblua.a')], make=['make', 'generic'], configure=None, native=native, cache_name_extra=name, env_init=env_init) if native: return ret shutil.copyfile(ret[0], ret[0] + '.bc') ret[0] += '.bc' return ret self.do_benchmark( 'lua_' + benchmark, '', expected, force_c=True, args=[benchmark + '.lua', DEFAULT_ARG], emcc_args=['--embed-file', benchmark + '.lua'], lib_builder=lib_builder, native_exec=os.path.join('building', 'lua_native', 'src', 'lua'), output_parser=output_parser, args_processor=args_processor)
def test_d8_path(self): """ Test that running JS commands works for node, d8, and jsc and is not path dependent """ # Fake some JS engines restore() sample_script = path_from_root('tests', 'print_args.js') # Note that the path contains 'd8'. test_path = path_from_root('tests', 'fake', 'abcd8765') if not os.path.exists(test_path): os.makedirs(test_path) try: os.environ['EM_IGNORE_SANITY'] = '1' jsengines = [('d8', V8_ENGINE), ('d8_g', V8_ENGINE), ('js', SPIDERMONKEY_ENGINE), ('node', NODE_JS), ('nodejs', NODE_JS)] for filename, engine in jsengines: if type(engine) is list: engine = engine[0] if engine == '': print 'WARNING: Not testing engine %s, not configured.' % (filename) continue print filename, engine test_engine_path = os.path.join(test_path, filename) f = open(test_engine_path, 'w') f.write('#!/bin/sh\n') f.write('%s $@\n' % (engine)) f.close() os.chmod(test_engine_path, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) try: out = jsrun.run_js(sample_script, engine=test_engine_path, args=['--foo'], full_output=True, assert_returncode=0) except Exception as e: if 'd8' in filename: assert False, 'Your d8 version does not correctly parse command-line arguments, please upgrade or delete from ~/.emscripten config file: %s' % (e) else: assert False, 'Error running script command: %s' % (e) self.assertEqual('0: --foo', out.strip()) finally: del os.environ['EM_IGNORE_SANITY']
def test_sockets_async_echo(self): # Run with ./runner.py sockets.test_sockets_async_echo sockets_include = "-I" + path_from_root("tests", "sockets") # Websockify-proxied servers can't run dgram tests harnesses = [ ( WebsockifyServerHarness( os.path.join("sockets", "test_sockets_echo_server.c"), [sockets_include, "-DTEST_ASYNC=1"], 49165 ), 0, ), ( CompiledServerHarness( os.path.join("sockets", "test_sockets_echo_server.c"), [sockets_include, "-DTEST_DGRAM=0", "-DTEST_ASYNC=1"], 49166, ), 0, ), ( CompiledServerHarness( os.path.join("sockets", "test_sockets_echo_server.c"), [sockets_include, "-DTEST_DGRAM=1", "-DTEST_ASYNC=1"], 49167, ), 1, ), # The following forces non-NULL addr and addlen parameters for the accept call ( CompiledServerHarness( os.path.join("sockets", "test_sockets_echo_server.c"), [sockets_include, "-DTEST_DGRAM=0", "-DTEST_ACCEPT_ADDR=1", "-DTEST_ASYNC=1"], 49168, ), 0, ), ] # harnesses = filter_harnesses(harnesses) for harness, datagram in harnesses: with harness: self.btest( os.path.join("sockets", "test_sockets_echo_client.c"), expected="0", args=[ "-DSOCKK=%d" % harness.listen_port, "-DTEST_DGRAM=%d" % datagram, "-DTEST_ASYNC=1", sockets_include, ], ) # Deliberately attempt a connection on a port that will fail to test the error callback and getsockopt self.btest( os.path.join("sockets", "test_sockets_echo_client.c"), expected="0", args=["-DSOCKK=49169", "-DTEST_ASYNC=1", sockets_include], )
def test_nodejs_sockets_echo(self): # This test checks that sockets work when the client code is run in Node.js # Run with ./runner.py sockets.test_nodejs_sockets_echo if not NODE_JS in JS_ENGINES: return self.skip('node is not present') sockets_include = '-I' + path_from_root('tests', 'sockets') # Websockify-proxied servers can't run dgram tests harnesses = [ # Websockify doesn't seem to like ws.WebSocket clients TODO check if this is a ws issue or Websockify issue #(WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 49160), 0), (CompiledServerHarness( os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 49161), 0), (CompiledServerHarness( os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 49162), 1) ] for harness, datagram in harnesses: with harness: Popen([ PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', path_from_root('tests', 'sockets', 'client.js'), '-DSOCKK=%d' % harness.listen_port, '-DREPORT_RESULT=int dummy' ], stdout=PIPE, stderr=PIPE).communicate() self.assertContained( 'do_msg_read: read 14 bytes', run_js(path_from_root('tests', 'sockets', 'client.js'), engine=NODE_JS)) # Tidy up files that might have been created by this test. try_delete(path_from_root('tests', 'sockets', 'client.js')) try_delete(path_from_root('tests', 'sockets', 'client.js.map'))
import time import re import tempfile from subprocess import PIPE, STDOUT from runner import RunnerCore, path_from_root, env_modify, test_file from runner import create_file, ensure_dir, make_executable from tools.config import config_file, EM_CONFIG from tools.shared import EMCC from tools.shared import CANONICAL_TEMP_DIR from tools.shared import try_delete, config from tools.shared import EXPECTED_LLVM_VERSION, Cache from tools import shared, system_libs, utils SANITY_FILE = shared.Cache.get_path('sanity.txt') commands = [[EMCC], [path_from_root('tests', 'runner'), 'blahblah']] def restore(): shutil.copyfile(config_file + '_backup', config_file) # restore the config file and set it up for our uses def restore_and_set_up(): restore() with open(config_file, 'a') as f: # make LLVM_ROOT sensitive to the LLVM env var, as we test that f.write('LLVM_ROOT = "%s"\n' % config.LLVM_ROOT) # unfreeze the cache, so we can test that f.write('FROZEN_CACHE = False\n')
def test_openal_playback(self): shutil.copyfile(path_from_root('tests', 'sounds', 'audio.wav'), os.path.join(self.get_dir(), 'audio.wav')) open(os.path.join(self.get_dir(), 'openal_playback.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'openal_playback.cpp')).read())) Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'openal_playback.cpp'), '--preload-file', 'audio.wav', '-o', 'page.html']).communicate() self.run_browser('page.html', '', '/report_result?1')
def test_getnameinfo(self): self.do_run( open(path_from_root('tests', 'sockets', 'test_getnameinfo.c')).read(), 'success')
def test_firstrun(self): for command in commands: wipe() def make_executable(name): with open(os.path.join(temp_bin, name), 'w') as f: os.fchmod(f.fileno(), stat.S_IRWXU) try: temp_bin = tempfile.mkdtemp() old_environ_path = os.environ['PATH'] os.environ['PATH'] = temp_bin + os.pathsep + old_environ_path make_executable('llvm-dis') make_executable('node') make_executable('python2') output = self.do(command) finally: os.environ['PATH'] = old_environ_path shutil.rmtree(temp_bin) self.assertContained('Welcome to Emscripten!', output) self.assertContained( 'This is the first time any of the Emscripten tools has been run.', output) self.assertContained( 'A settings file has been copied to %s, at absolute path: %s' % (EM_CONFIG, CONFIG_FILE), output) self.assertContained( 'It contains our best guesses for the important paths, which are:', output) self.assertContained('LLVM_ROOT', output) self.assertContained('NODE_JS', output) if platform.system() is not 'Windows': # os.chmod can't make files executable on Windows self.assertIdentical( temp_bin, re.search("^ *LLVM_ROOT *= (.*)$", output, re.M).group(1)) self.assertIdentical( os.path.join(temp_bin, 'node'), re.search("^ *NODE_JS *= (.*)$", output, re.M).group(1)) self.assertContained( 'Please edit the file if any of those are incorrect', output) self.assertContained( 'This command will now exit. When you are done editing those paths, re-run it.', output) assert output.split()[-1].endswith( '===='), 'We should have stopped: ' + output config_file = open(CONFIG_FILE).read() template_file = open( path_from_root('tools', 'settings_template_readonly.py')).read() self.assertNotContained('~/.emscripten', config_file) self.assertContained('~/.emscripten', template_file) self.assertNotContained('{{{', config_file) self.assertNotContained('}}}', config_file) self.assertContained('{{{', template_file) self.assertContained('}}}', template_file) for content in [ 'EMSCRIPTEN_ROOT', 'LLVM_ROOT', 'NODE_JS', 'TEMP_DIR', 'COMPILER_ENGINE', 'JS_ENGINES' ]: self.assertContained(content, config_file) # The guessed config should be ok XXX This depends on your local system! it is possible `which` guesses wrong #try_delete('a.out.js') #output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c')], stdout=PIPE, stderr=PIPE).communicate() #self.assertContained('hello, world!', run_js('a.out.js'), output) # Second run, with bad EM_CONFIG for settings in [ 'blah', 'LLVM_ROOT="blarg"; JS_ENGINES=[]; COMPILER_ENGINE=NODE_JS=SPIDERMONKEY_ENGINE=[]' ]: f = open(CONFIG_FILE, 'w') f.write(settings) f.close() output = self.do(command) if 'LLVM_ROOT' not in settings: self.assertContained('Error in evaluating %s' % EM_CONFIG, output) elif 'runner.py' not in ' '.join(command): self.assertContained('CRITICAL', output) # sanity check should fail
def test_emcc_ports(self): restore() # listing ports out = self.do([PYTHON, EMCC, '--show-ports']) assert 'Available ports:' in out, out assert 'SDL2' in out, out assert 'SDL2_image' in out, out # using ports RETRIEVING_MESSAGE = 'retrieving port' BUILDING_MESSAGE = 'building port' from tools import system_libs PORTS_DIR = system_libs.Ports.get_dir() for compiler in [EMCC, EMXX]: print compiler for i in [0, 1]: print i if i == 0: try_delete(PORTS_DIR) else: self.do([PYTHON, compiler, '--clear-ports']) assert not os.path.exists(PORTS_DIR) if i == 0: Cache.erase() # test with cache erased and without # Building a file that doesn't need ports should not trigger anything output = self.do( [compiler, path_from_root('tests', 'hello_world_sdl.cpp')]) assert RETRIEVING_MESSAGE not in output assert BUILDING_MESSAGE not in output assert not os.path.exists(PORTS_DIR) # Building a file that need a port does trigger stuff output = self.do([ compiler, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2' ]) assert RETRIEVING_MESSAGE in output, output assert BUILDING_MESSAGE in output, output assert os.path.exists(PORTS_DIR) def second_use(): # Using it again avoids retrieve and build output = self.do([ compiler, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2' ]) assert RETRIEVING_MESSAGE not in output, output assert BUILDING_MESSAGE not in output, output second_use() # if the version isn't sufficient, we retrieve and rebuild subdir = os.listdir(os.path.join(PORTS_DIR, 'sdl2'))[0] os.rename(os.path.join(PORTS_DIR, 'sdl2', subdir), os.path.join(PORTS_DIR, 'sdl2', 'old-subdir')) import zipfile z = zipfile.ZipFile(os.path.join(PORTS_DIR, 'sdl2' + '.zip'), 'w') if not os.path.exists('old-sub'): os.mkdir('old-sub') open(os.path.join('old-sub', 'a.txt'), 'w').write('waka') open(os.path.join('old-sub', 'b.txt'), 'w').write('waka') z.write(os.path.join('old-sub', 'a.txt')) z.write(os.path.join('old-sub', 'b.txt')) z.close() output = self.do([ compiler, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2' ]) assert RETRIEVING_MESSAGE in output, output assert BUILDING_MESSAGE in output, output assert os.path.exists(PORTS_DIR) second_use()
def test_emcc_caching(self): INCLUDING_MESSAGE = 'including X' BUILDING_MESSAGE = 'building X for cache' ERASING_MESSAGE = 'clearing cache' EMCC_CACHE = Cache.dirname for compiler in [EMCC, EMXX]: print compiler restore() Cache.erase() assert not os.path.exists(EMCC_CACHE) try: os.environ['EMCC_DEBUG'] = '1' self.working_dir = os.path.join(TEMP_DIR, 'emscripten_temp') # Building a file that doesn't need cached stuff should not trigger cache generation output = self.do( [compiler, path_from_root('tests', 'hello_world.cpp')]) assert INCLUDING_MESSAGE.replace('X', 'libcextra') not in output assert BUILDING_MESSAGE.replace('X', 'libcextra') not in output self.assertContained('hello, world!', run_js('a.out.js')) try_delete('a.out.js') basebc_name = os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-0-basebc.bc') dcebc_name = os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-1-linktime.bc') ll_names = [ os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-X-ll.ll').replace('X', str(x)) for x in range(2, 5) ] # Building a file that *does* need something *should* trigger cache generation, but only the first time for filename, libname in [('hello_libcxx.cpp', 'libcxx')]: for i in range(3): print filename, libname, i self.clear() try_delete(basebc_name ) # we might need to check this file later try_delete(dcebc_name ) # we might need to check this file later for ll_name in ll_names: try_delete(ll_name) output = self.do([ compiler, '-O' + str(i), '-s', 'RELOOP=0', '--llvm-lto', '0', path_from_root('tests', filename), '--save-bc', 'a.bc' ]) #print output assert INCLUDING_MESSAGE.replace('X', libname) in output if libname == 'libc': assert INCLUDING_MESSAGE.replace( 'X', 'libcxx' ) not in output # we don't need libcxx in this code else: assert INCLUDING_MESSAGE.replace( 'X', 'libc' ) in output # libcxx always forces inclusion of libc assert (BUILDING_MESSAGE.replace('X', libname) in output) == ( i == 0), 'Must only build the first time' self.assertContained('hello, world!', run_js('a.out.js')) assert os.path.exists(EMCC_CACHE) assert os.path.exists( os.path.join(EMCC_CACHE, libname + '.bc')) if libname == 'libcxx': print os.stat( os.path.join(EMCC_CACHE, libname + '.bc')).st_size, os.stat( basebc_name).st_size, os.stat( dcebc_name).st_size assert os.stat( os.path.join( EMCC_CACHE, libname + '.bc')).st_size > 1000000, 'libc++ is big' assert os.stat( basebc_name ).st_size > 1000000, 'libc++ is indeed big' assert os.stat(dcebc_name).st_size < os.stat( basebc_name ).st_size * 0.666, 'Dead code elimination must remove most of libc++' finally: del os.environ['EMCC_DEBUG'] restore() def ensure_cache(): self.do([ PYTHON, EMCC, '-O2', path_from_root('tests', 'hello_world.c') ]) # Manual cache clearing ensure_cache() assert os.path.exists(EMCC_CACHE) output = self.do([PYTHON, EMCC, '--clear-cache']) assert ERASING_MESSAGE in output assert not os.path.exists(EMCC_CACHE) assert SANITY_MESSAGE in output # Changing LLVM_ROOT, even without altering .emscripten, clears the cache ensure_cache() old = os.environ.get('LLVM') try: os.environ['LLVM'] = 'waka' assert os.path.exists(EMCC_CACHE) output = self.do([PYTHON, EMCC]) assert ERASING_MESSAGE in output assert not os.path.exists(EMCC_CACHE) finally: if old: os.environ['LLVM'] = old else: del os.environ['LLVM'] try_delete(CANONICAL_TEMP_DIR)
def test_llvm_fastcomp(self): assert os.environ.get( 'EMCC_FAST_COMPILER' ) != '0', 'must be using fastcomp to test fastcomp' WARNING = 'fastcomp in use, but LLVM has not been built with the JavaScript backend as a target' WARNING2 = 'you can fall back to the older (pre-fastcomp) compiler core, although that is not recommended, see https://github.com/kripken/emscripten/wiki/LLVM-Backend' restore() # Should see js backend during sanity check assert check_fastcomp() output = self.check_working(EMCC) assert WARNING not in output, output assert WARNING2 not in output, output # Fake incorrect llc output, no mention of js backend restore() f = open(CONFIG_FILE, 'a') f.write('LLVM_ROOT = "' + path_from_root('tests', 'fake', 'bin') + '"') f.close() #print '1', open(CONFIG_FILE).read() try_delete(path_from_root('tests', 'fake')) os.makedirs(path_from_root('tests', 'fake', 'bin')) f = open(path_from_root('tests', 'fake', 'bin', 'llc'), 'w') f.write('#!/bin/sh\n') f.write( 'echo "llc fake output\nRegistered Targets:\nno j-s backend for you!"' ) f.close() os.chmod(path_from_root('tests', 'fake', 'bin', 'llc'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) output = self.check_working(EMCC, WARNING) output = self.check_working(EMCC, WARNING2) # fake some more for fake in [ 'llvm-link', 'clang', 'clang++', 'llvm-ar', 'opt', 'llvm-as', 'llvm-dis', 'llvm-nm', 'lli' ]: open(path_from_root('tests', 'fake', 'bin', fake), 'w').write('.') try_delete(SANITY_FILE) output = self.check_working(EMCC, WARNING) # make sure sanity checks notice there is no source dir with version # open(path_from_root('tests', 'fake', 'bin', 'llc'), 'w').write( '#!/bin/sh\necho "Registered Targets: there IZ a js backend: JavaScript (asm.js, emscripten) backend"' ) open(path_from_root('tests', 'fake', 'bin', 'clang++'), 'w').write( '#!/bin/sh\necho "clang version %s (blah blah)" >&2\necho "..." >&2\n' % '.'.join(map(str, EXPECTED_LLVM_VERSION))) os.chmod(path_from_root('tests', 'fake', 'bin', 'llc'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) os.chmod(path_from_root('tests', 'fake', 'bin', 'clang++'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) try_delete(SANITY_FILE) output = self.check_working( EMCC, 'did not see a source tree above or next to the LLVM root directory' ) VERSION_WARNING = 'Emscripten, llvm and clang versions do not match, this is dangerous' # add version number open(path_from_root('tests', 'fake', 'emscripten-version.txt'), 'w').write('waka') try_delete(SANITY_FILE) output = self.check_working(EMCC, VERSION_WARNING) os.makedirs(path_from_root('tests', 'fake', 'tools', 'clang')) open( path_from_root('tests', 'fake', 'tools', 'clang', 'emscripten-version.txt'), 'w').write(EMSCRIPTEN_VERSION) try_delete(SANITY_FILE) output = self.check_working(EMCC, VERSION_WARNING) open(path_from_root('tests', 'fake', 'emscripten-version.txt'), 'w').write(EMSCRIPTEN_VERSION) try_delete(SANITY_FILE) output = self.check_working(EMCC) assert VERSION_WARNING not in output open( path_from_root('tests', 'fake', 'tools', 'clang', 'emscripten-version.txt'), 'w').write('waka') try_delete(SANITY_FILE) output = self.check_working(EMCC, VERSION_WARNING) restore() self.check_working([ EMCC, 'tests/hello_world.cpp', '-s', 'INIT_HEAP=1' ], '''Compiler settings are incompatible with fastcomp. You can fall back to the older compiler core, although that is not recommended''' )
def test_html5_pointerlockerror(self): self.btest( path_from_root('tests', 'test_html5_pointerlockerror.c'), expected='0', args=['-s', 'DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1'])
def test_vr(self): self.btest(path_from_root('tests', 'test_vr.c'), expected='0')
def test_emcc_ports(self): restore_and_set_up() # listing ports out = self.do([EMCC, '--show-ports']) assert 'Available ports:' in out, out assert 'SDL2' in out, out assert 'SDL2_image' in out, out assert 'SDL2_net' in out, out # using ports RETRIEVING_MESSAGE = 'retrieving port' BUILDING_MESSAGE = 'generating port' PORTS_DIR = system_libs.Ports.get_dir() for i in [0, 1]: self.do([EMCC, '--clear-cache']) print(i) if i == 0: try_delete(PORTS_DIR) else: self.do([EMCC, '--clear-ports']) assert not os.path.exists(PORTS_DIR) # Building a file that doesn't need ports should not trigger anything output = self.do( [EMCC, path_from_root('tests', 'hello_world_sdl.cpp')]) assert RETRIEVING_MESSAGE not in output, output assert BUILDING_MESSAGE not in output print('no', output) assert not os.path.exists(PORTS_DIR) def first_use(): output = self.do([ EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'WASM=0', '-s', 'USE_SDL=2' ]) assert RETRIEVING_MESSAGE in output, output assert BUILDING_MESSAGE in output, output self.assertExists(PORTS_DIR) print('yes', output) def second_use(): # Using it again avoids retrieve and build output = self.do([ EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'WASM=0', '-s', 'USE_SDL=2' ]) assert RETRIEVING_MESSAGE not in output, output assert BUILDING_MESSAGE not in output, output # Building a file that need a port does trigger stuff first_use() second_use() # if the tag doesn't match, we retrieve and rebuild subdir = os.listdir(os.path.join(PORTS_DIR, 'sdl2'))[0] os.rename(os.path.join(PORTS_DIR, 'sdl2', subdir), os.path.join(PORTS_DIR, 'sdl2', 'old-subdir')) ensure_dir('old-sub') open(os.path.join('old-sub', 'a.txt'), 'w').write('waka') open(os.path.join('old-sub', 'b.txt'), 'w').write('waka') with zipfile.ZipFile(os.path.join(PORTS_DIR, 'sdl2.zip'), 'w') as z: z.write(os.path.join('old-sub', 'a.txt')) z.write(os.path.join('old-sub', 'b.txt')) first_use() second_use()
import time import re import tempfile import zipfile from subprocess import PIPE, STDOUT from runner import RunnerCore, path_from_root, env_modify, chdir from runner import create_test_file, no_wasm_backend, ensure_dir from tools.shared import NODE_JS, PYTHON, EMCC, SPIDERMONKEY_ENGINE, V8_ENGINE from tools.shared import CONFIG_FILE, EM_CONFIG, LLVM_ROOT, CANONICAL_TEMP_DIR from tools.shared import run_process, try_delete, run_js, safe_ensure_dirs from tools.shared import expected_llvm_version, Cache, Settings from tools import jsrun, shared, system_libs SANITY_FILE = CONFIG_FILE + '_sanity' commands = [[EMCC], [PYTHON, path_from_root('tests', 'runner.py'), 'blahblah']] def restore(): shutil.copyfile(CONFIG_FILE + '_backup', CONFIG_FILE) # restore the config file and set it up for our uses def restore_and_set_up(): restore() with open(CONFIG_FILE, 'a') as f: # don't use the native optimizer from the emsdk - we want to test how it builds f.write('\nEMSCRIPTEN_NATIVE_OPTIMIZER = ""\n') # make LLVM_ROOT sensitive to the LLVM env var, as we test that f.write('LLVM_ROOT = "%s"\n' % LLVM_ROOT) # unfreeze the cache, so we can test that
def test_skinning(self): src = open(path_from_root('tests', 'skinning_test_no_simd.cpp'), 'r').read() self.do_benchmark('skinning', src, 'blah=0.000000')
def test_html5_mouse(self): self.btest(path_from_root('tests', 'test_html5_mouse.c'), expected='0')
def test_html5_fullscreen(self): self.btest(path_from_root('tests', 'test_html5_fullscreen.c'), expected='0')
def test_firstrun(self): for command in commands: wipe() def make_new_executable(name): open(os.path.join(temp_bin, name), 'w').close() make_executable(os.path.join(temp_bin, name)) env = os.environ.copy() if 'EM_CONFIG' in env: del env['EM_CONFIG'] try: temp_bin = tempfile.mkdtemp() make_new_executable('llvm-dis') make_new_executable('node') env['PATH'] = temp_bin + os.pathsep + os.environ['PATH'] output = self.do(command, env=env) finally: shutil.rmtree(temp_bin) default_config = config.embedded_config self.assertContained('Welcome to Emscripten!', output) self.assertContained( 'This is the first time any of the Emscripten tools has been run.', output) self.assertContained( 'A settings file has been copied to %s, at absolute path: %s' % (default_config, default_config), output) self.assertContained( 'It contains our best guesses for the important paths, which are:', output) self.assertContained('LLVM_ROOT', output) self.assertContained('NODE_JS', output) if platform.system() != 'Windows': # os.chmod can't make files executable on Windows self.assertIdentical( temp_bin, re.search("^ *LLVM_ROOT *= (.*)$", output, re.M).group(1)) possible_nodes = [os.path.join(temp_bin, 'node')] if os.path.exists('/usr/bin/nodejs'): possible_nodes.append('/usr/bin/nodejs') self.assertIdentical( possible_nodes, re.search("^ *NODE_JS *= (.*)$", output, re.M).group(1)) self.assertContained( 'Please edit the file if any of those are incorrect', output) self.assertContained( 'This command will now exit. When you are done editing those paths, re-run it.', output) assert output.split()[-1].endswith( '===='), 'We should have stopped: ' + output config_file = open(default_config).read() template_file = open( path_from_root('tools', 'settings_template.py')).read() self.assertNotContained('{{{', config_file) self.assertNotContained('}}}', config_file) self.assertContained('{{{', template_file) self.assertContained('}}}', template_file) for content in [ 'EMSCRIPTEN_ROOT', 'LLVM_ROOT', 'NODE_JS', 'JS_ENGINES' ]: self.assertContained(content, config_file) # The guessed config should be ok # XXX This depends on your local system! it is possible `which` guesses wrong # try_delete('a.out.js') # output = self.run_process([EMCC, test_file('hello_world.c')], stdout=PIPE, stderr=PIPE).output # self.assertContained('hello, world!', self.run_js('a.out.js'), output) # Second run, with bad EM_CONFIG for settings in [ 'blah', 'LLVM_ROOT="blarg"; JS_ENGINES=[]; NODE_JS=[]; SPIDERMONKEY_ENGINE=[]' ]: f = open(default_config, 'w') f.write(settings) f.close() output = self.do(command, env=env) if 'LLVM_ROOT' not in settings: self.assertContained( 'Error in evaluating %s' % default_config, output) elif 'runner.py' not in ' '.join(command): self.assertContained('error:', output) # sanity check should fail try_delete(default_config)
import os, shutil, stat, subprocess from runner import RunnerCore, path_from_root from tools.shared import * SANITY_FILE = CONFIG_FILE + '_sanity' commands = [[PYTHON, EMCC], [PYTHON, path_from_root('tests', 'runner.py'), 'blahblah']] def restore(): shutil.copyfile(CONFIG_FILE + '_backup', CONFIG_FILE) def wipe(): try_delete(CONFIG_FILE) try_delete(SANITY_FILE) def mtime(filename): return os.stat(filename).st_mtime SANITY_MESSAGE = 'Emscripten: Running sanity checks' class sanity(RunnerCore): @classmethod def setUpClass(self): super(RunnerCore, self).setUpClass() shutil.copyfile(CONFIG_FILE, CONFIG_FILE + '_backup')
def test_sdl_mousewheel(self): self.btest(path_from_root('tests', 'test_sdl_mousewheel.c'), expected='0')
def test_emcc(self): SANITY_FAIL_MESSAGE = 'sanity check failed to run' # emcc should check sanity if no ${EM_CONFIG}_sanity restore() time.sleep(0.1) assert not os.path.exists( SANITY_FILE) # restore is just the settings, not the sanity output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) assert os.path.exists( SANITY_FILE) # EMCC should have checked sanity successfully assert mtime(SANITY_FILE) >= mtime(CONFIG_FILE) assert generate_sanity() == open(SANITY_FILE).read() self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc run again should not sanity check, because the sanity file is newer output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # correct sanity contents mean we need not check open(SANITY_FILE, 'w').write(generate_sanity()) output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) # incorrect sanity contents mean we *must* check open(SANITY_FILE, 'w').write('wakawaka') output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) # but with EMCC_DEBUG=1 we should check try: os.environ['EMCC_DEBUG'] = '1' output = self.check_working(EMCC) finally: del os.environ['EMCC_DEBUG'] self.assertContained(SANITY_MESSAGE, output) output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) # also with -v, with or without inputs output = self.check_working([EMCC, '-v'], SANITY_MESSAGE) output = self.check_working( [EMCC, '-v', path_from_root('tests', 'hello_world.c')], SANITY_MESSAGE) # Make sure the test runner didn't do anything to the setup output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc should also check sanity if the file is outdated time.sleep(0.1) restore() assert mtime(SANITY_FILE) < mtime(CONFIG_FILE) output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) assert mtime(SANITY_FILE) >= mtime(CONFIG_FILE) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc should be configurable directly from EM_CONFIG without any config file restore() config = open(CONFIG_FILE, 'r').read() os.environ['EM_CONFIG'] = config wipe() dirname = tempfile.mkdtemp(prefix='emscripten_test_' + self.__class__.__name__ + '_', dir=TEMP_DIR) open(os.path.join(dirname, 'main.cpp'), 'w').write(''' #include <stdio.h> int main() { printf("hello from emcc with no config file\\n"); return 0; } ''') Popen([ PYTHON, EMCC, os.path.join(dirname, 'main.cpp'), '-o', os.path.join(dirname, 'a.out.js') ]).communicate() del os.environ['EM_CONFIG'] old_dir = os.getcwd() try: os.chdir(dirname) self.assertContained('hello from emcc with no config file', run_js('a.out.js')) finally: os.chdir(old_dir) shutil.rmtree(dirname) try_delete(CANONICAL_TEMP_DIR)
def test_sdl_touch(self): self.btest(path_from_root('tests', 'sdl_touch.c'), args=['-O2', '-g1', '--closure', '1'], expected='0')
def ensure_cache(): self.do([ PYTHON, EMCC, '-O2', path_from_root('tests', 'hello_world.c') ])
def test_sdl_audio_mix_channels(self): shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) open(os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix_channels.c')).read())) Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), '--preload-file', 'sound.ogg', '-o', 'page.html']).communicate() self.run_browser('page.html', '', '/report_result?1')
def test_native_optimizer(self): restore() def build(): return self.check_working([EMCC, '-O2', 'tests/hello_world.c'], 'running js post-opts') def test(): self.assertContained('hello, world!', run_js('a.out.js')) try: os.environ['EMCC_DEBUG'] = '1' # basic usage or lack of usage for native in [None, 0, 1]: print 'phase 1, part', native Cache.erase() try: if native is not None: os.environ['EMCC_NATIVE_OPTIMIZER'] = str(native) output = build() assert ('js optimizer using native' in output) == ( not not (native or native is None)), output test() if native or native is None: # None means use the default, which is to use the native optimizer assert 'building native optimizer' in output # compile again, no rebuild of optimizer output = build() assert 'building native optimizer' not in output assert 'js optimizer using native' in output test() finally: if native is not None: del os.environ['EMCC_NATIVE_OPTIMIZER'] # force a build failure, see we fall back to non-native try: for native in [1, 'g']: print 'phase 2, part', native Cache.erase() os.environ['EMCC_NATIVE_OPTIMIZER'] = str(native) try: # break it f = path_from_root('tools', 'optimizer', 'optimizer.cpp') src = open(f).read() bad = src.replace('main', '!waka waka<') assert bad != src open(f, 'w').write(bad) # first try output = build() assert 'failed to build native optimizer' in output, output if native == 1: assert 'to see compiler errors, build with EMCC_NATIVE_OPTIMIZER=g' in output assert 'waka waka' not in output else: assert 'output from attempt' in output, output assert 'waka waka' in output, output assert 'js optimizer using native' not in output test() # still works, without native optimizer # second try, see previous failure output = build() assert 'failed to build native optimizer' not in output assert 'seeing that optimizer could not be built' in output test() # still works, without native optimizer # clear cache, try again Cache.erase() output = build() assert 'failed to build native optimizer' in output test() # still works, without native optimizer finally: open(f, 'w').write(src) Cache.erase() # now it should work again output = build() assert 'js optimizer using native' in output test() # still works finally: del os.environ['EMCC_NATIVE_OPTIMIZER'] finally: del os.environ['EMCC_DEBUG']
def test_html5_fullscreen(self): self.btest(path_from_root('tests', 'test_html5_fullscreen.c'), expected='0', args=['-s', 'EXPORTED_FUNCTIONS=["_requestFullscreen","_enterSoftFullscreen","_main"]', '--shell-file', path_from_root('tests', 'test_html5_fullscreen.html')])
def test_getaddrinfo(self): self.emcc_args = [] self.do_run( open(path_from_root('tests', 'sockets', 'test_getaddrinfo.c')).read(), 'success')
def test_openal_buffers(self): shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.wav'), os.path.join(self.get_dir(), 'the_entertainer.wav')) self.btest('openal_buffers.c', '0', args=['--preload-file', 'the_entertainer.wav'],)
def test_getprotobyname(self): self.do_run( open(path_from_root('tests', 'sockets', 'test_getprotobyname.c')).read(), 'success')
def zzztest_webrtc( self ): # XXX see src/settings.js, this is disabled pending investigation host_src = 'webrtc_host.c' peer_src = 'webrtc_peer.c' host_outfile = 'host.html' peer_outfile = 'peer.html' host_filepath = path_from_root('tests', 'sockets', host_src) temp_host_filepath = os.path.join(self.get_dir(), os.path.basename(host_src)) with open(host_filepath) as f: host_src = f.read() with open(temp_host_filepath, 'w') as f: f.write(self.with_report_result(host_src)) peer_filepath = path_from_root('tests', 'sockets', peer_src) temp_peer_filepath = os.path.join(self.get_dir(), os.path.basename(peer_src)) with open(peer_filepath) as f: peer_src = f.read() with open(temp_peer_filepath, 'w') as f: f.write(self.with_report_result(peer_src)) open(os.path.join(self.get_dir(), 'host_pre.js'), 'w').write(''' var Module = { webrtc: { broker: 'http://localhost:8182', session: undefined, onpeer: function(peer, route) { window.open('http://localhost:8888/peer.html?' + route); // iframe = document.createElement("IFRAME"); // iframe.setAttribute("src", "http://localhost:8888/peer.html?" + route); // iframe.style.display = "none"; // document.body.appendChild(iframe); peer.listen(); }, onconnect: function(peer) { }, ondisconnect: function(peer) { }, onerror: function(error) { console.error(error); } }, }; ''') open(os.path.join(self.get_dir(), 'peer_pre.js'), 'w').write(''' var Module = { webrtc: { broker: 'http://localhost:8182', session: window.location.toString().split('?')[1], onpeer: function(peer, route) { peer.connect(Module['webrtc']['session']); }, onconnect: function(peer) { }, ondisconnect: function(peer) { // Calling window.close() from this handler hangs my browser, so run it in the next turn setTimeout(window.close, 0); }, onerror: function(error) { console.error(error); } } }; ''') Popen([PYTHON, EMCC, temp_host_filepath, '-o', host_outfile] + [ '-s', 'GL_TESTING=1', '--pre-js', 'host_pre.js', '-s', 'SOCKET_WEBRTC=1', '-s', 'SOCKET_DEBUG=1' ]).communicate() Popen([PYTHON, EMCC, temp_peer_filepath, '-o', peer_outfile] + [ '-s', 'GL_TESTING=1', '--pre-js', 'peer_pre.js', '-s', 'SOCKET_WEBRTC=1', '-s', 'SOCKET_DEBUG=1' ]).communicate() # note: you may need to run this manually yourself, if npm is not in the path, or if you need a version that is not in the path Popen(['npm', 'install', path_from_root('tests', 'sockets', 'p2p')]).communicate() broker = Popen(NODE_JS + [ path_from_root('tests', 'sockets', 'p2p', 'broker', 'p2p-broker.js') ]) expected = '1' self.run_browser(host_outfile, '.', ['/report_result?' + e for e in expected]) broker.kill()
def test_nodejs_sockets_echo(self): # This test checks that sockets work when the client code is run in Node.js # Run with ./runner.py sockets.test_nodejs_sockets_echo if not NODE_JS in JS_ENGINES: return self.skip('node is not present') sockets_include = '-I' + path_from_root('tests', 'sockets') harnesses = [(WebsockifyServerHarness( os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 59160), 0), (CompiledServerHarness( os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 59162), 0), (CompiledServerHarness( os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 59164), 1)] harnesses = filter_harnesses(harnesses) # Basic test of node client against both a Websockified and compiled echo server. for harness, datagram in harnesses: with harness: Popen([ PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', 'client.js', '-DSOCKK=%d' % harness.listen_port, '-DTEST_DGRAM=%d' % datagram, '-DREPORT_RESULT=int dummy' ], stdout=PIPE, stderr=PIPE).communicate() out = run_js('client.js', engine=NODE_JS, full_output=True) self.assertContained('do_msg_read: read 14 bytes', out) # Test against a Websockified server with compile time configured WebSocket subprotocol. We use a Websockified # server because as long as the subprotocol list contains binary it will configure itself to accept binary # This test also checks that the connect url contains the correct subprotocols. print "\nTesting compile time WebSocket configuration.\n" for harness in filter_harnesses([ WebsockifyServerHarness( os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 59166) ]): with harness: Popen([ PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_sockets_echo_client.c'), '-o', 'client.js', '-s', 'SOCKET_DEBUG=1', '-s', 'WEBSOCKET_SUBPROTOCOL="base64, binary"', '-DSOCKK=59166', '-DREPORT_RESULT=int dummy' ], stdout=PIPE, stderr=PIPE).communicate() out = run_js('client.js', engine=NODE_JS, full_output=True) self.assertContained('do_msg_read: read 14 bytes', out) self.assertContained([ 'connect: ws://127.0.0.1:59166, base64,binary', 'connect: ws://127.0.0.1:59166/, base64,binary' ], out) # Test against a Websockified server with runtime WebSocket configuration. We specify both url and subprotocol. # In this test we have *deliberately* used the wrong port '-DSOCKK=12345' to configure the echo_client.c, so # the connection would fail without us specifying a valid WebSocket URL in the configuration. print "\nTesting runtime WebSocket configuration.\n" for harness in filter_harnesses([ WebsockifyServerHarness( os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 59168) ]): with harness: open(os.path.join(self.get_dir(), 'websocket_pre.js'), 'w').write(''' var Module = { websocket: { url: 'ws://localhost:59168/testA/testB', subprotocol: 'text, base64, binary', } }; ''') Popen([ PYTHON, EMCC, path_from_root( 'tests', 'sockets', 'test_sockets_echo_client.c'), '-o', 'client.js', '--pre-js', 'websocket_pre.js', '-s', 'SOCKET_DEBUG=1', '-DSOCKK=12345', '-DREPORT_RESULT=int dummy' ], stdout=PIPE, stderr=PIPE).communicate() out = run_js('client.js', engine=NODE_JS, full_output=True) self.assertContained('do_msg_read: read 14 bytes', out) self.assertContained( 'connect: ws://localhost:59168/testA/testB, text,base64,binary', out)
def zzztest_sdl2_audio_beeps(self): open(os.path.join(self.get_dir(), 'sdl2_audio_beep.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl2_audio_beep.cpp')).read())) # use closure to check for a possible bug with closure minifying away newer Audio() attributes Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl2_audio_beep.cpp'), '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-s', 'USE_SDL=2', '-o', 'page.html']).communicate() self.run_browser('page.html', '', '/report_result?1')