diff options
Diffstat (limited to 'tests/test_other.py')
-rw-r--r-- | tests/test_other.py | 258 |
1 files changed, 225 insertions, 33 deletions
diff --git a/tests/test_other.py b/tests/test_other.py index cdea493a..39796b71 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -203,6 +203,12 @@ Options that are modified or new in %s include: (['--typed-arrays', '1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), (['--typed-arrays', '2'], lambda generated: 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 selected'), (['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'), + ([], lambda generated: '// The Module object' in generated, 'without opts, comments in shell code'), + (['-O2'], lambda generated: '// The Module object' not in generated, 'with opts, no comments in shell code'), + (['-O2', '-g2'], lambda generated: '// The Module object' not in generated, 'with -g2, no comments in shell code'), + (['-O2', '-g3'], lambda generated: '// The Module object' in generated, 'with -g3, yes comments in shell code'), + (['-O2', '-profiling'], lambda generated: '// The Module object' in generated or os.environ.get('EMCC_FAST_COMPILER') == '0', 'with -profiling, yes comments in shell code (in fastcomp)'), + ]: print params, text self.clear() @@ -349,9 +355,10 @@ f.close() except KeyError: postbuild = None - cmake_cases = ['target_js', 'target_html'] - cmake_outputs = ['test_cmake.js', 'hello_world_gles.html'] - for i in range(0, 2): + cmake_cases = ['target_js', 'target_html', 'target_library', 'target_library'] + cmake_outputs = ['test_cmake.js', 'hello_world_gles.html', 'libtest_cmake.a', 'libtest_cmake.so'] + cmake_arguments = ['', '', '-DBUILD_SHARED_LIBS=OFF', '-DBUILD_SHARED_LIBS=ON'] + for i in range(0, len(cmake_cases)): for configuration in ['Debug', 'Release']: # CMake can be invoked in two ways, using 'emconfigure cmake', or by directly running 'cmake'. # Test both methods. @@ -369,11 +376,11 @@ f.close() if invoke_method == 'cmake': # Test invoking cmake directly. cmd = ['cmake', '-DCMAKE_TOOLCHAIN_FILE='+path_from_root('cmake', 'Platform', 'Emscripten.cmake'), - '-DCMAKE_BUILD_TYPE=' + configuration, '-G', generator, cmakelistsdir] + '-DCMAKE_BUILD_TYPE=' + configuration, cmake_arguments[i], '-G', generator, cmakelistsdir] else: # Test invoking via 'emconfigure cmake' - cmd = [emconfigure, 'cmake', '-DCMAKE_BUILD_TYPE=' + configuration, '-G', generator, cmakelistsdir] - + cmd = [emconfigure, 'cmake', '-DCMAKE_BUILD_TYPE=' + configuration, cmake_arguments[i], '-G', generator, cmakelistsdir] + ret = Popen(cmd, stdout=None if verbose_level >= 2 else PIPE, stderr=None if verbose_level >= 1 else PIPE).communicate() if len(ret) > 1 and ret[1] != None and len(ret[1].strip()) > 0: logging.error(ret[1]) # If there were any errors, print them directly to console for diagnostics. @@ -1027,10 +1034,93 @@ This pointer might make sense in another type signature: i: 0 Building.emar('cr', lib_name, [a_name + '.o', b_name + '.o']) # libLIB.a with a and b # a is in the lib AND in an .o, so should be ignored in the lib. We do still need b from the lib though - Building.emcc(main_name, ['-L.', '-lLIB', a_name+'.o', c_name + '.o'], output_filename='a.out.js') + Building.emcc(main_name, [a_name+'.o', c_name + '.o', '-L.', '-lLIB'], output_filename='a.out.js') self.assertContained('result: 62', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_link_group_asserts(self): + lib_src_name = os.path.join(self.get_dir(), 'lib.c') + open(lib_src_name, 'w').write('int x() { return 42; }') + + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(r''' + #include <stdio.h> + int x(); + int main() { + printf("result: %d\n", x()); + return 0; + } + ''') + + Building.emcc(lib_src_name) # lib.c.o + lib_name = os.path.join(self.get_dir(), 'libLIB.a') + Building.emar('cr', lib_name, [lib_src_name + '.o']) # libLIB.a with lib.c.o + + def test(lib_args, err_expected): + output = Popen([PYTHON, EMCC, main_name, '-o', 'a.out.js'] + lib_args, stdout=PIPE, stderr=PIPE).communicate() + if err_expected: + self.assertContained(err_expected, output[1]) + else: + out_js = os.path.join(self.get_dir(), 'a.out.js') + assert os.path.exists(out_js), '\n'.join(output) + self.assertContained('result: 42', run_js(out_js)) + + test(['-Wl,--start-group', lib_name], '--start-group without matching --end-group') + test(['-Wl,--start-group', lib_name, '-Wl,--start-group'], 'Nested --start-group, missing --end-group?') + test(['-Wl,--end-group', lib_name, '-Wl,--start-group'], '--end-group without --start-group') + test(['-Wl,--start-group', lib_name, '-Wl,--end-group'], None) + + def test_circular_libs(self): + def tmp_source(name, code): + file_name = os.path.join(self.get_dir(), name) + open(file_name, 'w').write(code) + return file_name + + a = tmp_source('a.c', 'int z(); int x() { return z(); }') + b = tmp_source('b.c', 'int x(); int y() { return x(); } int z() { return 42; }') + c = tmp_source('c.c', 'int q() { return 0; }') + main = tmp_source('main.c', r''' + #include <stdio.h> + int y(); + int main() { + printf("result: %d\n", y()); + return 0; + } + ''') + + Building.emcc(a) # a.c.o + Building.emcc(b) # b.c.o + Building.emcc(c) # c.c.o + lib_a = os.path.join(self.get_dir(), 'libA.a') + Building.emar('cr', lib_a, [a + '.o', c + '.o']) # libA.a with a.c.o,c.c.o + lib_b = os.path.join(self.get_dir(), 'libB.a') + Building.emar('cr', lib_b, [b + '.o', c + '.o']) # libB.a with b.c.o,c.c.o + + args = ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1', main, '-o', 'a.out.js'] + libs_list = [lib_a, lib_b] + + # lib_a does not satisfy any symbols from main, so it will not be included, + # and there will be an unresolved symbol. + output = Popen([PYTHON, EMCC] + args + libs_list, stdout=PIPE, stderr=PIPE).communicate() + self.assertContained('error: unresolved symbol: x', output[1]) + + # -Wl,--start-group and -Wl,--end-group around the libs will cause a rescan + # of lib_a after lib_b adds undefined symbol "x", so a.c.o will now be + # included (and the link will succeed). + libs = ['-Wl,--start-group'] + libs_list + ['-Wl,--end-group'] + output = Popen([PYTHON, EMCC] + args + libs, stdout=PIPE, stderr=PIPE).communicate() + out_js = os.path.join(self.get_dir(), 'a.out.js') + assert os.path.exists(out_js), '\n'.join(output) + self.assertContained('result: 42', run_js(out_js)) + + # -( and -) should also work. + args = ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1', main, '-o', 'a2.out.js'] + libs = ['-Wl,-('] + libs_list + ['-Wl,-)'] + output = Popen([PYTHON, EMCC] + args + libs, stdout=PIPE, stderr=PIPE).communicate() + out_js = os.path.join(self.get_dir(), 'a2.out.js') + assert os.path.exists(out_js), '\n'.join(output) + self.assertContained('result: 42', run_js(out_js)) + def test_redundant_link(self): lib = "int mult() { return 1; }" lib_name = os.path.join(self.get_dir(), 'libA.c') @@ -1879,25 +1969,20 @@ This pointer might make sense in another type signature: i: 0 assert 'If you see this - the world is all right!' in output def test_embind(self): - def nonfc(): - if os.environ.get('EMCC_FAST_COMPILER') != '0': return self.skip('todo in fastcomp') - for args, fail in [ - ([], True), # without --bind, we fail - (['--bind'], False), - (['--bind', '-O1'], False), - (['--bind', '-O2'], False), - (['--bind', '-O1', '-s', 'ASM_JS=0'], False), - (['--bind', '-O2', '-s', 'ASM_JS=0'], False) - ]: - print args, fail - self.clear() - try_delete(self.in_dir('a.out.js')) - Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'underscore-1.4.2.js'), '--post-js', path_from_root('tests', 'embind', 'imvu_test_adapter.js'), '--post-js', path_from_root('tests', 'embind', 'embind.test.js')] + args, stderr=PIPE if fail else None).communicate() - assert os.path.exists(self.in_dir('a.out.js')) == (not fail) - if not fail: - output = run_js(self.in_dir('a.out.js'), stdout=PIPE, stderr=PIPE, full_output=True) - assert "FAIL" not in output, output - nonfastcomp(nonfc) + for args, fail in [ + ([], True), # without --bind, we fail + (['--bind'], False), + (['--bind', '-O1'], False), + (['--bind', '-O2'], False), + ]: + print args, fail + self.clear() + try_delete(self.in_dir('a.out.js')) + Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'underscore-1.4.2.js'), '--post-js', path_from_root('tests', 'embind', 'imvu_test_adapter.js'), '--post-js', path_from_root('tests', 'embind', 'embind.test.js')] + args, stderr=PIPE if fail else None).communicate() + assert os.path.exists(self.in_dir('a.out.js')) == (not fail) + if not fail: + output = run_js(self.in_dir('a.out.js'), stdout=PIPE, stderr=PIPE, full_output=True, assert_returncode=0) + assert "FAIL" not in output, output def test_llvm_nativizer(self): try: @@ -2140,7 +2225,7 @@ void wakaw::Cm::RasterBase<wakaw::watwat::Polocator?>(unsigned int*, unsigned in test_js_closure_0 = open(path_from_root('tests', 'Module-exports', 'test.js')).read() # Check that test.js compiled with --closure 0 contains "module['exports'] = Module;" - assert "module['exports'] = Module;" in test_js_closure_0 + assert ("module['exports'] = Module;" in test_js_closure_0) or ('module["exports"]=Module' in test_js_closure_0) # Check that main.js (which requires test.js) completes successfully when run in node.js # in order to check that the exports are indeed functioning correctly. @@ -2296,15 +2381,17 @@ mergeInto(LibraryManager.library, { self.clear() os.mkdir(outdir) - process = Popen([PYTHON, EMCC, '-c', path_from_root('tests', 'hello_world.c'), '-o', outdir]) - process.communicate() - assert(os.path.isfile(outdir + 'hello_world.o')) + process = Popen([PYTHON, EMCC, '-c', path_from_root('tests', 'hello_world.c'), '-o', outdir], stderr=PIPE) + out, err = process.communicate() + assert not err, err + assert os.path.isfile(outdir + 'hello_world.o') self.clear() os.mkdir(outdir) - process = Popen([PYTHON, EMCC, '-c', path_from_root('tests', 'hello_world.c'), '-o', outdir, '--default-obj-ext', 'obj']) - process.communicate() - assert(os.path.isfile(outdir + 'hello_world.obj')) + process = Popen([PYTHON, EMCC, '-c', path_from_root('tests', 'hello_world.c'), '-o', outdir, '--default-obj-ext', 'obj'], stderr=PIPE) + out, err = process.communicate() + assert not err, err + assert os.path.isfile(outdir + 'hello_world.obj') def test_doublestart_bug(self): open('code.cpp', 'w').write(r''' @@ -2381,6 +2468,11 @@ int main() { err = Popen([PYTHON, EMCC, 'src.cpp', '-include', 'header.h', '-Xclang', '-print-stats'], stderr=PIPE).communicate() assert '*** PCH/Modules Loaded:\nModule: header.h.gch' not in err[1], err[1] + # with specified target via -o + try_delete('header.h.gch') + Popen([PYTHON, EMCC, '-xc++-header', 'header.h', '-o', 'my.gch']).communicate() + assert os.path.exists('my.gch') + def test_warn_unaligned(self): if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('need fastcomp') open('src.cpp', 'w').write(r''' @@ -2710,3 +2802,103 @@ int main() assert os.path.exists('hello_world.o') assert os.path.exists('hello_world.bc') + def test_bad_function_pointer_cast(self): + open('src.cpp', 'w').write(r''' +#include <stdio.h> + +typedef int (*callback) (int, ...); + +int impl(int foo) { + printf("Hello, world.\n"); + return 0; +} + +int main() { + volatile callback f = (callback) impl; + f(0); /* This fails with or without additional arguments. */ + return 0; +} +''') + + for opts in [0, 1, 2]: + for safe in [0, 1]: + cmd = [PYTHON, EMCC, 'src.cpp', '-O' + str(opts), '-s', 'SAFE_HEAP=' + str(safe)] + print cmd + Popen(cmd).communicate() + output = run_js('a.out.js', stderr=PIPE, full_output=True) + if safe: + assert 'Function table mask error' in output, output + else: + if opts == 0: + assert 'Invalid function pointer called' in output, output + else: + assert 'abort()' in output, output + + def test_aliased_func_pointers(self): + open('src.cpp', 'w').write(r''' +#include <stdio.h> + +int impl1(int foo) { return foo; } +float impla(float foo) { return foo; } +int impl2(int foo) { return foo+1; } +float implb(float foo) { return foo+1; } +int impl3(int foo) { return foo+2; } +float implc(float foo) { return foo+2; } + +int main(int argc, char **argv) { + volatile void *f = (void*)impl1; + if (argc == 50) f = (void*)impla; + if (argc == 51) f = (void*)impl2; + if (argc == 52) f = (void*)implb; + if (argc == 53) f = (void*)impl3; + if (argc == 54) f = (void*)implc; + return (int)f; +} +''') + + print 'aliasing' + + sizes_ii = {} + sizes_dd = {} + + for alias in [None, 0, 1]: + cmd = [PYTHON, EMCC, 'src.cpp', '-O1'] + if alias is not None: + cmd += ['-s', 'ALIASING_FUNCTION_POINTERS=' + str(alias)] + else: + alias = -1 + print cmd + Popen(cmd).communicate() + src = open('a.out.js').read().split('\n') + for line in src: + if line.strip().startswith('var FUNCTION_TABLE_ii = '): + sizes_ii[alias] = line.count(',') + if line.strip().startswith('var FUNCTION_TABLE_dd = '): + sizes_dd[alias] = line.count(',') + + for sizes in [sizes_ii, sizes_dd]: + assert sizes[-1] == 3 # default - let them alias + assert sizes[0] == 7 # no aliasing, all unique, fat tables + assert sizes[1] == 3 # aliased once more + + def test_bad_export(self): + for m in ['', ' ']: + self.clear() + cmd = [PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'EXPORTED_FUNCTIONS=["' + m + '_main"]'] + print cmd + stdout, stderr = Popen(cmd, stderr=PIPE).communicate() + if m: + assert 'function requested to be exported, but not implemented: " _main"' in stderr, stderr + else: + self.assertContained('hello, world!', run_js('a.out.js')) + + def test_no_dynamic_execution(self): + cmd = [PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-O1', '-s', 'NO_DYNAMIC_EXECUTION=1'] + stdout, stderr = Popen(cmd, stderr=PIPE).communicate() + self.assertContained('hello, world!', run_js('a.out.js')) + src = open('a.out.js').read() + assert 'eval(' not in src + assert 'eval.' not in src + assert 'new Function' not in src + + |