aboutsummaryrefslogtreecommitdiff
path: root/tests/runner.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/runner.py')
-rwxr-xr-xtests/runner.py251
1 files changed, 243 insertions, 8 deletions
diff --git a/tests/runner.py b/tests/runner.py
index 2ce72240..51745180 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -16,7 +16,7 @@ so you may prefer to use fewer cores here.
'''
from subprocess import Popen, PIPE, STDOUT
-import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, tempfile, re, difflib, webbrowser, hashlib, threading, platform, BaseHTTPServer, multiprocessing, functools, stat
+import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, re, difflib, webbrowser, hashlib, threading, platform, BaseHTTPServer, multiprocessing, functools, stat
if len(sys.argv) == 1:
print '''
@@ -265,6 +265,12 @@ process(sys.argv[1])
else:
assert 'memory initializer */' in open(filename + '.o.js').read()
+ def validate_asmjs(self, err):
+ if 'uccessfully compiled asm.js code' in err and 'asm.js link error' not in err:
+ print >> sys.stderr, "[was asm.js'ified]"
+ elif 'asm.js' in err: # if no asm.js error, then not an odin build
+ raise Exception("did NOT asm.js'ify")
+
def run_generated_code(self, engine, filename, args=[], check_timeout=True, output_nicerizer=None):
stdout = os.path.join(self.get_dir(), 'stdout') # use files, as PIPE can get too full and hang us
stderr = os.path.join(self.get_dir(), 'stderr')
@@ -279,10 +285,7 @@ process(sys.argv[1])
out = open(stdout, 'r').read()
err = open(stderr, 'r').read()
if engine == SPIDERMONKEY_ENGINE and Settings.ASM_JS:
- if 'uccessfully compiled asm.js code' in err and 'asm.js link error' not in err:
- print >> sys.stderr, "[was asm.js'ified]"
- elif 'asm.js' in err: # if no asm.js error, then not an odin build
- raise Exception("did NOT asm.js'ify")
+ self.validate_asmjs(err)
if output_nicerizer:
ret = output_nicerizer(out, err)
else:
@@ -8582,7 +8585,7 @@ def process(filename):
del os.environ['EMCC_DEBUG']
for debug in [1,2]:
def clean(text):
- return text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n')
+ return text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('{\n}', '{}')
self.assertIdentical(clean(open('release.js').read()), clean(open('debug%d.js' % debug).read())) # EMCC_DEBUG=1 mode must not generate different code!
print >> sys.stderr, 'debug check %d passed too' % debug
@@ -9637,7 +9640,9 @@ def process(filename):
# objects when generating source maps, so we want to make sure the
# optimizer can deal with both types.
out_file = re.sub(' *//@.*$', '', out_file, flags=re.MULTILINE)
- self.assertIdentical(no_maps_file, out_file)
+ def clean(code):
+ return code.replace('{\n}', '{}')
+ self.assertIdentical(clean(no_maps_file), clean(out_file))
map_filename = out_filename + '.map'
data = json.load(open(map_filename, 'r'))
self.assertIdentical(out_filename, data['file'])
@@ -10606,6 +10611,215 @@ f.close()
self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js')))
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):
+ 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')
+ out = run_js('together.js', engine=SPIDERMONKEY_ENGINE, stderr=PIPE, full_output=True)
+ self.assertContained(expected, out)
+ 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')
+
+ # 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
+ 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')
+
+ # 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'])
+
+ # 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'])
+
+ # 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'])
+
+ # 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_library('zlib', os.path.join('libz.a'), make_args=['libz.a']),
+ open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(),
+ args=['-I' + path_from_root('tests', 'zlib')], suffix='c')
+
+ # bullet physics engine. tests all the things
+ test('bullet', '', open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(),
+ 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')]),
+ [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')])
+
+
def test_symlink(self):
if os.name == 'nt':
return self.skip('Windows FS does not need to be tested for symlinks support, since it does not have them.')
@@ -13893,7 +14107,22 @@ elif 'sanity' in str(sys.argv):
def test_firstrun(self):
for command in commands:
wipe()
- output = self.do(command)
+
+ 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)
@@ -13901,6 +14130,12 @@ elif 'sanity' in str(sys.argv):
self.assertContained('It contains our best guesses for the important paths, which are:', output)
self.assertContained('LLVM_ROOT', output)
self.assertContained('NODE_JS', output)
+ self.assertContained('PYTHON', 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.assertIdentical(os.path.join(temp_bin, 'python2'), re.search("^ *PYTHON *= (.*)$", 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