|
5 | 5 |
|
6 | 6 | from pyrit.converter import BrailleConverter, ConverterResult |
7 | 7 |
|
| 8 | +# Printable ASCII symbols and their two-cell Unified English Braille sequences. |
| 9 | +UEB_SYMBOL_CELLS = { |
| 10 | + "@": "\u2808\u2801", # dot 4, dot 1 |
| 11 | + "#": "\u2838\u2839", # dots 456, dots 1456 |
| 12 | + "%": "\u2828\u2834", # dots 46, dots 356 |
| 13 | + "&": "\u2808\u282f", # dot 4, dots 12346 |
| 14 | + "*": "\u2810\u2814", # dot 5, dots 35 |
| 15 | + "+": "\u2810\u2816", # dot 5, dots 235 |
| 16 | + "<": "\u2808\u2823", # dot 4, dots 126 |
| 17 | + "=": "\u2810\u2836", # dot 5, dots 2356 |
| 18 | + ">": "\u2808\u281c", # dot 4, dots 345 |
| 19 | + '"': "\u2820\u2836", # dot 6, dots 2356 |
| 20 | + "[": "\u2828\u2823", # dots 46, dots 126 |
| 21 | + "]": "\u2828\u281c", # dots 46, dots 345 |
| 22 | + "\\": "\u2838\u2821", # dots 456, dots 16 |
| 23 | + "^": "\u2808\u2822", # dot 4, dots 26 |
| 24 | + "_": "\u2828\u2824", # dots 46, dots 36 |
| 25 | + "`": "\u2828\u2821", # dots 46, dots 16 |
| 26 | + "{": "\u2838\u2823", # dots 456, dots 126 |
| 27 | + "}": "\u2838\u281c", # dots 456, dots 345 |
| 28 | + "|": "\u2838\u2833", # dots 456, dots 1256 |
| 29 | + "~": "\u2808\u2814", # dot 4, dots 35 |
| 30 | +} |
| 31 | + |
8 | 32 |
|
9 | 33 | async def test_braille_converter_simple_text(): |
10 | 34 | """Test basic Braille conversion.""" |
@@ -131,3 +155,49 @@ async def test_braille_converter_punctuation_cells(): |
131 | 155 | f"{char!r} -> {result.output_text!r} (U+{ord(result.output_text):04X}), " |
132 | 156 | f"expected {cell!r} (U+{ord(cell):04X})" |
133 | 157 | ) |
| 158 | + |
| 159 | + |
| 160 | +@pytest.mark.parametrize("char, expected", sorted(UEB_SYMBOL_CELLS.items())) |
| 161 | +async def test_braille_converter_ascii_symbol_cells(char, expected): |
| 162 | + """Printable ASCII symbols map to their UEB cells rather than being dropped. |
| 163 | +
|
| 164 | + Regression: '@', '%', '+', '<' and the other unmapped symbols were silently |
| 165 | + dropped, corrupting the encoded prompt (e.g. "a@b.com" became "ab.com"). |
| 166 | + Cells are pinned against the Unified English Braille symbol definitions. |
| 167 | + """ |
| 168 | + converter = BrailleConverter() |
| 169 | + |
| 170 | + result = await converter.convert_async(prompt=char, input_type="text") |
| 171 | + assert result.output_text == expected |
| 172 | + |
| 173 | + |
| 174 | +@pytest.mark.parametrize( |
| 175 | + "char", |
| 176 | + ["\u00e9", "\u4e2d", "\U0001f600", "\n", "\t", "\r"], |
| 177 | + ids=["e-acute", "cjk", "emoji", "newline", "tab", "carriage-return"], |
| 178 | +) |
| 179 | +async def test_braille_converter_unmapped_characters_pass_through(char): |
| 180 | + """Characters with no Braille cell survive conversion unchanged.""" |
| 181 | + converter = BrailleConverter() |
| 182 | + |
| 183 | + result = await converter.convert_async(prompt=f"a{char}b", input_type="text") |
| 184 | + assert result.output_text == f"\u2801{char}\u2803" |
| 185 | + |
| 186 | + |
| 187 | +@pytest.mark.parametrize( |
| 188 | + "prompt, expected", |
| 189 | + [ |
| 190 | + ("hello", "\u2813\u2811\u2807\u2807\u2815"), |
| 191 | + ("a@b.com", "\u2801\u2808\u2801\u2803\u2832\u2809\u2815\u280d"), |
| 192 | + ("1+2", "\u283c\u2801\u2810\u2816\u283c\u2803"), |
| 193 | + ("100%", "\u283c\u2801\u281a\u281a\u2828\u2834"), |
| 194 | + ("caf\u00e9", "\u2809\u2801\u280b\u00e9"), |
| 195 | + ], |
| 196 | + ids=["letters", "email", "digits-around-symbol", "percent", "accented-letter"], |
| 197 | +) |
| 198 | +async def test_braille_converter_exact_output(prompt, expected): |
| 199 | + """Mapped characters are still encoded, and pass-through does not corrupt number mode.""" |
| 200 | + converter = BrailleConverter() |
| 201 | + |
| 202 | + result = await converter.convert_async(prompt=prompt, input_type="text") |
| 203 | + assert result.output_text == expected |
0 commit comments