Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions test/js/bun/yaml/translate_yaml_test_suite_to_bun.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sys
import re
import argparse
import tempfile

def escape_js_string(s):
"""Escape a string for use in JavaScript string literals."""
Expand Down Expand Up @@ -434,6 +435,39 @@ def get_expected_from_yaml_parser(yaml_content, use_eemeli_yaml=True):
if os.path.exists(temp_js):
os.remove(temp_js)

# The bun whose YAML.parse supplies the message that each error test asserts. Set by --bun.
Comment thread
robobun marked this conversation as resolved.
Resolved
bun_executable = 'bun'

def get_bun_syntax_error_message(yaml_content):
"""Ask Bun's parser for the message of the SyntaxError it throws for this input.

Returns None when Bun does not throw a SyntaxError. The error test for that input then fails until Bun does.
"""
js_code = f'''
const input = {format_js_string(yaml_content)};

let message = null;
try {{
Bun.YAML.parse(input);
}} catch (e) {{
if (e instanceof SyntaxError) message = e.message;
}}
console.log(JSON.stringify(message));
'''

with tempfile.NamedTemporaryFile('w', suffix='.js', encoding='utf-8', delete=False) as f:
f.write(js_code)
temp_js = f.name

try:
result = subprocess.run([bun_executable, temp_js], capture_output=True, text=True, timeout=60,
env={**os.environ, 'BUN_DEBUG_QUIET_LOGS': '1'})
if result.returncode != 0:
raise RuntimeError(f"{bun_executable} exited with {result.returncode}: {result.stderr}")
Comment thread
robobun marked this conversation as resolved.
Resolved
return json.loads(result.stdout.strip().splitlines()[-1])
finally:
os.remove(temp_js)

def generate_test(test_dir, test_name, check_ast=True, use_js_yaml=False, use_yaml_pkg=False):
"""Generate a single Bun test case from a yaml-test-suite directory.

Expand Down Expand Up @@ -519,15 +553,26 @@ def generate_test(test_dir, test_name, check_ast=True, use_js_yaml=False, use_ya
}});
'''
else:
# toEqual compares the class and the message of two errors, so one expect() covers both.
# CI collects garbage after every expect() (BUN_GARBAGE_COLLECTOR_LEVEL=1).
message = get_bun_syntax_error_message(yaml_content)
if message is not None:
assertion = f'expect(error).toEqual(new SyntaxError("{escape_js_string(message)}"));'
else:
assertion = 'expect(error).toBeInstanceOf(SyntaxError);'
return f'''
test("{test_name}", () => {{
// {description}
// Error test - expecting parse to fail
const input: string = {formatted_content};

expect(() => {{
return YAML.parse(input);
}}).toThrow();
let error: unknown;
try {{
YAML.parse(input);
}} catch (e) {{
error = e;
}}
{assertion}
}});
'''

Expand Down Expand Up @@ -905,8 +950,14 @@ def main():
help='Also generate a companion test file using js-yaml for validation')
parser.add_argument('--with-yaml', action='store_true',
help='Also generate a companion test file using yaml package for validation')
parser.add_argument('--bun', default='bun',
help='bun executable whose YAML.parse supplies the message that each error test asserts '
'(use the build under test, e.g. build/debug/bun-debug)')
args = parser.parse_args()

global bun_executable
bun_executable = args.bun

check_ast = not args.no_ast_check
with_js_yaml = args.with_js_yaml
with_yaml = args.with_yaml
Expand Down Expand Up @@ -997,6 +1048,7 @@ def main():
output = f'''// Tests translated from official yaml-test-suite
{commit_comment}
{mode_comment}
// Error tests assert a SyntaxError with the message Bun's parser gave when this file was generated
// Total: {len(test_dirs)} test directories

import {{ test, expect }} from "bun:test";
Expand Down
Loading
Loading