|
18 | 18 | # guitest: show |
19 | 19 |
|
20 | 20 | import os.path as osp |
| 21 | +import time |
21 | 22 |
|
22 | 23 | from guidata.qthelpers import qt_app_context |
| 24 | +from qtpy import QtWidgets as QW |
23 | 25 |
|
24 | 26 | from datalab.env import execenv |
25 | 27 | from datalab.gui.macroeditor import Macro |
26 | 28 | from datalab.gui.panel import macro |
27 | | -from datalab.tests import helpers |
| 29 | +from datalab.tests import datalab_test_app_context, helpers |
28 | 30 |
|
29 | 31 |
|
30 | 32 | def get_macro_example_path() -> str: |
@@ -98,5 +100,104 @@ def test_macro_editor(): |
98 | 100 | assert widget.get_macro_titles()[0] == osp.basename(macro_path) |
99 | 101 |
|
100 | 102 |
|
| 103 | +def test_macro_unicode_encoding(): |
| 104 | + """Test that macros can print Unicode characters without encoding errors. |
| 105 | +
|
| 106 | + This test verifies the fix for the UnicodeEncodeError that occurred on Windows |
| 107 | + systems with locales like cp1252 when macros printed Unicode characters. |
| 108 | +
|
| 109 | + The test creates and runs a macro that prints various Unicode characters, |
| 110 | + simulating the scenario where RemoteProxy connection messages (which contain |
| 111 | + arrows →) would cause encoding errors on Windows with cp1252 locale. |
| 112 | +
|
| 113 | + Without the UTF-8 encoding fix in Macro.run(), this test would fail with: |
| 114 | + UnicodeEncodeError: 'charmap' codec can't encode character '\u2192' |
| 115 | + """ |
| 116 | + with helpers.WorkdirRestoringTempDir(): |
| 117 | + with datalab_test_app_context(console=False) as win: |
| 118 | + win.set_current_panel("macro") |
| 119 | + |
| 120 | + # Create a macro that prints various Unicode characters |
| 121 | + macro = win.macropanel.add_macro() |
| 122 | + macro.title = "Unicode Test Macro" |
| 123 | + |
| 124 | + # This test verifies that Unicode characters can be printed successfully. |
| 125 | + # The macro prints Unicode characters without any encoding manipulation. |
| 126 | + # With the UTF-8 fix in Macro.run(), these print statements work correctly. |
| 127 | + # Without the fix, on systems with cp1252 locale, these would fail. |
| 128 | + # |
| 129 | + # Note: We cannot reliably simulate cp1252 locale in the test because: |
| 130 | + # 1. Modern Python often defaults to UTF-8 |
| 131 | + # 2. If we manually reconfigure to cp1252 in the macro, it overrides |
| 132 | + # any fix done before the macro code runs |
| 133 | + # 3. The PYTHONIOENCODING env var might be set system-wide |
| 134 | + # |
| 135 | + # This test serves as a regression test - it will catch if the fix |
| 136 | + # is removed, but only on systems that actually default to cp1252. |
| 137 | + unicode_code = """ |
| 138 | +import sys |
| 139 | +
|
| 140 | +# Print encoding info for debugging |
| 141 | +print(f"stdout encoding: {sys.stdout.encoding}") |
| 142 | +print(f"stderr encoding: {sys.stderr.encoding}") |
| 143 | +
|
| 144 | +# Print various Unicode characters that are not in cp1252 |
| 145 | +# On systems with cp1252 default locale, without the UTF-8 fix, |
| 146 | +# these would cause UnicodeEncodeError |
| 147 | +print("Testing Unicode output:") |
| 148 | +print(" → Arrow character (U+2192)") |
| 149 | +print(" ✓ Check mark (U+2713)") |
| 150 | +print(" • Bullet point (U+2022)") |
| 151 | +print(" … Ellipsis (U+2026)") |
| 152 | +print(" Emoji: 🎉 🚀 ⚡") |
| 153 | +
|
| 154 | +# Simulate RemoteProxy connection message format |
| 155 | +print("Setting XML-RPC port... [input:None] →[execenv.xmlrpcport:None] OK") |
| 156 | +
|
| 157 | +print("All Unicode tests passed! ✓") |
| 158 | +""" |
| 159 | + macro.set_code(unicode_code) |
| 160 | + |
| 161 | + # Run the macro and wait for completion |
| 162 | + execenv.print("Running Unicode test macro...") |
| 163 | + win.macropanel.run_macro() |
| 164 | + |
| 165 | + # Wait for macro to complete (with timeout) |
| 166 | + # We need to process Qt events for the QProcess signals to be delivered |
| 167 | + max_wait = 10 # seconds |
| 168 | + elapsed = 0 |
| 169 | + while macro.is_running() and elapsed < max_wait: |
| 170 | + QW.QApplication.processEvents() |
| 171 | + time.sleep(0.1) |
| 172 | + elapsed += 0.1 |
| 173 | + |
| 174 | + # Verify the macro completed (not still running) |
| 175 | + # If there was an encoding error, the process would have crashed |
| 176 | + assert not macro.is_running(), ( |
| 177 | + "Macro did not complete within timeout - " |
| 178 | + "likely failed with encoding error" |
| 179 | + ) |
| 180 | + |
| 181 | + # Check the exit code - should be 0 for success |
| 182 | + # With the UTF-8 fix, the macro completes successfully (exit code 0) |
| 183 | + # Without the fix, it crashes with UnicodeEncodeError (exit code 1) |
| 184 | + exit_code = macro.get_exit_code() |
| 185 | + assert exit_code == 0, ( |
| 186 | + f"Macro exited with error code {exit_code} - " |
| 187 | + f"likely UnicodeEncodeError when trying to print Unicode characters" |
| 188 | + ) |
| 189 | + |
| 190 | + execenv.print("✓ Unicode test macro completed successfully!") |
| 191 | + execenv.print( |
| 192 | + "Note: This test verifies Unicode support works. On systems with " |
| 193 | + "UTF-8 as default encoding, it may pass even without the fix. " |
| 194 | + "The fix is critical for Windows systems with cp1252 locale." |
| 195 | + ) |
| 196 | + |
| 197 | + # Clean up |
| 198 | + win.macropanel.remove_all_objects() |
| 199 | + |
| 200 | + |
101 | 201 | if __name__ == "__main__": |
102 | 202 | test_macro_editor() |
| 203 | + test_macro_unicode_encoding() |
0 commit comments