|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +import os |
| 4 | +import sys |
| 5 | +import subprocess |
| 6 | +import glob |
| 7 | +import unittest |
| 8 | +from pathlib import Path |
| 9 | +from typing import List, Tuple, Dict, Optional |
| 10 | + |
| 11 | +def strip_trailing_empty_lines(text: str) -> str: |
| 12 | + """Remove trailing empty lines from text.""" |
| 13 | + lines = text.splitlines() |
| 14 | + while lines and not lines[-1].strip(): |
| 15 | + lines.pop() |
| 16 | + return '\n'.join(lines) |
| 17 | + |
| 18 | + |
| 19 | +def run_binary(binary_path: str, test_dir: str, jsonnet_file: str) -> Tuple[str, int]: |
| 20 | + """Run the binary on a jsonnet file and return output and exit code.""" |
| 21 | + cmd = [binary_path, '-Xss100m', '-J', test_dir, jsonnet_file] |
| 22 | + try: |
| 23 | + result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) |
| 24 | + return result.stdout + result.stderr, result.returncode |
| 25 | + except subprocess.TimeoutExpired: |
| 26 | + return "TIMEOUT: Test execution exceeded 10 seconds", 1 |
| 27 | + except Exception as e: |
| 28 | + return f"ERROR: Failed to execute binary: {e}", 1 |
| 29 | + |
| 30 | +class BaseGraalVMTestSuite(unittest.TestCase): |
| 31 | + """Base class for GraalVM test suites.""" |
| 32 | + |
| 33 | + @classmethod |
| 34 | + def setUpClass(cls): |
| 35 | + """Set up the test class by building the GraalVM native binary.""" |
| 36 | + if not hasattr(cls, '_binary_built'): |
| 37 | + subprocess.run(["./mill", "sjsonnet.graal.nativeImage"], check=True, cwd=".") |
| 38 | + BaseGraalVMTestSuite._binary_built = True |
| 39 | + cls.binary_path = "out/sjsonnet/graal/nativeImage.dest/native-executable" |
| 40 | + |
| 41 | + def run_individual_test(self, test_dir: str, suite_name: str, jsonnet_file: str, base_name: str): |
| 42 | + """Run a single jsonnet test and assert it passes.""" |
| 43 | + # Run the binary on the jsonnet file and capture output |
| 44 | + output, exit_code = run_binary(self.binary_path, test_dir, jsonnet_file) |
| 45 | + with open(os.path.join(test_dir, f"{base_name}.jsonnet.golden"), 'r', encoding='utf-8') as f: |
| 46 | + golden_content = f.read() |
| 47 | + |
| 48 | + # Strip test directory path from output only for go_test_suite |
| 49 | + if suite_name == "go_test_suite": |
| 50 | + normalized_output = strip_trailing_empty_lines(output.replace(f"{test_dir}/", "")) |
| 51 | + normalized_golden_content = strip_trailing_empty_lines(golden_content.replace(f"{test_dir}/", "")) |
| 52 | + else: |
| 53 | + normalized_output = strip_trailing_empty_lines(output) |
| 54 | + normalized_golden_content = strip_trailing_empty_lines(golden_content) |
| 55 | + |
| 56 | + # Compare with golden file, ignoring trailing empty lines |
| 57 | + if len(normalized_golden_content) > 10000 and normalized_golden_content != normalized_output: |
| 58 | + print(f"normalized_golden_content: {normalized_golden_content[:100]}") |
| 59 | + print(f"normalized_output: {normalized_output[:100]}") |
| 60 | + self.fail("Mismatch - but content very very large") |
| 61 | + self.assertEqual(normalized_golden_content, normalized_output) |
| 62 | + |
| 63 | + |
| 64 | +class MainTestSuite(BaseGraalVMTestSuite): |
| 65 | + """Test suite for the main jsonnet test files.""" |
| 66 | + |
| 67 | + def test_all_files(self): |
| 68 | + """Test all files in the main test suite using subTest for each file.""" |
| 69 | + test_dir = "sjsonnet/test/resources/test_suite" |
| 70 | + suite_name = "test_suite" |
| 71 | + |
| 72 | + if not os.path.exists(test_dir): |
| 73 | + self.fail(f"Test directory {test_dir} not found") |
| 74 | + |
| 75 | + # Skip list for main test suite |
| 76 | + skip_list = [ |
| 77 | + "error.obj_recursive_manifest", |
| 78 | + "error.recursive_object_non_term", |
| 79 | + "error.recursive_import", |
| 80 | + "error.recursive_function_nonterm", |
| 81 | + "error.function_infinite_default", |
| 82 | + "error.obj_recursive", |
| 83 | + "error.array_recursive_manifest", |
| 84 | + "error.function_no_default_arg", |
| 85 | + "error.invariant.option", |
| 86 | + "error.negative_shfit", |
| 87 | + "error.overflow", |
| 88 | + "error.overflow2", |
| 89 | + "error.overflow3", |
| 90 | + "error.parse_json", |
| 91 | + "error.parse.string.invalid_escape", |
| 92 | + "error.top_level_func", |
| 93 | + "stdlib", |
| 94 | + "tla.simple", |
| 95 | + "trace" |
| 96 | + ] |
| 97 | + |
| 98 | + # Find all .jsonnet files in the test directory |
| 99 | + jsonnet_pattern = os.path.join(test_dir, "*.jsonnet") |
| 100 | + jsonnet_files = glob.glob(jsonnet_pattern) |
| 101 | + |
| 102 | + self.assertTrue(jsonnet_files, f"No .jsonnet files found in {test_dir}") |
| 103 | + |
| 104 | + for jsonnet_file in sorted(jsonnet_files): |
| 105 | + base_name = Path(jsonnet_file).stem |
| 106 | + |
| 107 | + # Check if this test should be skipped |
| 108 | + if base_name in skip_list: |
| 109 | + continue |
| 110 | + |
| 111 | + with self.subTest(file=base_name): |
| 112 | + self.run_individual_test(test_dir, suite_name, jsonnet_file, base_name) |
| 113 | + |
| 114 | +class GoTestSuite(BaseGraalVMTestSuite): |
| 115 | + """Test suite for the Go jsonnet test files.""" |
| 116 | + |
| 117 | + def test_all_files(self): |
| 118 | + """Test all files in the go test suite using subTest for each file.""" |
| 119 | + test_dir = "sjsonnet/test/resources/go_test_suite" |
| 120 | + suite_name = "go_test_suite" |
| 121 | + |
| 122 | + if not os.path.exists(test_dir): |
| 123 | + self.fail(f"Test directory {test_dir} not found") |
| 124 | + |
| 125 | + # Skip list for go_test_suite |
| 126 | + skip_list = [ |
| 127 | + "builtin_cos", |
| 128 | + "builtin_exp4", |
| 129 | + "builtin_log3", |
| 130 | + "div3", |
| 131 | + "extvar_code", |
| 132 | + "extvar_error", |
| 133 | + "extvar_hermetic", |
| 134 | + "extvar_mutually_recursive", |
| 135 | + "extvar_self_recursive", |
| 136 | + "extvar_static_error", |
| 137 | + "extvar_string", |
| 138 | + "function_too_many_params", |
| 139 | + "native1", |
| 140 | + "native2", |
| 141 | + "native3", |
| 142 | + "native4", |
| 143 | + "native5", |
| 144 | + "native6", |
| 145 | + "native7", |
| 146 | + "native_error", |
| 147 | + "native_panic", |
| 148 | + "std.mantissa3", |
| 149 | + "stdlib_smoke_test" |
| 150 | + ] |
| 151 | + |
| 152 | + # Find all .jsonnet files in the test directory |
| 153 | + jsonnet_pattern = os.path.join(test_dir, "*.jsonnet") |
| 154 | + jsonnet_files = glob.glob(jsonnet_pattern) |
| 155 | + |
| 156 | + self.assertTrue(jsonnet_files, f"No .jsonnet files found in {test_dir}") |
| 157 | + |
| 158 | + for jsonnet_file in sorted(jsonnet_files): |
| 159 | + base_name = Path(jsonnet_file).stem |
| 160 | + |
| 161 | + # Check if this test should be skipped |
| 162 | + if base_name in skip_list: |
| 163 | + continue |
| 164 | + |
| 165 | + with self.subTest(file=base_name): |
| 166 | + self.run_individual_test(test_dir, suite_name, jsonnet_file, base_name) |
| 167 | + |
| 168 | + |
| 169 | +if __name__ == "__main__": |
| 170 | + unittest.main(verbosity=2) |
0 commit comments