12
12
import importlib
13
13
import itertools
14
14
import json
15
+ from math import inf
15
16
import os
16
17
import re
17
18
import select
@@ -9245,6 +9246,76 @@ def build(args):
9245
9246
# adding --metrics should not affect code size
9246
9247
self.assertEqual(base_size, os.path.getsize('a.out.wasm'))
9247
9248
9249
+ def check_output_sizes(self, *outputs: str, **metadata):
9250
+ test_name = self.id().split('.')[-1]
9251
+ results_file = test_file('code_size', test_name + '.json')
9252
+
9253
+ expected_results: dict = {}
9254
+ try:
9255
+ expected_results = json.loads(read_file(results_file))
9256
+ except Exception:
9257
+ if not common.EMTEST_REBASELINE:
9258
+ raise
9259
+
9260
+ obtained_results = {}
9261
+
9262
+ total_output_size = 0
9263
+ total_expected_size = 0
9264
+ total_output_size_gz = 0
9265
+ total_expected_size_gz = 0
9266
+ for f in outputs:
9267
+ f_gz = f + '.gz'
9268
+ expected_size = expected_results.get(f, inf)
9269
+ expected_size_gz = expected_results.get(f_gz, inf)
9270
+ contents = read_binary(f)
9271
+ size = len(contents)
9272
+ size_gz = len(gzip.compress(contents))
9273
+
9274
+ obtained_results[f] = size
9275
+ obtained_results[f_gz] = size_gz
9276
+
9277
+ if not common.EMTEST_REBASELINE and size != expected_size and (f.endswith(('.js', '.html'))):
9278
+ print('Contents of ' + f + ': ')
9279
+ print(contents.decode('utf-8', errors='replace'))
9280
+
9281
+ def print_diff(title, actual, expected):
9282
+ diff = actual - expected
9283
+ s = f'{title}={actual}, expected {expected}'
9284
+ if diff > 0:
9285
+ s += f', delta={diff} ({diff * 100.0 / expected:+.2f}%)'
9286
+ print(s)
9287
+
9288
+ print_diff(f'size of {f}', size, expected_size)
9289
+ print_diff(f'size of {f_gz}', size_gz, expected_size_gz)
9290
+
9291
+ # N.B. even though the test code above prints out gzip compressed sizes, regression testing is done against uncompressed sizes
9292
+ # this is because optimizing for compressed sizes can be unpredictable and sometimes counterproductive
9293
+ total_output_size += size
9294
+ total_expected_size += expected_size
9295
+
9296
+ total_output_size_gz += size_gz
9297
+ total_expected_size_gz += expected_size_gz
9298
+
9299
+ if len(outputs) > 1:
9300
+ obtained_results['total'] = total_output_size
9301
+ obtained_results['total_gz'] = total_output_size_gz
9302
+
9303
+ print_diff('Total output size', total_output_size, total_expected_size)
9304
+ print_diff('Total output size gzipped', total_output_size_gz, total_expected_size_gz)
9305
+
9306
+ obtained_results.update(metadata)
9307
+
9308
+ if common.EMTEST_REBASELINE:
9309
+ create_file(results_file, json.dumps(obtained_results, indent=2) + '\n', absolute=True)
9310
+ else:
9311
+ if total_output_size > total_expected_size:
9312
+ print(f'Oops, overall generated code size regressed by {total_output_size - total_expected_size} bytes!')
9313
+ print('If this is expected, rerun the test with --rebaseline to update the expected sizes')
9314
+ if total_output_size < total_expected_size:
9315
+ print(f'Hey amazing, overall generated code size was improved by {total_expected_size - total_output_size} bytes!')
9316
+ print('If this is expected, rerun the test with --rebaseline to update the expected sizes')
9317
+ self.assertDictEqual(obtained_results, expected_results)
9318
+
9248
9319
@crossplatform
9249
9320
def test_unoptimized_code_size(self):
9250
9321
# We don't care too about unoptimized code size but we would like to keep it
@@ -9255,7 +9326,7 @@ def test_unoptimized_code_size(self):
9255
9326
self.build('hello_world.c', cflags=['-O0', '--output-eol=linux', '-sASSERTIONS=0'], output_basename='no_asserts')
9256
9327
self.build('hello_world.c', cflags=['-O0', '--output-eol=linux', '-sSTRICT'], output_basename='strict')
9257
9328
9258
- self.check_output_sizes([ 'hello_world.js', 'hello_world.wasm', 'no_asserts.js', 'no_asserts.wasm', 'strict.js', 'strict.wasm'] )
9329
+ self.check_output_sizes('hello_world.js', 'hello_world.wasm', 'no_asserts.js', 'no_asserts.wasm', 'strict.js', 'strict.wasm')
9259
9330
9260
9331
def run_codesize_test(self, filename, cflags, check_funcs=True, check_full_js=False):
9261
9332
# in -Os, -Oz, we remove imports wasm doesn't need
@@ -9328,7 +9399,7 @@ def strip_numeric_suffixes(funcname):
9328
9399
funcs.sort()
9329
9400
info['funcs'] = [strip_numeric_suffixes(f) for f in funcs]
9330
9401
9331
- self.check_output_sizes(outputs, info)
9402
+ self.check_output_sizes(* outputs, ** info)
9332
9403
9333
9404
@parameterized({
9334
9405
'O0': ([], True),
@@ -11889,7 +11960,7 @@ def test_minimal_runtime_code_size(self, test_name, wasm2js, compare_js_output=F
11889
11960
self.run_process(terser + ['-b', 'beautify=true', 'a.js', '-o', 'pretty.js'], env=shared.env_with_node_in_path())
11890
11961
self.assertFileContents(js_out, read_file('pretty.js'))
11891
11962
11892
- self.check_output_sizes(outputs)
11963
+ self.check_output_sizes(* outputs)
11893
11964
11894
11965
# Tests the library_c_preprocessor.js functionality.
11895
11966
@crossplatform
@@ -12333,7 +12404,7 @@ def test(args):
12333
12404
# Changing this option to [] should decrease code size.
12334
12405
self.assertLess(changed, normal)
12335
12406
# Check an absolute code size as well.
12336
- self.check_output_sizes([ 'a.out.js'] )
12407
+ self.check_output_sizes('a.out.js')
12337
12408
12338
12409
def test_INCOMING_MODULE_JS_API_missing(self):
12339
12410
create_file('pre.js', 'Module.onRuntimeInitialized = () => out("initialized");')
0 commit comments