Skip to content
Merged
Changes from all 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
181 changes: 181 additions & 0 deletions maldoca/js/ir/transforms/generate_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
r"""Generates test files for the conversion tests.

In each directory, only `input.js` is manually written. The other files are
generated by running this tool.

Usage:

python3 maldoca/js/ir/transforms/generate_tests.py \
--passes source2ast,ast2hir,constprop,hir2ast,ast2source \
--path maldoca/js/ir/transforms/constant_propagation/tests

python3 maldoca/js/ir/transforms/generate_tests.py \
--passes source2ast,extract_prelude,erase_comments,ast2hir,dynconstprop,hir2ast,ast2source \
--path maldoca/js/ir/transforms/dynamic_constant_propagation/tests

python3 maldoca/js/ir/transforms/generate_tests.py \
--passes source2ast,erase_comments,ast2hir,movenamedfuncs,hir2ast,ast2source \
--path maldoca/js/ir/transforms/move_named_functions/tests

python3 maldoca/js/ir/transforms/generate_tests.py \
--passes source2ast,erase_comments,ast2hir,normalize_member_expressions,hir2ast,ast2source \
--path maldoca/js/ir/transforms/normalize_member_expressions/tests

python3 maldoca/js/ir/transforms/generate_tests.py \
--passes source2ast,erase_comments,ast2hir,constprop,normalize_member_expressions,hir2ast,ast2source \
--path maldoca/js/ir/transforms/normalize_member_expressions/tests/with_constant_propagation

python3 maldoca/js/ir/transforms/generate_tests.py \
--passes source2ast,ast2hir,normalizeobjprops,hir2ast,ast2source \
--path maldoca/js/ir/transforms/normalize_object_properties/tests

python3 maldoca/js/ir/transforms/generate_tests.py \
--passes source2ast,erase_comments,ast2hir,peelparens,hir2ast,ast2source \
--path maldoca/js/ir/transforms/peel_parentheses/tests

python3 maldoca/js/ir/transforms/generate_tests.py \
--passes source2ast,ast2hir,split_declaration_statements,hir2ast,ast2source \
--path maldoca/js/ir/transforms/split_declaration_statements/tests

python3 maldoca/js/ir/transforms/generate_tests.py \
--passes source2ast,ast2hir,split_sequence_expressions,hir2ast,ast2source \
--path maldoca/js/ir/transforms/split_sequence_expressions/tests
"""

import argparse
import os
import shutil
import subprocess
import sys


def run_command(cmd: str) -> bytes:
"""Run a shell command and return its output."""

print("Running:")
print(" ", cmd)

try:
return subprocess.check_output(cmd, shell=True, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
print("Error running command, output:", e.output)
sys.exit(1)


def generate_test_file(
input_path: str,
output_path: str,
passes: str,
prefix: str,
):
"""Generates a test file.

Args:
input_path: Test case name.
output_path: The output file will be <name>/<output_name>.
passes: Comma-separated list of JSIR passes.
prefix: FileCheck prefix.
"""
input_abs_path: str = os.path.abspath(input_path)

command: str = (
"bazel run //maldoca/js/ir:jsir_gen --"
f" --input_file={input_abs_path}"
f" --passes='{passes}'"
)

try:
output = run_command(command).decode("utf-8")
except UnicodeDecodeError:
output = run_command(command).decode("latin-1")

output = output.strip()

# Prepend prefix
lines = output.split("\n")
out_lines = []
for i, line in enumerate(lines):
if not line:
line = f"// {prefix}-EMPTY:"
elif i == 0:
line = f"// {prefix}: " + line
else:
line = f"// {prefix}-NEXT: " + line
out_lines.append(line)

output = "\n".join(out_lines) + "\n"

with open(output_path, "w") as f:
f.write(output)


README_TEMPLATE = r"""To run manually:

```shell
bazel run //maldoca/js/ir:jsir_gen -- \
--input_file $(pwd)/{test_path}/input.js \
--passes "{passes}"
```
"""

LIT_TEMPLATE = r"""// RUN: CURRENT_FILE_BASENAME=$(basename %s .lit) && \
// RUN: jsir_gen --input_file "$(dirname %s)"/input.js \
// RUN: --passes "{passes}" \
// RUN: | FileCheck --check-prefix SOURCE "$(dirname %s)"/output.generated.txt
"""


def generate_readme_file(test_path: str, passes: str):
with open(os.path.join(test_path, "README.generated.md"), "w") as f:
f.write(README_TEMPLATE.format(test_path=test_path, passes=passes))


def generate_lit_file(test_path: str, passes: str):
with open(os.path.join(test_path, "run.generated.lit"), "w") as f:
f.write(LIT_TEMPLATE.format(test_path=test_path, passes=passes))


def main():
parser = argparse.ArgumentParser()
parser.add_argument("--passes", required=True)
parser.add_argument("--path", required=True)

args = parser.parse_args()
passes: str = args.passes
root_path: str = args.path
print("Passes:", passes)
print("Path:", root_path)

for test_path, _, file_names_in_dir in os.walk(root_path):

if "input.js" not in file_names_in_dir:
continue

test_name = os.path.basename(test_path)
print("Generating test:", test_name)

generate_test_file(
input_path=os.path.join(test_path, "input.js"),
output_path=os.path.join(test_path, "output.generated.txt"),
passes=passes,
prefix="SOURCE",
)

generate_readme_file(
test_path=test_path,
passes=passes,
)

generate_lit_file(
test_path,
passes=passes,
)

shutil.copyfile(
os.path.join(root_path, "BUILD.template"),
os.path.join(test_path, "BUILD"),
)


if __name__ == "__main__":
main()