|
| 1 | +import time |
| 2 | +import csv |
| 3 | +import os |
| 4 | +import glob |
| 5 | +import json |
| 6 | +from pywinauto.application import Application |
| 7 | + |
| 8 | +pattern = " a1b'k2l`cif/msp\"e3h9o6r^djg>ntq,*5<-u8v.%[$+x!&;:4\\0z7(_?w]#y)=" |
| 9 | +braille = "⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿" |
| 10 | + |
| 11 | + |
| 12 | +def raw_to_unicode_braille(raw_text: str) -> str: |
| 13 | + """Convert 점사랑 raw output to unicode braille.""" |
| 14 | + result = "" |
| 15 | + for ch in raw_text: |
| 16 | + if ch in pattern: |
| 17 | + result += braille[pattern.index(ch)] |
| 18 | + elif ch == "@": |
| 19 | + result += braille[8] |
| 20 | + elif ch == "|": |
| 21 | + result += braille[51] |
| 22 | + else: |
| 23 | + raise ValueError(f"Unknown character in output: {repr(ch)}") |
| 24 | + return result |
| 25 | + |
| 26 | + |
| 27 | +def main(): |
| 28 | + app = None |
| 29 | + try: |
| 30 | + app = Application(backend="uia").start( |
| 31 | + r"C:\Program Files (x86)\Jeomsarang6\BrailleLove.exe" |
| 32 | + ) |
| 33 | + print("BrailleLove started.") |
| 34 | + |
| 35 | + main_window = app.window(title="점사랑 6.0") |
| 36 | + main_window.set_focus() |
| 37 | + main_window.maximize() |
| 38 | + |
| 39 | + main_window.child_window(title="새문서", control_type="Button").click() |
| 40 | + main_window.child_window(title="확인(O)", control_type="Button").click() |
| 41 | + |
| 42 | + main_window = app.window(title=app.windows()[0].window_text()) |
| 43 | + pane = main_window.child_window(control_type="Pane", title="작업 영역") |
| 44 | + output_edit = main_window.child_window(control_type="Edit", title="") |
| 45 | + |
| 46 | + test_case_files = sorted(glob.glob("../test_cases/*.csv")) |
| 47 | + if not test_case_files: |
| 48 | + print("No test case files found in ../test_cases/") |
| 49 | + return |
| 50 | + |
| 51 | + total_cases = 0 |
| 52 | + passed_cases = 0 |
| 53 | + failed_cases = 0 |
| 54 | + errors = 0 |
| 55 | + failures_detail = [] |
| 56 | + results_per_file = {} |
| 57 | + |
| 58 | + for test_file in test_case_files: |
| 59 | + file_name = os.path.basename(test_file) |
| 60 | + print(f"\n--- {file_name} ---") |
| 61 | + file_total = 0 |
| 62 | + file_passed = 0 |
| 63 | + |
| 64 | + with open(test_file, "r", encoding="utf-8") as f: |
| 65 | + reader = csv.reader(f) |
| 66 | + for row in reader: |
| 67 | + if not row or len(row) < 4: |
| 68 | + continue |
| 69 | + |
| 70 | + korean_input = row[0].strip() |
| 71 | + expected_unicode = row[-1].strip() |
| 72 | + |
| 73 | + if not korean_input or not expected_unicode: |
| 74 | + continue |
| 75 | + |
| 76 | + file_total += 1 |
| 77 | + total_cases += 1 |
| 78 | + |
| 79 | + try: |
| 80 | + # Type input into 점사랑 |
| 81 | + time.sleep(0.3) |
| 82 | + pane.type_keys( |
| 83 | + korean_input.replace(" ", "{SPACE}") |
| 84 | + .replace("(", "{(}") |
| 85 | + .replace(")", "{)}"), |
| 86 | + pause=0.05, |
| 87 | + ) |
| 88 | + time.sleep(0.3) |
| 89 | + |
| 90 | + # Read output |
| 91 | + raw_output = output_edit.get_value() |
| 92 | + actual_unicode = raw_to_unicode_braille(raw_output) |
| 93 | + |
| 94 | + if actual_unicode == expected_unicode: |
| 95 | + passed_cases += 1 |
| 96 | + file_passed += 1 |
| 97 | + else: |
| 98 | + failed_cases += 1 |
| 99 | + detail = { |
| 100 | + "file": file_name, |
| 101 | + "input": korean_input, |
| 102 | + "expected": expected_unicode, |
| 103 | + "actual": actual_unicode, |
| 104 | + } |
| 105 | + failures_detail.append(detail) |
| 106 | + print( |
| 107 | + f" FAIL: '{korean_input}' expected={expected_unicode} actual={actual_unicode}" |
| 108 | + ) |
| 109 | + |
| 110 | + # Clear input |
| 111 | + main_window.set_focus() |
| 112 | + time.sleep(0.3) |
| 113 | + pane.type_keys("{BACKSPACE}" * len(korean_input)) |
| 114 | + while output_edit.get_value() != "": |
| 115 | + pane.type_keys("{BACKSPACE}") |
| 116 | + |
| 117 | + except Exception as e: |
| 118 | + errors += 1 |
| 119 | + print(f" ERROR: '{korean_input}' -> {e}") |
| 120 | + # Try to clear |
| 121 | + try: |
| 122 | + main_window.set_focus() |
| 123 | + pane.type_keys("^a{DELETE}") |
| 124 | + time.sleep(0.5) |
| 125 | + except: |
| 126 | + pass |
| 127 | + |
| 128 | + results_per_file[file_name] = { |
| 129 | + "total": file_total, |
| 130 | + "passed": file_passed, |
| 131 | + "failed": file_total - file_passed, |
| 132 | + } |
| 133 | + if file_total > 0: |
| 134 | + pct = file_passed / file_total * 100 |
| 135 | + print(f" {file_passed}/{file_total} passed ({pct:.1f}%)") |
| 136 | + |
| 137 | + # Summary |
| 138 | + print("\n" + "=" * 60) |
| 139 | + print("VERIFICATION SUMMARY") |
| 140 | + print("=" * 60) |
| 141 | + print(f"Total cases: {total_cases}") |
| 142 | + print(f"Passed: {passed_cases}") |
| 143 | + print(f"Failed: {failed_cases}") |
| 144 | + print(f"Errors: {errors}") |
| 145 | + if total_cases > 0: |
| 146 | + accuracy = passed_cases / total_cases * 100 |
| 147 | + print(f"Accuracy: {accuracy:.2f}%") |
| 148 | + print("=" * 60) |
| 149 | + |
| 150 | + # Per-file breakdown |
| 151 | + print("\nPer-file results:") |
| 152 | + for fname, stats in sorted(results_per_file.items()): |
| 153 | + pct = stats["passed"] / stats["total"] * 100 if stats["total"] > 0 else 0 |
| 154 | + status = "PASS" if stats["failed"] == 0 else "FAIL" |
| 155 | + print( |
| 156 | + f" [{status}] {fname}: {stats['passed']}/{stats['total']} ({pct:.1f}%)" |
| 157 | + ) |
| 158 | + |
| 159 | + # Save results to JSON |
| 160 | + report = { |
| 161 | + "total": total_cases, |
| 162 | + "passed": passed_cases, |
| 163 | + "failed": failed_cases, |
| 164 | + "errors": errors, |
| 165 | + "accuracy_percent": round(passed_cases / total_cases * 100, 2) |
| 166 | + if total_cases > 0 |
| 167 | + else 0, |
| 168 | + "per_file": results_per_file, |
| 169 | + "failures": failures_detail[:100], # Cap at 100 for readability |
| 170 | + } |
| 171 | + with open("../jeomsarang_verify_result.json", "w", encoding="utf-8") as f: |
| 172 | + json.dump(report, f, ensure_ascii=False, indent=2) |
| 173 | + print(f"\nDetailed results saved to jeomsarang_verify_result.json") |
| 174 | + |
| 175 | + except Exception as e: |
| 176 | + print(f"Error: {e}") |
| 177 | + import traceback |
| 178 | + |
| 179 | + traceback.print_exc() |
| 180 | + finally: |
| 181 | + if app: |
| 182 | + app.kill() |
| 183 | + print("BrailleLove terminated.") |
| 184 | + |
| 185 | + |
| 186 | +if __name__ == "__main__": |
| 187 | + main() |
0 commit comments