diff --git a/.github/workflows/gh-actions.yml b/.github/workflows/gh-actions.yml index 023d7e4822..9982bec23e 100644 --- a/.github/workflows/gh-actions.yml +++ b/.github/workflows/gh-actions.yml @@ -56,6 +56,34 @@ jobs: - run: $RUNNER -q --jerry-tests --buildoptions=--compile-flag=-m32,--cpointer-32bit=on - run: $RUNNER -q --jerry-tests --buildoptions=--compile-flag=-m32,--cpointer-32bit=on --build-debug + Win_x86-64_Conformance_Tests_ESNext: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - run: python $env:RUNNER --test262 update + + Win_x86-64_Conformance_Tests_ESNext_Debug: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - run: python $env:RUNNER --test262 update --build-debug + + Win_x86-64_Tests: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - run: python $env:RUNNER -q --jerry-tests + - run: python $env:RUNNER -q --unittests + - run: python $env:RUNNER -q --buildoption-test + + Win_x86-64_Tests_Debug: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - run: python $env:RUNNER -q --jerry-tests --build-debug + - run: python $env:RUNNER -q --unittests --build-debug + - run: python $env:RUNNER -q --buildoption-test --build-debug + OSX_x86-64_Build_Correctness_Unit_Tests: runs-on: macos-13 steps: diff --git a/README.md b/README.md index eac8ef0c45..ebe84d3f96 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ # JerryScript: JavaScript engine for the Internet of Things [![License](https://img.shields.io/badge/licence-Apache%202.0-brightgreen.svg?style=flat)](LICENSE) [![GitHub Actions Status](https://github.com/jerryscript-project/jerryscript/workflows/JerryScript%20CI/badge.svg)](https://github.com/jerryscript-project/jerryscript/actions) -[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/ct8reap35u2vooa5/branch/master?svg=true)](https://ci.appveyor.com/project/jerryscript-project/jerryscript/branch/master) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fjerryscript-project%2Fjerryscript.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fjerryscript-project%2Fjerryscript?ref=badge_shield) [![IRC Channel](https://img.shields.io/badge/chat-on%20freenode-brightgreen.svg)](https://kiwiirc.com/client/irc.freenode.net/#jerryscript) diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index cc215ac00d..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: "{build}" -branches: - except: - - coverity_scan - - gh_pages -skip_tags: true - -# Build matrix setup. -image: - - Visual Studio 2017 -configuration: - - Debug - - Release -platform: - - x64 - - Win32 - -# Steps of a job. -init: - - cmake -version -before_build: - - if "%PLATFORM%"=="Win32" cmake -G"Visual Studio 15 2017" -Bbuild -H. -DJERRY_DEBUGGER=ON - - if "%PLATFORM%"=="x64" cmake -G"Visual Studio 15 2017 Win64" -Bbuild -H. -DJERRY_DEBUGGER=ON -build: - project: build\Jerry.sln - parallel: false # FIXME: This should not be needed but right now it is: msbuild generates all amalgamated files twice, at the same time in parallel builds, leading to I/O errors. - verbosity: minimal diff --git a/jerry-port/win/jerry-port-win-date.c b/jerry-port/win/jerry-port-win-date.c index 91aee32646..ea3823eabe 100644 --- a/jerry-port/win/jerry-port-win-date.c +++ b/jerry-port/win/jerry-port-win-date.c @@ -23,8 +23,8 @@ #include #include -#define UNIX_EPOCH_IN_TICKS 116444736000000000ull /* difference between 1970 and 1601 */ -#define TICKS_PER_MS 10000ull /* 1 tick is 100 nanoseconds */ +#define UNIX_EPOCH_IN_TICKS 116444736000000000LL /* difference between 1970 and 1601 */ +#define TICKS_PER_MS 10000LL /* 1 tick is 100 nanoseconds */ /* * If you take the limit of SYSTEMTIME (last millisecond in 30827) then you end up with @@ -44,15 +44,9 @@ * https://support.microsoft.com/en-us/help/167296/how-to-convert-a-unix-time-t-to-a-win32-filetime-or-systemtime */ static void -unix_time_to_filetime (double t, LPFILETIME ft_p) +unix_time_to_filetime (LONGLONG t, LPFILETIME ft_p) { - LONGLONG ll = (LONGLONG) t * TICKS_PER_MS + UNIX_EPOCH_IN_TICKS; - - /* FILETIME values before the epoch are invalid. */ - if (ll < 0) - { - ll = 0; - } + LONGLONG ll = t * TICKS_PER_MS + UNIX_EPOCH_IN_TICKS; ft_p->dwLowDateTime = (DWORD) ll; ft_p->dwHighDateTime = (DWORD) (ll >> 32); @@ -63,13 +57,15 @@ unix_time_to_filetime (double t, LPFILETIME ft_p) * * @return unix time */ -static double +static LONGLONG filetime_to_unix_time (LPFILETIME ft_p) { ULARGE_INTEGER date; + LONGLONG ll; date.HighPart = ft_p->dwHighDateTime; date.LowPart = ft_p->dwLowDateTime; - return (double) (((LONGLONG) date.QuadPart - UNIX_EPOCH_IN_TICKS) / TICKS_PER_MS); + ll = date.QuadPart - UNIX_EPOCH_IN_TICKS; + return ll / TICKS_PER_MS; } /* filetime_to_unix_time */ /** @@ -85,6 +81,7 @@ jerry_port_local_tza (double unix_ms) FILETIME local; SYSTEMTIME utc_sys; SYSTEMTIME local_sys; + LONGLONG t = (LONGLONG) (unix_ms); /* * If the time is earlier than the date 1601-01-02, then always using date 1601-01-02 to @@ -93,23 +90,23 @@ jerry_port_local_tza (double unix_ms) * after converting between local time and utc time, the time may be earlier than 1601-01-01 * in UTC time, that exceeds the FILETIME representation range. */ - if (unix_ms < (double) UNIX_EPOCH_DATE_1601_01_02) + if (t < UNIX_EPOCH_DATE_1601_01_02) { - unix_ms = (double) UNIX_EPOCH_DATE_1601_01_02; + t = UNIX_EPOCH_DATE_1601_01_02; } /* Like above, do not use the last supported day */ - if (unix_ms > (double) UNIX_EPOCH_DATE_30827_12_29) + if (t > UNIX_EPOCH_DATE_30827_12_29) { - unix_ms = (double) UNIX_EPOCH_DATE_30827_12_29; + t = UNIX_EPOCH_DATE_30827_12_29; } - unix_time_to_filetime (unix_ms, &utc); + unix_time_to_filetime (t, &utc); if (FileTimeToSystemTime (&utc, &utc_sys) && SystemTimeToTzSpecificLocalTime (NULL, &utc_sys, &local_sys) && SystemTimeToFileTime (&local_sys, &local)) { - double unix_local = filetime_to_unix_time (&local); - return (int32_t) (unix_local - unix_ms); + LONGLONG unix_local = filetime_to_unix_time (&local); + return (int32_t) (unix_local - t); } return 0; @@ -125,7 +122,7 @@ jerry_port_current_time (void) { FILETIME ft; GetSystemTimeAsFileTime (&ft); - return filetime_to_unix_time (&ft); + return (double) filetime_to_unix_time (&ft); } /* jerry_port_current_time */ #endif /* defined(_WIN32) */ diff --git a/tests/jerry/date-getters.js b/tests/jerry/date-getters.js index 77cd9042b1..0a2061fd38 100644 --- a/tests/jerry/date-getters.js +++ b/tests/jerry/date-getters.js @@ -110,8 +110,16 @@ assert (new Date(-1, -1, -1, -1, -1, -1, -1, -1).getMilliseconds() === 999); assert (isNaN(new Date(20000000, 0).getFullYear())); assert (new Date(0, 0).getFullYear() === 1900); assert (new Date(1.2, 0).getFullYear() === 1901); + +/* 7. test case */ +/* A Number can exactly represent all integers from -9,007,199,254,740,992 to 9,007,199,254,740,992 (21.1.2.8 and 21.1.2.6). + A time value supports a slightly smaller range of -8,640,000,000,000,000 to 8,640,000,000,000,000 milliseconds. */ assert((new Date(8640000000000000).getFullYear()) == 275760); assert(isNaN(new Date(8640000000000001).getFullYear())); +assert((new Date(-8640000000000000).getFullYear()) == -271821); +assert(isNaN(new Date(-8640000000000001).getFullYear())); + +/* 8. test case */ assert((new Date(-271821, 3, 21).getFullYear()) == -271821); assert(isNaN(new Date(1970, 0, -100000000).getFullYear())); assert(new Date(1970, 0, -100000000 + 1).getFullYear() == -271821); diff --git a/tools/run-tests.py b/tools/run-tests.py index 64bc1ed827..72914e2fbb 100755 --- a/tools/run-tests.py +++ b/tools/run-tests.py @@ -23,14 +23,14 @@ import sys import settings -if sys.version_info.major >= 3: - from runners import util -else: - sys.path.append(os.path.dirname(os.path.realpath(__file__)) + '/runners') - import util +from runners import util +from runners.util import TERM_NORMAL, TERM_YELLOW, TERM_BLUE, TERM_RED OUTPUT_DIR = os.path.join(settings.PROJECT_DIR, 'build', 'tests') +# The saved result of get_arguments() for globally access +options = None # pylint: disable=invalid-name + Options = collections.namedtuple('Options', ['name', 'build_args', 'test_args', 'skip']) Options.__new__.__defaults__ = ([], [], False) @@ -170,6 +170,8 @@ def get_arguments(): help='Run "magic string source code generator should be executed" check') parser.add_argument('--build-debug', action='store_true', help='Build debug version jerryscript') + parser.add_argument('--run-check-timeout', type=int, default=30 * 60, + help='Specify run_check timeout, default to 30 minutes, unit: second') parser.add_argument('--jerry-debugger', action='store_true', help='Run jerry-debugger tests') parser.add_argument('--jerry-tests', action='store_true', @@ -197,11 +199,6 @@ def get_arguments(): BINARY_CACHE = {} -TERM_NORMAL = '\033[0m' -TERM_YELLOW = '\033[1;33m' -TERM_BLUE = '\033[1;34m' -TERM_RED = '\033[1;31m' - def report_command(cmd_type, cmd, env=None): sys.stderr.write(f'{TERM_BLUE}{cmd_type}{TERM_NORMAL}\n') if env is not None: @@ -210,14 +207,16 @@ def report_command(cmd_type, cmd, env=None): sys.stderr.write(f"{TERM_BLUE}" + f" \\{TERM_NORMAL}\n\t{TERM_BLUE}".join(cmd) + f"{TERM_NORMAL}\n") + sys.stderr.flush() def report_skip(job): sys.stderr.write(f'{TERM_YELLOW}Skipping: {job.name}') if job.skip: sys.stderr.write(f' ({job.skip})') sys.stderr.write(f'{TERM_NORMAL}\n') + sys.stderr.flush() -def create_binary(job, options): +def create_binary(job): build_args = job.build_args[:] build_dir_path = os.path.join(options.outdir, job.name) if options.build_debug: @@ -246,13 +245,17 @@ def create_binary(job, options): if binary_key in BINARY_CACHE: ret, build_dir_path = BINARY_CACHE[binary_key] sys.stderr.write(f'(skipping: already built at {build_dir_path} with returncode {ret})\n') + sys.stderr.flush() return ret, build_dir_path try: subprocess.check_output(build_cmd) ret = 0 except subprocess.CalledProcessError as err: - print(err.output.decode("utf8")) + # As on Win32, the output encoding of 'cmd.exe' is 'native' such as 'gbk' not 'utf8'; + # For Linux, the output encoding of bash is 'utf8' by default, and native is 'utf8 in most case + # So it's better not specify the encoding when call native program with subprocess.check_output + print(err.output.decode(errors="ignore")) ret = err.returncode BINARY_CACHE[binary_key] = (ret, build_dir_path) @@ -272,17 +275,18 @@ def hash_binary(bin_path): buf = bin_file.read(blocksize) return hasher.hexdigest() -def iterate_test_runner_jobs(jobs, options): +def iterate_test_runner_jobs(jobs): tested_paths = set() tested_hashes = {} for job in jobs: - ret_build, build_dir_path = create_binary(job, options) + ret_build, build_dir_path = create_binary(job) if ret_build: yield job, ret_build, None if build_dir_path in tested_paths: sys.stderr.write(f'(skipping: already tested with {build_dir_path})\n') + sys.stderr.flush() continue tested_paths.add(build_dir_path) @@ -291,6 +295,7 @@ def iterate_test_runner_jobs(jobs, options): if bin_hash in tested_hashes: sys.stderr.write(f'(skipping: already tested with equivalent {tested_hashes[bin_hash]})\n') + sys.stderr.flush() continue tested_hashes[bin_hash] = build_dir_path @@ -308,13 +313,17 @@ def run_check(runnable, env=None): env = full_env with subprocess.Popen(runnable, env=env) as proc: - proc.wait() + try: + proc.wait(timeout=options.run_check_timeout) + except subprocess.TimeoutExpired: + proc.kill() + return -1 return proc.returncode -def run_jerry_debugger_tests(options): +def run_jerry_debugger_tests(): ret_build = ret_test = 0 for job in DEBUGGER_TEST_OPTIONS: - ret_build, build_dir_path = create_binary(job, options) + ret_build, build_dir_path = create_binary(job) if ret_build: print(f"\n{TERM_RED}Build failed{TERM_NORMAL}\n") break @@ -339,9 +348,9 @@ def run_jerry_debugger_tests(options): return ret_build | ret_test -def run_jerry_tests(options): +def run_jerry_tests(): ret_build = ret_test = 0 - for job, ret_build, test_cmd in iterate_test_runner_jobs(JERRY_TESTS_OPTIONS, options): + for job, ret_build, test_cmd in iterate_test_runner_jobs(JERRY_TESTS_OPTIONS): if ret_build: break @@ -371,13 +380,13 @@ def run_jerry_tests(options): return ret_build | ret_test -def run_test262_test_suite(options): +def run_test262_test_suite(): ret_build = ret_test = 0 jobs = TEST262_TEST_SUITE_OPTIONS for job in jobs: - ret_build, build_dir_path = create_binary(job, options) + ret_build, build_dir_path = create_binary(job) if ret_build: print(f"\n{TERM_RED}Build failed{TERM_NORMAL}\n") break @@ -401,13 +410,13 @@ def run_test262_test_suite(options): return ret_build | ret_test -def run_unittests(options): +def run_unittests(): ret_build = ret_test = 0 for job in JERRY_UNITTESTS_OPTIONS: if job.skip: report_skip(job) continue - ret_build, build_dir_path = create_binary(job, options) + ret_build, build_dir_path = create_binary(job) if ret_build: print(f"\n{TERM_RED}Build failed{TERM_NORMAL}\n") break @@ -430,13 +439,13 @@ def run_unittests(options): return ret_build | ret_test -def run_buildoption_test(options): +def run_buildoption_test(): for job in JERRY_BUILDOPTIONS: if job.skip: report_skip(job) continue - ret, _ = create_binary(job, options) + ret, _ = create_binary(job) if ret: print(f"\n{TERM_RED}Build failed{TERM_NORMAL}\n") break @@ -445,7 +454,8 @@ def run_buildoption_test(options): Check = collections.namedtuple('Check', ['enabled', 'runner', 'arg']) -def main(options): +def main(): + util.setup_stdio() checks = [ Check(options.check_signed_off, run_check, [settings.SIGNED_OFF_SCRIPT] + {'tolerant': ['--tolerant'], 'gh-actions': ['--gh-actions']}.get(options.check_signed_off, [])), @@ -455,18 +465,22 @@ def main(options): Check(options.check_format, run_check, [settings.FORMAT_SCRIPT]), Check(options.check_license, run_check, [settings.LICENSE_SCRIPT]), Check(options.check_strings, run_check, [settings.STRINGS_SCRIPT]), - Check(options.jerry_debugger, run_jerry_debugger_tests, options), - Check(options.jerry_tests, run_jerry_tests, options), - Check(options.test262, run_test262_test_suite, options), - Check(options.unittests, run_unittests, options), - Check(options.buildoption_test, run_buildoption_test, options), + Check(options.jerry_debugger, run_jerry_debugger_tests, None), + Check(options.jerry_tests, run_jerry_tests, None), + Check(options.test262, run_test262_test_suite, None), + Check(options.unittests, run_unittests, None), + Check(options.buildoption_test, run_buildoption_test, None), ] for check in checks: if check.enabled or options.all: - ret = check.runner(check.arg) + if check.arg is None: + ret = check.runner() + else: + ret = check.runner(check.arg) if ret: sys.exit(ret) if __name__ == "__main__": - main(get_arguments()) + options = get_arguments() + main() diff --git a/tools/runners/run-test-suite-test262.py b/tools/runners/run-test-suite-test262.py index a03c678627..08e665ef2a 100755 --- a/tools/runners/run-test-suite-test262.py +++ b/tools/runners/run-test-suite-test262.py @@ -22,12 +22,6 @@ import util -def get_platform_cmd_prefix(): - if sys.platform == 'win32': - return ['cmd', '/S', '/C'] - return ['python3'] - - def get_arguments(): execution_runtime = os.environ.get('RUNTIME', '') parser = argparse.ArgumentParser() @@ -91,7 +85,7 @@ def update_exclude_list(args): # Tests pass in strict-mode but fail in non-strict-mode (or vice versa) should be considered as failures passing_tests = passing_tests - failing_tests - with open(args.excludelist_path, 'r+', encoding='utf8') as exclude_file: + with open(args.excludelist_path, 'r+', encoding='utf8', errors='ignore') as exclude_file: lines = exclude_file.readlines() exclude_file.seek(0) exclude_file.truncate() @@ -135,6 +129,7 @@ def update_exclude_list(args): def main(args): + util.setup_stdio() return_code = prepare_test262_test_suite(args) if return_code: return return_code @@ -149,12 +144,11 @@ def main(args): command += ' --test262-object' kwargs = {} - if sys.version_info.major >= 3: - kwargs['errors'] = 'ignore' + kwargs['errors'] = 'ignore' test262_harness_path = os.path.join(args.test262_harness_dir, 'test262-harness.py') - test262_command = get_platform_cmd_prefix() + \ + test262_command = util.get_python_cmd_prefix() + \ [test262_harness_path, '--command', command, '--tests', args.test_dir, diff --git a/tools/runners/run-test-suite.py b/tools/runners/run-test-suite.py index afd4477f77..fb3c341dda 100755 --- a/tools/runners/run-test-suite.py +++ b/tools/runners/run-test-suite.py @@ -61,13 +61,13 @@ def get_tests(test_dir, test_list, skip_list): dirname = os.path.dirname(test_list) with open(test_list, "r", encoding='utf8') as test_list_fd: for test in test_list_fd: - tests.append(os.path.normpath(os.path.join(dirname, test.rstrip()))) + tests.append(os.path.join(dirname, test.rstrip())) tests.sort() def filter_tests(test): for skipped in skip_list: - if skipped in test: + if os.path.normpath(skipped) in os.path.normpath(test): return False return True @@ -76,8 +76,8 @@ def filter_tests(test): def execute_test_command(test_cmd): kwargs = {} - if sys.version_info.major >= 3: - kwargs['encoding'] = 'unicode_escape' + kwargs['encoding'] = 'unicode_escape' + kwargs['errors'] = 'ignore' with subprocess.Popen(test_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, **kwargs) as process: stdout, _ = process.communicate() @@ -85,6 +85,7 @@ def execute_test_command(test_cmd): def main(args): + util.setup_stdio() tests = get_tests(args.test_dir, args.test_list, args.skip_list) total = len(tests) if total == 0: diff --git a/tools/runners/run-unittests.py b/tools/runners/run-unittests.py index d54f643692..ef07d41f14 100755 --- a/tools/runners/run-unittests.py +++ b/tools/runners/run-unittests.py @@ -49,6 +49,7 @@ def get_unittests(path): def main(args): + util.setup_stdio() unittests = get_unittests(args.path) total = len(unittests) if total == 0: diff --git a/tools/runners/test262-harness.py b/tools/runners/test262-harness.py index 404e20fc20..9c7118e085 100755 --- a/tools/runners/test262-harness.py +++ b/tools/runners/test262-harness.py @@ -55,6 +55,8 @@ import signal import multiprocessing +import util + ####################################################################### # based on _monkeyYaml.py ####################################################################### @@ -400,10 +402,10 @@ def open_file(self): text=self.text) def write(self, string): - os.write(self.file_desc, string.encode('utf8')) + os.write(self.file_desc, string.encode('utf8', 'ignore')) def read(self): - with open(self.name, "r", newline='', encoding='utf8') as file_desc: + with open(self.name, "r", newline='', encoding='utf8', errors='ignore') as file_desc: return file_desc.read() def close(self): @@ -490,7 +492,7 @@ def __init__(self, suite, name, full_path, strict_mode, command_template, module self.name = name self.full_path = full_path self.strict_mode = strict_mode - with open(self.full_path, "r", newline='', encoding='utf8') as file_desc: + with open(self.full_path, "r", newline='', encoding='utf8', errors='ignore') as file_desc: self.contents = file_desc.read() test_record = parse_test_record(self.contents, name) self.test = test_record["test"] @@ -742,7 +744,7 @@ def should_run(rel_path, tests): if not tests: return True for test in tests: - if test in rel_path: + if os.path.normpath(test) in os.path.normpath(rel_path): return True return False @@ -750,7 +752,7 @@ def get_include(self, name): if not name in self.include_cache: static = path.join(self.lib_root, name) if path.exists(static): - with open(static, encoding='utf8') as file_desc: + with open(static, encoding='utf8', errors='ignore') as file_desc: contents = file_desc.read() contents = re.sub(r'\r\n', '\n', contents) self.include_cache[name] = contents + "\n" @@ -839,7 +841,7 @@ def run(self, command_template, tests, print_summary, full_summary, logname, job report_error("No tests to run") progress = ProgressIndicator(len(cases)) if logname: - self.logf = open(logname, "w", encoding='utf8') # pylint: disable=consider-using-with + self.logf = open(logname, "w", encoding='utf8', errors='ignore') # pylint: disable=consider-using-with if job_count == 1: for case in cases: @@ -901,6 +903,7 @@ def list_includes(self, tests): def main(): + util.setup_stdio() code = 0 parser = build_options() options = parser.parse_args() diff --git a/tools/runners/util.py b/tools/runners/util.py index 90e920491b..f4225da62a 100755 --- a/tools/runners/util.py +++ b/tools/runners/util.py @@ -12,13 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +import codecs import signal import subprocess import sys -TERM_NORMAL = '\033[0m' -TERM_RED = '\033[1;31m' -TERM_GREEN = '\033[1;32m' +if sys.platform == 'win32': + TERM_NORMAL = '' + TERM_RED = '' + TERM_GREEN = '' + TERM_YELLOW = '' + TERM_BLUE = '' +else: + TERM_NORMAL = '\033[0m' + TERM_RED = '\033[1;31m' + TERM_GREEN = '\033[1;32m' + TERM_YELLOW = '\033[1;33m' + TERM_BLUE = '\033[1;34m' def set_timezone(timezone): @@ -42,6 +52,18 @@ def set_sighdl_to_reset_timezone(timezone): signal.signal(signal.SIGINT, lambda signal, frame: set_timezone_and_exit(timezone)) +# This is for not lost data on 'win32' with python 'print'. +# When use python subprocess call another script on win32, output with +# 'utf-8' encoding, that's the same like linux platform; but when +# call the python script in 'cmd.exe' shell, we have to output in 'native' encoding. +def setup_stdio(): + # For tty using native encoding, otherwise (pipe) use 'utf-8' + encoding = sys.stdout.encoding if sys.stdout.isatty() else 'utf-8' + # Always override it to avoid encode error + sys.stdout = codecs.getwriter(encoding)(sys.stdout.buffer, 'xmlcharrefreplace') + sys.stderr = codecs.getwriter(encoding)(sys.stderr.buffer, 'xmlcharrefreplace') + + def print_test_summary(summary_string, total, passed, failed): print(f"\n[summary] {summary_string}\n") print(f"TOTAL: {total}") @@ -72,4 +94,4 @@ def get_platform_cmd_prefix(): def get_python_cmd_prefix(): # python script doesn't have execute permission on github actions windows runner - return get_platform_cmd_prefix() + [sys.executable or 'python'] + return [sys.executable or 'python']