diff options
Diffstat (limited to 'tests/test_other.py')
-rw-r--r-- | tests/test_other.py | 869 |
1 files changed, 524 insertions, 345 deletions
diff --git a/tests/test_other.py b/tests/test_other.py index b4abf275..5af2b22c 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -1,7 +1,7 @@ import multiprocessing, os, re, shutil, subprocess, sys import tools.shared from tools.shared import * -from runner import RunnerCore, path_from_root, get_bullet_library +from runner import RunnerCore, path_from_root, get_bullet_library, nonfastcomp class other(RunnerCore): def get_zlib_library(self): @@ -19,7 +19,7 @@ class other(RunnerCore): output = Popen([PYTHON, compiler, '--version'], stdout=PIPE, stderr=PIPE).communicate() output = output[0].replace('\r', '') self.assertContained('''emcc (Emscripten GCC-like replacement)''', output) - self.assertContained('''Copyright (C) 2013 the Emscripten authors (see AUTHORS.txt) + self.assertContained('''Copyright (C) 2014 the Emscripten authors (see AUTHORS.txt) This is free and open source software under the MIT license. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ''', output) @@ -114,8 +114,6 @@ Options that are modified or new in %s include: os.chdir(self.get_dir()) self.clear() - # dlmalloc. dlmalloc is special in that it is the only part of libc that is (1) hard to write well, and - # very speed-sensitive. So we do not implement it in JS in library.js, instead we compile it from source for source, has_malloc in [('hello_world' + suffix, False), ('hello_malloc.cpp', True)]: print source, has_malloc self.clear() @@ -195,7 +193,6 @@ Options that are modified or new in %s include: (['-O2', '-g3'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize is cancelled by -g3'), #(['-O2', '-g4'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'same as -g3 for now'), (['-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' in generated, 'no inlining without opts'), - (['-Os', '--llvm-lto', '1', '-s', 'ASM_JS=0', '-g2'], lambda generated: 'function _dump' in generated, '-Os disables inlining'), (['-s', 'USE_TYPED_ARRAYS=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), (['-s', 'USE_TYPED_ARRAYS=1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), ([], lambda generated: 'Module["_dump"]' not in generated, 'dump is not exported by default'), @@ -207,7 +204,7 @@ Options that are modified or new in %s include: ]: print params, text self.clear() - if os.environ.get('EMCC_FAST_COMPILER') == '1' and ['disable typed arrays', 'typed arrays 1 selected']: continue + if os.environ.get('EMCC_FAST_COMPILER') != '0' and ['disable typed arrays', 'typed arrays 1 selected']: continue output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_loop.cpp'), '-o', 'a.out.js'] + params, stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] assert os.path.exists('a.out.js'), '\n'.join(output) @@ -277,6 +274,9 @@ f.close() # TODO: test normal project linking, static and dynamic: get_library should not need to be told what to link! # TODO: deprecate llvm optimizations, dlmalloc, etc. in emscripten.py. + def test_emcc_nonfastcomp(self): + nonfastcomp(self.test_emcc) + def test_cmake(self): # Test all supported generators. if WINDOWS: @@ -451,48 +451,48 @@ f.close() self.assertContained('hello, world!', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_unaligned_memory(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - - open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' - #include <stdio.h> - #include <stdarg.h> + def test(): + open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' + #include <stdio.h> + #include <stdarg.h> - typedef unsigned char Bit8u; - typedef unsigned short Bit16u; - typedef unsigned int Bit32u; + typedef unsigned char Bit8u; + typedef unsigned short Bit16u; + typedef unsigned int Bit32u; - int main() - { - va_list argp; - va_arg(argp, char *); // check for compilation error, #1705 + int main() + { + va_list argp; + va_arg(argp, char *); // check for compilation error, #1705 - Bit8u data[4] = {0x01,0x23,0x45,0x67}; + Bit8u data[4] = {0x01,0x23,0x45,0x67}; - printf("data: %x\n", *(Bit32u*)data); - printf("data[0,1] 16bit: %x\n", *(Bit16u*)data); - printf("data[1,2] 16bit: %x\n", *(Bit16u*)(data+1)); - } - ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() - self.assertContained('data: 67452301\ndata[0,1] 16bit: 2301\ndata[1,2] 16bit: 4523', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + printf("data: %x\n", *(Bit32u*)data); + printf("data[0,1] 16bit: %x\n", *(Bit16u*)data); + printf("data[1,2] 16bit: %x\n", *(Bit16u*)(data+1)); + } + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() + self.assertContained('data: 67452301\ndata[0,1] 16bit: 2301\ndata[1,2] 16bit: 4523', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + nonfastcomp(test) def test_unaligned_memory_2(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - - open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' - #include <string> - #include <stdio.h> + def test(): + open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' + #include <string> + #include <stdio.h> - int main( int argc, char ** argv ) - { - std::string testString( "Hello, World!" ); + int main( int argc, char ** argv ) + { + std::string testString( "Hello, World!" ); - printf( "testString = %s\n", testString.c_str() ); - return 0; - } - ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() - self.assertContained('testString = Hello, World!', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + printf( "testString = %s\n", testString.c_str() ); + return 0; + } + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() + self.assertContained('testString = Hello, World!', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + nonfastcomp(test) def test_asm_minify(self): def test(args): @@ -537,12 +537,13 @@ f.close() ''' open('src.c', 'w').write(src) def test(args, expected, err_expected=None): + print args, expected, err_expected out, err = Popen([PYTHON, EMCC, 'src.c'] + args, stderr=PIPE).communicate() if err_expected: self.assertContained(err_expected, err) self.assertContained(expected, run_js(self.in_dir('a.out.js'), stderr=PIPE, full_output=True)) return open(self.in_dir('a.out.js')).read() - if os.environ.get('EMCC_FAST_COMPILER') != '1': + if os.environ.get('EMCC_FAST_COMPILER') == '0': test([], 'my func') # no asm, so casting func works test(['-O2'], 'abort', ['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func', 'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure @@ -552,12 +553,12 @@ f.close() 'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure else: # fastcomp. all asm, so it can't just work with wrong sigs. but, ASSERTIONS=2 gives much better info to debug - test(['-O1'], 'abort') # no useful info - test(['-O1', '-s', 'ASSERTIONS=1'], '''Invalid function pointer called with signature 'v'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an different type, which will fail? + test(['-O1'], 'If this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.') # no useful info, but does mention ASSERTIONS + test(['-O1', '-s', 'ASSERTIONS=1'], '''Invalid function pointer called with signature 'v'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this) Build with ASSERTIONS=2 for more info. ''') # some useful text - test(['-O1', '-s', 'ASSERTIONS=2'], '''Invalid function pointer '1' called with signature 'v'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an different type, which will fail? -This pointer might make sense in another type signature: i: _my_func + test(['-O1', '-s', 'ASSERTIONS=2'], '''Invalid function pointer '0' called with signature 'v'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this) +This pointer might make sense in another type signature: i: 0 ''') # actually useful identity of the bad pointer, with comparisons to what it would be in other types/tables def test_l_link(self): @@ -590,283 +591,285 @@ This pointer might make sense in another type signature: i: _my_func assert not os.path.exists('a.out') and not os.path.exists('a.exe'), 'Must not leave unneeded linker stubs' def test_static_link(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - - def test(name, header, main, side, expected, args=[], suffix='cpp', first=True): - print name - #t = main ; main = side ; side = t - original_main = main - original_side = side - if header: open(os.path.join(self.get_dir(), 'header.h'), 'w').write(header) - if type(main) == str: - open(os.path.join(self.get_dir(), 'main.' + suffix), 'w').write(main) - main = ['main.' + suffix] - if type(side) == str: - open(os.path.join(self.get_dir(), 'side.' + suffix), 'w').write(side) - side = ['side.' + suffix] - Popen([PYTHON, EMCC] + side + ['-o', 'side.js', '-s', 'SIDE_MODULE=1', '-O2'] + args).communicate() - # TODO: test with and without DISABLE_GL_EMULATION, check that file sizes change - Popen([PYTHON, EMCC] + main + ['-o', 'main.js', '-s', 'MAIN_MODULE=1', '-O2', '-s', 'DISABLE_GL_EMULATION=1'] + args).communicate() - Popen([PYTHON, EMLINK, 'main.js', 'side.js', 'together.js'], stdout=PIPE).communicate() - assert os.path.exists('together.js') - for engine in JS_ENGINES: - out = run_js('together.js', engine=engine, stderr=PIPE, full_output=True) - self.assertContained(expected, out) - if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) - if first: - shutil.copyfile('together.js', 'first.js') - test(name + ' (reverse)', header, original_side, original_main, expected, args, suffix, False) # test reverse order - - # test a simple call from one module to another. only one has a string (and constant memory initialization for it) - test('basics', '', ''' - #include <stdio.h> - extern int sidey(); - int main() { - printf("other says %d.", sidey()); - return 0; - } - ''', ''' - int sidey() { return 11; } - ''', 'other says 11.') - - # finalization of float variables should pass asm.js validation - test('floats', '', ''' - #include <stdio.h> - extern float sidey(); - int main() { - printf("other says %.2f.", sidey()+1); - return 0; - } - ''', ''' - float sidey() { return 11.5; } - ''', 'other says 12.50') + def nonfc(): + if os.environ.get('EMCC_FAST_COMPILER') != '0': return self.skip('todo in fastcomp') + + def test(name, header, main, side, expected, args=[], suffix='cpp', first=True): + print name + #t = main ; main = side ; side = t + original_main = main + original_side = side + if header: open(os.path.join(self.get_dir(), 'header.h'), 'w').write(header) + if type(main) == str: + open(os.path.join(self.get_dir(), 'main.' + suffix), 'w').write(main) + main = ['main.' + suffix] + if type(side) == str: + open(os.path.join(self.get_dir(), 'side.' + suffix), 'w').write(side) + side = ['side.' + suffix] + Popen([PYTHON, EMCC] + side + ['-o', 'side.js', '-s', 'SIDE_MODULE=1', '-O2'] + args).communicate() + # TODO: test with and without DISABLE_GL_EMULATION, check that file sizes change + Popen([PYTHON, EMCC] + main + ['-o', 'main.js', '-s', 'MAIN_MODULE=1', '-O2', '-s', 'DISABLE_GL_EMULATION=1'] + args).communicate() + Popen([PYTHON, EMLINK, 'main.js', 'side.js', 'together.js'], stdout=PIPE).communicate() + assert os.path.exists('together.js') + for engine in JS_ENGINES: + out = run_js('together.js', engine=engine, stderr=PIPE, full_output=True) + self.assertContained(expected, out) + if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) + if first: + shutil.copyfile('together.js', 'first.js') + test(name + ' (reverse)', header, original_side, original_main, expected, args, suffix, False) # test reverse order + + # test a simple call from one module to another. only one has a string (and constant memory initialization for it) + test('basics', '', ''' + #include <stdio.h> + extern int sidey(); + int main() { + printf("other says %d.", sidey()); + return 0; + } + ''', ''' + int sidey() { return 11; } + ''', 'other says 11.') - # memory initialization in both - test('multiple memory inits', '', r''' - #include <stdio.h> - extern void sidey(); - int main() { - printf("hello from main\n"); - sidey(); - return 0; - } - ''', r''' - #include <stdio.h> - void sidey() { printf("hello from side\n"); } - ''', 'hello from main\nhello from side\n') + # finalization of float variables should pass asm.js validation + test('floats', '', ''' + #include <stdio.h> + extern float sidey(); + int main() { + printf("other says %.2f.", sidey()+1); + return 0; + } + ''', ''' + float sidey() { return 11.5; } + ''', 'other says 12.50') - # function pointers - test('fp1', 'typedef void (*voidfunc)();', r''' - #include <stdio.h> - #include "header.h" - voidfunc sidey(voidfunc f); - void a() { printf("hello from funcptr\n"); } - int main() { - sidey(a)(); - return 0; - } - ''', ''' - #include "header.h" - voidfunc sidey(voidfunc f) { return f; } - ''', 'hello from funcptr\n') + # memory initialization in both + test('multiple memory inits', '', r''' + #include <stdio.h> + extern void sidey(); + int main() { + printf("hello from main\n"); + sidey(); + return 0; + } + ''', r''' + #include <stdio.h> + void sidey() { printf("hello from side\n"); } + ''', 'hello from main\nhello from side\n') - # function pointers with 'return' in the name - test('fp2', 'typedef void (*voidfunc)();', r''' - #include <stdio.h> - #include "header.h" - int sidey(voidfunc f); - void areturn0() { printf("hello 0\n"); } - void areturn1() { printf("hello 1\n"); } - void areturn2() { printf("hello 2\n"); } - int main(int argc, char **argv) { - voidfunc table[3] = { areturn0, areturn1, areturn2 }; - table[sidey(NULL)](); - return 0; - } - ''', ''' - #include "header.h" - int sidey(voidfunc f) { if (f) f(); return 1; } - ''', 'hello 1\n') + # function pointers + test('fp1', 'typedef void (*voidfunc)();', r''' + #include <stdio.h> + #include "header.h" + voidfunc sidey(voidfunc f); + void a() { printf("hello from funcptr\n"); } + int main() { + sidey(a)(); + return 0; + } + ''', ''' + #include "header.h" + voidfunc sidey(voidfunc f) { return f; } + ''', 'hello from funcptr\n') - # Global initializer - test('global init', '', r''' - #include <stdio.h> - struct Class { - Class() { printf("a new Class\n"); } - }; - static Class c; - int main() { - return 0; - } - ''', r''' - void nothing() {} - ''', 'a new Class\n') + # function pointers with 'return' in the name + test('fp2', 'typedef void (*voidfunc)();', r''' + #include <stdio.h> + #include "header.h" + int sidey(voidfunc f); + void areturn0() { printf("hello 0\n"); } + void areturn1() { printf("hello 1\n"); } + void areturn2() { printf("hello 2\n"); } + int main(int argc, char **argv) { + voidfunc table[3] = { areturn0, areturn1, areturn2 }; + table[sidey(NULL)](); + return 0; + } + ''', ''' + #include "header.h" + int sidey(voidfunc f) { if (f) f(); return 1; } + ''', 'hello 1\n') - # Multiple global initializers (LLVM generates overlapping names for them) - test('global inits', r''' - #include <stdio.h> - struct Class { - Class(const char *name) { printf("new %s\n", name); } - }; - ''', r''' - #include "header.h" - static Class c("main"); - int main() { - return 0; - } - ''', r''' - #include "header.h" - static Class c("side"); - ''', ['new main\nnew side\n', 'new side\nnew main\n']) + # Global initializer + test('global init', '', r''' + #include <stdio.h> + struct Class { + Class() { printf("a new Class\n"); } + }; + static Class c; + int main() { + return 0; + } + ''', r''' + void nothing() {} + ''', 'a new Class\n') - # Class code used across modules - test('codecall', r''' - #include <stdio.h> - struct Class { - Class(const char *name); - }; - ''', r''' - #include "header.h" - int main() { - Class c("main"); - return 0; - } - ''', r''' - #include "header.h" - Class::Class(const char *name) { printf("new %s\n", name); } - ''', ['new main\n']) - - # malloc usage in both modules - test('malloc', r''' - #include <stdlib.h> - #include <string.h> - char *side(const char *data); - ''', r''' - #include <stdio.h> - #include "header.h" - int main() { - char *temp = side("hello through side\n"); - char *ret = (char*)malloc(strlen(temp)+1); - strcpy(ret, temp); - temp[1] = 'x'; - puts(ret); - return 0; - } - ''', r''' - #include "header.h" - char *side(const char *data) { - char *ret = (char*)malloc(strlen(data)+1); - strcpy(ret, data); - return ret; - } - ''', ['hello through side\n']) + # Multiple global initializers (LLVM generates overlapping names for them) + test('global inits', r''' + #include <stdio.h> + struct Class { + Class(const char *name) { printf("new %s\n", name); } + }; + ''', r''' + #include "header.h" + static Class c("main"); + int main() { + return 0; + } + ''', r''' + #include "header.h" + static Class c("side"); + ''', ['new main\nnew side\n', 'new side\nnew main\n']) - # js library call - open('lib.js', 'w').write(r''' - mergeInto(LibraryManager.library, { - test_lib_func: function(x) { - return x + 17.2; + # Class code used across modules + test('codecall', r''' + #include <stdio.h> + struct Class { + Class(const char *name); + }; + ''', r''' + #include "header.h" + int main() { + Class c("main"); + return 0; } - }); - ''') - test('js-lib', 'extern "C" { extern double test_lib_func(int input); }', r''' - #include <stdio.h> - #include "header.h" - extern double sidey(); - int main2() { return 11; } - int main() { - int input = sidey(); - double temp = test_lib_func(input); - printf("other says %.2f\n", temp); - printf("more: %.5f, %d\n", temp, input); - return 0; - } - ''', r''' - #include <stdio.h> - #include "header.h" - extern int main2(); - double sidey() { - int temp = main2(); - printf("main2 sed: %d\n", temp); - printf("main2 sed: %u, %c\n", temp, temp/2); - return test_lib_func(temp); - } - ''', 'other says 45.2', ['--js-library', 'lib.js']) + ''', r''' + #include "header.h" + Class::Class(const char *name) { printf("new %s\n", name); } + ''', ['new main\n']) - # libc usage in one modules. must force libc inclusion in the main module if that isn't the one using mallinfo() - try: - os.environ['EMCC_FORCE_STDLIBS'] = 'libc' - test('malloc-1', r''' + # malloc usage in both modules + test('malloc', r''' + #include <stdlib.h> #include <string.h> - int side(); + char *side(const char *data); ''', r''' #include <stdio.h> #include "header.h" int main() { - printf("|%d|\n", side()); + char *temp = side("hello through side\n"); + char *ret = (char*)malloc(strlen(temp)+1); + strcpy(ret, temp); + temp[1] = 'x'; + puts(ret); return 0; } ''', r''' - #include <stdlib.h> - #include <malloc.h> #include "header.h" - int side() { - struct mallinfo m = mallinfo(); - return m.arena > 1; + char *side(const char *data) { + char *ret = (char*)malloc(strlen(data)+1); + strcpy(ret, data); + return ret; } - ''', ['|1|\n']) - finally: - del os.environ['EMCC_FORCE_STDLIBS'] - - # iostream usage in one and std::string in both - test('iostream', r''' - #include <iostream> - #include <string> - std::string side(); - ''', r''' - #include "header.h" - int main() { - std::cout << "hello from main " << side() << std::endl; - return 0; - } - ''', r''' - #include "header.h" - std::string side() { return "and hello from side"; } - ''', ['hello from main and hello from side\n']) - - # followup to iostream test: a second linking - print 'second linking of a linking output' - open('moar.cpp', 'w').write(r''' - #include <iostream> - struct Moar { - Moar() { std::cout << "moar!" << std::endl; } - }; - Moar m; - ''') - Popen([PYTHON, EMCC, 'moar.cpp', '-o', 'moar.js', '-s', 'SIDE_MODULE=1', '-O2']).communicate() - Popen([PYTHON, EMLINK, 'together.js', 'moar.js', 'triple.js'], stdout=PIPE).communicate() - assert os.path.exists('triple.js') - for engine in JS_ENGINES: - out = run_js('triple.js', engine=engine, stderr=PIPE, full_output=True) - self.assertContained('moar!\nhello from main and hello from side\n', out) - if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) - - # zlib compression library. tests function pointers in initializers and many other things - test('zlib', '', open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(), - self.get_zlib_library(), - open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), - args=['-I' + path_from_root('tests', 'zlib')], suffix='c') - - use_cmake = WINDOWS - bullet_library = get_bullet_library(self, use_cmake) - - # bullet physics engine. tests all the things - test('bullet', '', open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(), - bullet_library, - [open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), # different roundings - open(path_from_root('tests', 'bullet', 'output2.txt'), 'r').read(), - open(path_from_root('tests', 'bullet', 'output3.txt'), 'r').read()], - args=['-I' + path_from_root('tests', 'bullet', 'src')]) + ''', ['hello through side\n']) + + # js library call + open('lib.js', 'w').write(r''' + mergeInto(LibraryManager.library, { + test_lib_func: function(x) { + return x + 17.2; + } + }); + ''') + test('js-lib', 'extern "C" { extern double test_lib_func(int input); }', r''' + #include <stdio.h> + #include "header.h" + extern double sidey(); + int main2() { return 11; } + int main() { + int input = sidey(); + double temp = test_lib_func(input); + printf("other says %.2f\n", temp); + printf("more: %.5f, %d\n", temp, input); + return 0; + } + ''', r''' + #include <stdio.h> + #include "header.h" + extern int main2(); + double sidey() { + int temp = main2(); + printf("main2 sed: %d\n", temp); + printf("main2 sed: %u, %c\n", temp, temp/2); + return test_lib_func(temp); + } + ''', 'other says 45.2', ['--js-library', 'lib.js']) + + # libc usage in one modules. must force libc inclusion in the main module if that isn't the one using mallinfo() + try: + os.environ['EMCC_FORCE_STDLIBS'] = 'libc' + test('malloc-1', r''' + #include <string.h> + int side(); + ''', r''' + #include <stdio.h> + #include "header.h" + int main() { + printf("|%d|\n", side()); + return 0; + } + ''', r''' + #include <stdlib.h> + #include <malloc.h> + #include "header.h" + int side() { + struct mallinfo m = mallinfo(); + return m.arena > 1; + } + ''', ['|1|\n']) + finally: + del os.environ['EMCC_FORCE_STDLIBS'] + + # iostream usage in one and std::string in both + test('iostream', r''' + #include <iostream> + #include <string> + std::string side(); + ''', r''' + #include "header.h" + int main() { + std::cout << "hello from main " << side() << std::endl; + return 0; + } + ''', r''' + #include "header.h" + std::string side() { return "and hello from side"; } + ''', ['hello from main and hello from side\n']) + + # followup to iostream test: a second linking + print 'second linking of a linking output' + open('moar.cpp', 'w').write(r''' + #include <iostream> + struct Moar { + Moar() { std::cout << "moar!" << std::endl; } + }; + Moar m; + ''') + Popen([PYTHON, EMCC, 'moar.cpp', '-o', 'moar.js', '-s', 'SIDE_MODULE=1', '-O2']).communicate() + Popen([PYTHON, EMLINK, 'together.js', 'moar.js', 'triple.js'], stdout=PIPE).communicate() + assert os.path.exists('triple.js') + for engine in JS_ENGINES: + out = run_js('triple.js', engine=engine, stderr=PIPE, full_output=True) + self.assertContained('moar!\nhello from main and hello from side\n', out) + if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) + + # zlib compression library. tests function pointers in initializers and many other things + test('zlib', '', open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(), + self.get_zlib_library(), + open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), + args=['-I' + path_from_root('tests', 'zlib')], suffix='c') + + use_cmake = WINDOWS + bullet_library = get_bullet_library(self, use_cmake) + + # bullet physics engine. tests all the things + test('bullet', '', open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(), + bullet_library, + [open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), # different roundings + open(path_from_root('tests', 'bullet', 'output2.txt'), 'r').read(), + open(path_from_root('tests', 'bullet', 'output3.txt'), 'r').read()], + args=['-I' + path_from_root('tests', 'bullet', 'src')]) + nonfastcomp(nonfc) def test_outline(self): def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp'): @@ -914,11 +917,11 @@ This pointer might make sense in another type signature: i: _my_func for test_opts, expected_ranges in [ ([], { 100: (190, 500), - 250: (200, 500), - 500: (250, 500), + 250: (200, 600), + 500: (200, 700), 1000: (230, 1000), - 2000: (380, 2000), - 5000: (800, 5000), + 2000: (300, 2000), + 5000: (500, 5000), 0: (1500, 5000) }), (['-O2'], { @@ -1817,26 +1820,28 @@ This pointer might make sense in another type signature: i: _my_func assert 'error' not in err, 'Unexpected stderr: ' + err def test_chunking(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('not relevant for fastcomp, only checks js compiler chunking') - if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') - if os.environ.get('EMCC_CORES'): return self.skip('cannot run if cores are altered') - if multiprocessing.cpu_count() < 2: return self.skip('need multiple cores') - try: - os.environ['EMCC_DEBUG'] = '1' - os.environ['EMCC_CORES'] = '2' # standardize over machines - for asm, linkable, chunks in [ - (0, 0, 2), (0, 1, 2), - (1, 0, 2), (1, 1, 2) - ]: - print asm, linkable, chunks - output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1', '-s', 'LINKABLE=%d' % linkable, '-s', 'ASM_JS=%d' % asm] + (['-O2'] if asm else []), stdout=PIPE, stderr=PIPE).communicate() - ok = False - for c in range(chunks, chunks+2): - ok = ok or ('phase 2 working on %d chunks' % c in err) - assert ok, err - finally: - del os.environ['EMCC_DEBUG'] - del os.environ['EMCC_CORES'] + def nonfc(): + if os.environ.get('EMCC_FAST_COMPILER') != '0': return self.skip('not relevant for fastcomp, only checks js compiler chunking') + if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') + if os.environ.get('EMCC_CORES'): return self.skip('cannot run if cores are altered') + if multiprocessing.cpu_count() < 2: return self.skip('need multiple cores') + try: + os.environ['EMCC_DEBUG'] = '1' + os.environ['EMCC_CORES'] = '2' # standardize over machines + for asm, linkable, chunks in [ + (0, 0, 2), (0, 1, 2), + (1, 0, 2), (1, 1, 2) + ]: + print asm, linkable, chunks + output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1', '-s', 'LINKABLE=%d' % linkable, '-s', 'ASM_JS=%d' % asm] + (['-O2'] if asm else []), stdout=PIPE, stderr=PIPE).communicate() + ok = False + for c in range(chunks, chunks+2): + ok = ok or ('phase 2 working on %d chunks' % c in err) + assert ok, err + finally: + del os.environ['EMCC_DEBUG'] + del os.environ['EMCC_CORES'] + nonfastcomp(nonfc) def test_debuginfo(self): if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') @@ -1868,23 +1873,25 @@ This pointer might make sense in another type signature: i: _my_func assert 'If you see this - the world is all right!' in output def test_embind(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': 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 + 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) def test_llvm_nativizer(self): try: @@ -2362,7 +2369,7 @@ int main() { assert '*** PCH/Modules Loaded:\nModule: header.h.gch' not in err[1], err[1] def test_warn_unaligned(self): - if os.environ.get('EMCC_FAST_COMPILER') != '1': return self.skip('need fastcomp') + if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('need fastcomp') open('src.cpp', 'w').write(r''' #include <stdio.h> static const double grid[4][2] = {{-3 / 3., -1 / 3.}, @@ -2382,3 +2389,175 @@ int main() { assert 'emcc: warning: unaligned store' in output[1] assert '@line 9 "src.cpp"' in output[1] + def test_no_exit_runtime(self): + open('code.cpp', 'w').write(r''' +#include <stdio.h> + +template<int x> +struct Waste { + Waste() { + printf("coming around %d\n", x); + } + ~Waste() { + printf("going away %d\n", x); + } +}; + +Waste<1> w1; +Waste<2> w2; +Waste<3> w3; +Waste<4> w4; +Waste<5> w5; + +int main(int argc, char **argv) { + return 0; +} +''') + + for no_exit in [0, 1]: + for opts in [[], ['-O1'], ['-O2', '-g2'], ['-O2', '-g2', '--llvm-lto', '1']]: + print no_exit, opts + Popen([PYTHON, EMCC] + opts + ['code.cpp', '-s', 'NO_EXIT_RUNTIME=' + str(no_exit)]).communicate() + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True, engine=NODE_JS) + src = open('a.out.js').read() + exit = 1-no_exit + assert 'coming around' in output + assert ('going away' in output) == exit, 'destructors should not run if no exit' + assert ('_ZN5WasteILi2EED1Ev' in src) == exit, 'destructors should not appear if no exit' + assert ('atexit(' in src) == exit, 'atexit should not appear or be called' + + def test_os_oz(self): + if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') + try: + os.environ['EMCC_DEBUG'] = '1' + for args, expect in [ + (['-O1'], 'LLVM opts: -O1'), + (['-O2'], 'LLVM opts: -O3'), + (['-Os'], 'LLVM opts: -Os'), + (['-Oz'], 'LLVM opts: -Oz'), + (['-O3'], 'LLVM opts: -O3'), + ]: + print args, expect + output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')] + args, stdout=PIPE, stderr=PIPE).communicate() + self.assertContained(expect, err) + self.assertContained('hello, world!', run_js('a.out.js')) + finally: + del os.environ['EMCC_DEBUG'] + + def test_global_inits(self): + open('inc.h', 'w').write(r''' +#include <stdio.h> + +template<int x> +struct Waste { + int state; + Waste() : state(10) {} + void test(int a) { + printf("%d\n", a + state); + } + ~Waste() { + printf("going away %d\n", x); + } +}; + +Waste<3> *getMore(); + +''') + open('main.cpp', 'w').write(r''' +#include "inc.h" + +Waste<1> mw1; +Waste<2> mw2; + +int main(int argc, char **argv) { + printf("argc: %d\n", argc); + mw1.state += argc; + mw2.state += argc; + mw1.test(5); + mw2.test(6); + getMore()->test(0); + return 0; +} +''') + + open('side.cpp', 'w').write(r''' +#include "inc.h" + +Waste<3> sw3; + +Waste<3> *getMore() { + return &sw3; +} +''') + + for opts, has_global in [ + (['-O2', '-g'], True), + (['-O2', '-g', '-s', 'NO_EXIT_RUNTIME=1'], False), # no-exit-runtime removes the atexits, and then globalgce can work it's magic to remove the global initializer entirely + (['-Os', '-g'], True), + (['-Os', '-g', '-s', 'NO_EXIT_RUNTIME=1'], False), + (['-O2', '-g', '--llvm-lto', '1'], True), + (['-O2', '-g', '-s', 'NO_EXIT_RUNTIME=1', '--llvm-lto', '1'], False), + ]: + print opts, has_global + Popen([PYTHON, EMCC, 'main.cpp', '-c'] + opts).communicate() + Popen([PYTHON, EMCC, 'side.cpp', '-c'] + opts).communicate() + Popen([PYTHON, EMCC, 'main.o', 'side.o'] + opts).communicate() + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True, engine=NODE_JS) + src = open('a.out.js').read() + self.assertContained('argc: 1\n16\n17\n10\n', run_js('a.out.js')) + assert ('_GLOBAL_' in src) == has_global + + def test_implicit_func(self): + open('src.c', 'w').write(r''' +#include <stdio.h> +int main() +{ + printf("hello %d\n", strnlen("waka", 2)); // Implicit declaration, no header, for strnlen + int (*my_strnlen)(char*, ...) = strnlen; + printf("hello %d\n", my_strnlen("shaka", 2)); + return 0; +} +''') + + IMPLICIT_WARNING = '''warning: implicit declaration of function 'strnlen' is invalid in C99''' + IMPLICIT_ERROR = '''error: implicit declaration of function 'strnlen' is invalid in C99''' + + for opts, expected, compile_expected in [ + ([], None, [IMPLICIT_ERROR]), + (['-Wno-error=implicit-function-declaration'], ['abort()', 'it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this'], [IMPLICIT_WARNING]), # turn error into warning + (['-Wno-implicit-function-declaration'], ['abort()', 'it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this'], []), # turn error into nothing at all + (['-Wno-error=implicit-function-declaration', '-s', 'ASSERTIONS=2'], ['abort()', 'This pointer might make sense in another type signature'], []), + (['-Wno-error=implicit-function-declaration', '-O1'], ['hello 2\nhello 5\n'], []), # invalid output - second arg is sent as varargs, but needs to be int. llvm optimizer avoided the crash silently, caused undefined behavior... at least people can debug this by running an -O0 build. + ]: + print opts, expected + stdout, stderr = Popen([PYTHON, EMCC, 'src.c'] + opts, stderr=PIPE).communicate() + for ce in compile_expected + ['''warning: incompatible pointer types''']: + self.assertContained(ce, stderr) + if expected is None: + assert not os.path.exists('a.out.js') + else: + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True) + for e in expected: + self.assertContained(e, output) + + def test_incorrect_static_call(self): + for opts in [0, 1]: + for asserts in [0, 1]: + extra = [] + if opts != 1-asserts: extra = ['-s', 'ASSERTIONS=' + str(asserts)] + cmd = [PYTHON, EMCC, path_from_root('tests', 'cases', 'sillyfuncast2_noasm.ll'), '-O' + str(opts)] + extra + print cmd + stdout, stderr = Popen(cmd, stderr=PIPE).communicate() + assert ('''unexpected number of arguments 3 in call to 'doit', should be 2''' in stderr) == asserts, stderr + assert ('''unexpected return type i32 in call to 'doit', should be void''' in stderr) == asserts, stderr + assert ('''unexpected argument type float at index 1 in call to 'doit', should be i32''' in stderr) == asserts, stderr + + def test_llvm_lit(self): + llvm_src = LLVM_ROOT + while not os.path.exists(os.path.join(llvm_src, 'emscripten-version.txt')): llvm_src = os.path.dirname(llvm_src) + cmd = [os.path.join(LLVM_ROOT, 'llvm-lit'), os.path.join(llvm_src, 'test', 'CodeGen', 'JS')] + print cmd + p = Popen(cmd) + p.communicate() + assert p.returncode == 0, 'LLVM tests must pass with exit code 0' + |